diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index d4fdb04066..b9c80dacc8 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -147,4 +147,4 @@ required-features = ["direct-vectoring"] [[example]] name = "embassy_twai" -required-features = ["embassy", "async"] +required-features = ["embassy", "async", "embassy-executor-thread"] diff --git a/esp32c3-hal/examples/embassy_twai.rs b/esp32c3-hal/examples/embassy_twai.rs index 98b88351c9..0620217e92 100644 --- a/esp32c3-hal/examples/embassy_twai.rs +++ b/esp32c3-hal/examples/embassy_twai.rs @@ -1,7 +1,14 @@ -// ! embassy twai -// ! -// ! This is an example of running the embassy executor and asynchronously -// ! receiving and transmitting twai frames. +//! 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`. #![no_std] #![no_main] @@ -9,6 +16,7 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use embedded_can::{Frame, Id}; use esp32c3_hal::{ clock::ClockControl, embassy, @@ -19,6 +27,7 @@ use esp32c3_hal::{ IO, }; use esp_backtrace as _; +use esp_println::println; use static_cell::make_static; type TwaiOutbox = Channel; @@ -26,17 +35,18 @@ type TwaiOutbox = Channel; #[embassy_executor::task] async fn receiver(mut rx: TwaiRx<'static, TWAI0>, channel: &'static TwaiOutbox) -> ! { loop { - // let frame = nb::block!(can.receive()); let frame = rx.receive_async().await; match frame { Ok(frame) => { - esp_println::println!("Received frame: {:?}", frame); + println!("Received a frame:"); + print_frame(&frame); + // repeat the frame back channel.send(frame).await; } Err(e) => { - esp_println::println!("Receive error: {:?}", e); + println!("Receive error: {:?}", e); } } } @@ -50,10 +60,11 @@ async fn transmitter(mut tx: TwaiTx<'static, TWAI0>, channel: &'static TwaiOutbo match result { Ok(()) => { - esp_println::println!("Transmitted frame: {:?}", frame); + println!("Transmitted a frame:"); + print_frame(&frame); } Err(e) => { - esp_println::println!("Transmit error: {:?}", e); + println!("Transmit error: {:?}", e); } } } @@ -126,3 +137,23 @@ async fn main(spawner: Spawner) { 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()); + } +} diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 03dd8240d0..245186596a 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -1,6 +1,17 @@ +//! This example demonstrates the use of the twai peripheral to send and receive +//! frames. When `IS_SENDER` is set to `true`, the node will send a frame +//! every `DELAY_MS` interval. When `IS_SENDER` is set to `false`, the node will +//! wait to receive a frame and repeat it back. +//! +//! When running this example on two ESP boards, `IS_SENDER` must be set to +//! `true` for at least one node. It is okay to have multiple senders. + #![no_std] #![no_main] +const IS_SENDER: bool = true; +const DELAY_MS: u32 = 1000; + // Run this example with the eh1 feature enabled to use embedded-can instead of // embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's // upgrade to 1.0.0. cargo run --example twai --features eh1 --release @@ -9,8 +20,15 @@ use embedded_can::{Frame, Id}; // Run this example without the eh1 flag to use the embedded-hal 0.2.7 CAN traits. // cargo run --example twai --release #[cfg(not(feature = "eh1"))] -use embedded_hal::can::{Frame, Id}; -use esp32c3_hal::{clock::ClockControl, gpio::IO, peripherals::Peripherals, prelude::*, twai}; +use embedded_hal::can::{Frame, Id, StandardId}; +use esp32c3_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + twai::{self, EspTwaiError, EspTwaiFrame}, + Delay, +}; use esp_backtrace as _; use esp_println::println; use nb::block; @@ -23,6 +41,8 @@ fn main() -> ! { let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut delay = Delay::new(&clocks); + // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN // transceiver. let can_tx_pin = io.pins.gpio2; @@ -45,10 +65,10 @@ fn main() -> ! { // 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. + // these partial acceptance filters to exactly match. + // A filter that matches StandardId::ZERO. const FILTER: twai::filter::SingleStandardFilter = - twai::filter::SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + twai::filter::SingleStandardFilter::new(b"00000000000", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); can_config.set_filter(FILTER); // Start the peripheral. This locks the configuration settings of the peripheral @@ -56,31 +76,71 @@ fn main() -> ! { // received. let mut can = can_config.start(); + let mut counter: u32 = 0; + loop { - // Wait for a frame to be received. - let frame = block!(can.receive()).unwrap(); + if IS_SENDER { + // If this is the sender-node, then send a frame every DELAY_MS inteval. - println!("Received a frame:"); + // Send a frame to the other ESP + let frame = Frame::new(StandardId::ZERO, &counter.to_be_bytes()).unwrap(); - // Print different messages based on the frame id type. - match frame.id() { - Id::Standard(id) => { - println!("\tStandard Id: {:?}", id); + // Check for BusOff error, and restart the peripheral if it occurs. + let transmit_result = block!(can.transmit(&frame)); + if let Err(EspTwaiError::BusOff) = transmit_result { + println!("Transmit Error: BusOff. restarting..."); + let config = can.stop(); + can = config.start(); + continue; } - Id::Extended(id) => { - println!("\tExtended Id: {:?}", id); + + println!("Transmitted a frame."); + print_frame(&frame); + counter += 1; + + // Check for received frame, but do not block. + let receive_result = can.receive(); + if let Ok(f) = receive_result { + println!("Received a frame:"); + print_frame(&f); } - } - // 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()); + delay.delay_ms(DELAY_MS); } else { - println!("\tRemote Frame. Data Length Code: {}", frame.dlc()); + // If this is the receiver-node, then wait for a frame to be received, then send + // it back + + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame:"); + print_frame(&frame); + + // Transmit the frame back. + // We just received a frame, so chances of BusOff are low. + block!(can.transmit(&frame)).unwrap(); + + println!("Transmitted the frame back."); + } + } +} + +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); + } + } - // Transmit the frame back. - let _result = block!(can.transmit(&frame)).unwrap(); + // 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()); } } diff --git a/esp32s3-hal/examples/embassy_twai.rs b/esp32s3-hal/examples/embassy_twai.rs index 8cc9649f13..c283f623b0 100644 --- a/esp32s3-hal/examples/embassy_twai.rs +++ b/esp32s3-hal/examples/embassy_twai.rs @@ -1,7 +1,14 @@ -// ! embassy twai -// ! -// ! This is an example of running the embassy executor and asynchronously -// ! receiving and transmitting twai frames. +//! 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`. #![no_std] #![no_main] @@ -9,6 +16,7 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use embedded_can::{Frame, Id}; use esp32s3_hal::{ clock::ClockControl, embassy, @@ -19,6 +27,7 @@ use esp32s3_hal::{ IO, }; use esp_backtrace as _; +use esp_println::println; use static_cell::make_static; type TwaiOutbox = Channel; @@ -30,12 +39,14 @@ async fn receiver(mut rx: TwaiRx<'static, TWAI0>, channel: &'static TwaiOutbox) match frame { Ok(frame) => { - esp_println::println!("Received frame: {:?}", frame); + println!("Received a frame:"); + print_frame(&frame); + // repeat the frame back channel.send(frame).await; } Err(e) => { - esp_println::println!("Receive error: {:?}", e); + println!("Receive error: {:?}", e); } } } @@ -49,10 +60,11 @@ async fn transmitter(mut tx: TwaiTx<'static, TWAI0>, channel: &'static TwaiOutbo match result { Ok(()) => { - esp_println::println!("Transmitted frame: {:?}", frame); + println!("Transmitted a frame:"); + print_frame(&frame); } Err(e) => { - esp_println::println!("Transmit error: {:?}", e); + println!("Transmit error: {:?}", e); } } } @@ -125,3 +137,23 @@ async fn main(spawner: Spawner) { 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()); + } +} diff --git a/esp32s3-hal/examples/twai.rs b/esp32s3-hal/examples/twai.rs index 462ef7505f..9a295c25d3 100644 --- a/esp32s3-hal/examples/twai.rs +++ b/esp32s3-hal/examples/twai.rs @@ -1,4 +1,10 @@ -//! This example sends a CAN message to another ESP and receives it back. +//! This example demonstrates the use of the twai peripheral to send and receive +//! frames. When `IS_SENDER` is set to `true`, the node will send a frame +//! every `DELAY_MS` interval. When `IS_SENDER` is set to `false`, the node will +//! wait to receive a frame and repeat it back. +//! +//! When running this example on two ESP boards, `IS_SENDER` must be set to +//! `true` for at least one node. It is okay to have multiple senders. //! //! Wiring: //! This example works without CAN Transceivers by: @@ -8,24 +14,30 @@ //! //! ESP1/GND --- ESP2/GND //! ESP1/IO2 --- ESP1/IO3 --- ESP2/IO2 --- ESP2/IO3 --- 4.8kOhm --- ESP1/5V -//! -//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's #![no_std] #![no_main] -const IS_FIRST_SENDER: bool = true; +const IS_SENDER: bool = true; +const DELAY_MS: u32 = 1000; // Run this example with the eh1 feature enabled to use embedded-can instead of // embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's // upgrade to 1.0.0. cargo run --example twai --features eh1 --release #[cfg(feature = "eh1")] -use embedded_can::{Frame, StandardId}; +use embedded_can::{Frame, Id, StandardId}; // Run this example without the eh1 flag to use the embedded-hal 0.2.7 CAN traits. // cargo run --example twai --release #[cfg(not(feature = "eh1"))] -use embedded_hal::can::{Frame, StandardId}; -use esp32s3_hal::{clock::ClockControl, gpio::IO, peripherals::Peripherals, prelude::*, twai}; +use embedded_hal::can::{Frame, Id, StandardId}; +use esp32s3_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + twai::{self, EspTwaiError, EspTwaiFrame}, + Delay, +}; use esp_backtrace as _; use esp_println::println; use nb::block; @@ -38,6 +50,10 @@ fn main() -> ! { let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut delay = Delay::new(&clocks); + + // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN + // transceiver. // Set the tx pin as open drain. Skip this if using transceivers. let can_tx_pin = io.pins.gpio2.into_open_drain_output(); let can_rx_pin = io.pins.gpio3; @@ -70,23 +86,71 @@ fn main() -> ! { // received. let mut can = can_config.start(); - if IS_FIRST_SENDER { - // Send a frame to the other ESP - let frame = Frame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); - block!(can.transmit(&frame)).unwrap(); - println!("Sent a frame"); - } + let mut counter: u32 = 0; + + loop { + if IS_SENDER { + // If this is the sender-node, then send a frame every DELAY_MS inteval. + + // Send a frame to the other ESP + let frame = Frame::new(StandardId::ZERO, &counter.to_be_bytes()).unwrap(); + + // Check for BusOff error, and restart the peripheral if it occurs. + let transmit_result = block!(can.transmit(&frame)); + if let Err(EspTwaiError::BusOff) = transmit_result { + println!("Transmit Error: BusOff. restarting..."); + let config = can.stop(); + can = config.start(); + continue; + } + + println!("Transmitted a frame."); + print_frame(&frame); + counter += 1; - // Wait for a frame to be received. - let frame = block!(can.receive()).unwrap(); + // Check for received frame, but do not block. + let receive_result = can.receive(); + if let Ok(f) = receive_result { + println!("Received a frame:"); + print_frame(&f); + } - println!("Received a frame: {frame:?}"); + delay.delay_ms(DELAY_MS); + } else { + // If this is the receiver-node, then wait for a frame to be received, then send + // it back - if !IS_FIRST_SENDER { - // Transmit the frame back to the other ESP - block!(can.transmit(&frame)).unwrap(); - println!("Sent a frame"); + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame:"); + print_frame(&frame); + + // Transmit the frame back. + // We just received a frame, so chances of BusOff are low. + block!(can.transmit(&frame)).unwrap(); + + println!("Transmitted the frame back."); + } } +} - loop {} +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()); + } }