esp-hal/examples/src/bin/embassy_twai.rs
Dániel Buga 99bf346898
Remove the need to manually pass clocks around (#1999)
* Clean up passing clocks to drivers

* Update changelog

* Initialise Clocks in a critical section

* Fix calling now() before init

* Fix doc

* Fix esp-wifi migration guide

* Add safety comment

* Update tests
2024-09-04 14:13:51 +00:00

160 lines
4.8 KiB
Rust

//! This example demonstrates use of the twai peripheral running the embassy
//! executor and asynchronously receiving and transmitting twai frames.
//!
//! The `receiver` task waits to receive a frame and puts it into a channel
//! which will be picked up by the `transmitter` task.
//!
//! The `transmitter` task waits for a channel to receive a frame and transmits
//! it.
//!
//! This example should work with another ESP board running the `twai` example
//! with `IS_SENDER` set to `true`.
//!
//! The following wiring is assumed:
//! - TX => GPIO0
//! - RX => GPIO2
//% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3
//% FEATURES: embassy embassy-generic-timers
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel};
use embedded_can::{Frame, Id};
use esp_backtrace as _;
use esp_hal::{
gpio::Io,
interrupt,
peripherals::{self, TWAI0},
timer::timg::TimerGroup,
twai::{self, EspTwaiFrame, TwaiMode, TwaiRx, TwaiTx},
};
use esp_println::println;
use static_cell::StaticCell;
type TwaiOutbox = Channel<NoopRawMutex, EspTwaiFrame, 16>;
#[embassy_executor::task]
async fn receiver(
mut rx: TwaiRx<'static, TWAI0, esp_hal::Async>,
channel: &'static TwaiOutbox,
) -> ! {
loop {
let frame = rx.receive_async().await;
match frame {
Ok(frame) => {
println!("Received a frame:");
print_frame(&frame);
// repeat the frame back
channel.send(frame).await;
}
Err(e) => {
println!("Receive error: {:?}", e);
}
}
}
}
#[embassy_executor::task]
async fn transmitter(
mut tx: TwaiTx<'static, TWAI0, esp_hal::Async>,
channel: &'static TwaiOutbox,
) -> ! {
loop {
let frame = channel.receive().await;
let result = tx.transmit_async(&frame).await;
match result {
Ok(()) => {
println!("Transmitted a frame:");
print_frame(&frame);
}
Err(e) => {
println!("Transmit error: {:?}", e);
}
}
}
}
#[esp_hal_embassy::main]
async fn main(spawner: Spawner) {
let peripherals = esp_hal::init(esp_hal::Config::default());
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0);
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let can_tx_pin = io.pins.gpio0;
let can_rx_pin = io.pins.gpio2;
// The speed of the CAN bus.
const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K;
// !!! Use `new_async` when using a transceiver. `new_async_no_transceiver` sets TX to open-drain
// Begin configuring the TWAI peripheral. The peripheral is in a reset like
// state that prevents transmission but allows configuration.
let mut can_config = twai::TwaiConfiguration::new_async_no_transceiver(
peripherals.TWAI0,
can_tx_pin,
can_rx_pin,
CAN_BAUDRATE,
TwaiMode::Normal,
);
// Partially filter the incoming messages to reduce overhead of receiving
// undesired messages. Note that due to how the hardware filters messages,
// standard ids and extended ids may both match a filter. Frame ids should
// be explicitly checked in the application instead of fully relying on
// these partial acceptance filters to exactly match. A filter that matches
// standard ids of an even value.
const FILTER: twai::filter::SingleStandardFilter =
twai::filter::SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]);
can_config.set_filter(FILTER);
// Start the peripheral. This locks the configuration settings of the peripheral
// and puts it into operation mode, allowing packets to be sent and
// received.
let can = can_config.start();
// Get separate transmit and receive halves of the peripheral.
let (tx, rx) = can.split();
interrupt::enable(
peripherals::Interrupt::TWAI0,
interrupt::Priority::Priority1,
)
.unwrap();
static CHANNEL: StaticCell<TwaiOutbox> = StaticCell::new();
let channel = &*CHANNEL.init(Channel::new());
spawner.spawn(receiver(rx, channel)).ok();
spawner.spawn(transmitter(tx, channel)).ok();
}
fn print_frame(frame: &EspTwaiFrame) {
// Print different messages based on the frame id type.
match frame.id() {
Id::Standard(id) => {
println!("\tStandard Id: {:?}", id);
}
Id::Extended(id) => {
println!("\tExtended Id: {:?}", id);
}
}
// Print out the frame data or the requested data length code for a remote
// transmission request frame.
if frame.is_data_frame() {
println!("\tData: {:?}", frame.data());
} else {
println!("\tRemote Frame. Data Length Code: {}", frame.dlc());
}
}