diff --git a/CHANGELOG.md b/CHANGELOG.md index cc760733423..a2ddd733ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32-S3: Added support for 80Mhz PSRAM (#1069) - ESP32-C3/S3: Add workaround for USB pin exchange on usb-serial-jtag (#1104). - ESP32C6: Added LP_UART initialization (#1113) +- Added async support for TWAI (#951) + ### Changed +- Added inherent `transmit` and `receive` methods for `Twai`. `embedded-hal` `Can` trait is no longer needed in scope. (#951) - Set up interrupts for the DMA and async enabled peripherals only when `async` feature is provided (#1042) - Update to `1.0.0` releases of the `embedded-hal-*` packages (#1068) @@ -101,6 +104,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated to latest release candidate (`1.0.0-rc.2`) for `embedded-hal{-async,-nb}` (#994) - Explicit panic when hitting the `DefaultHandler` (#1005) - Relevant interrupts are now auto enabled in `embassy::init` (#1014). +- Set up interrupts for the DMA and async enabled peripherals only when `async` feature is provided (#1042) ### Fixed diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index f673b2d4340..3a1a223d2d2 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -75,6 +75,8 @@ //! } //! ``` +use core::marker::PhantomData; + #[cfg(feature = "eh1")] use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; #[cfg(not(feature = "eh1"))] @@ -262,7 +264,7 @@ impl BaudRate { /// An inactive TWAI peripheral in the "Reset"/configuration state. pub struct TwaiConfiguration<'d, T> { - peripheral: PeripheralRef<'d, T>, + peripheral: PhantomData<&'d PeripheralRef<'d, T>>, } impl<'d, T> TwaiConfiguration<'d, T> @@ -270,22 +272,23 @@ where T: Instance, { pub fn new( - peripheral: impl Peripheral

+ 'd, + _peripheral: impl Peripheral

+ 'd, tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, clocks: &Clocks, baud_rate: BaudRate, ) -> Self { // Enable the peripheral clock for the TWAI peripheral. - PeripheralClockControl::enable(T::SYSTEM_PERIPHERAL); + T::enable_peripheral(); // Set up the GPIO pins. crate::into_ref!(tx_pin, rx_pin); tx_pin.connect_peripheral_to_output(T::OUTPUT_SIGNAL); rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL); - crate::into_ref!(peripheral); - let mut cfg = TwaiConfiguration { peripheral }; + let mut cfg = TwaiConfiguration { + peripheral: PhantomData, + }; cfg.set_baud_rate(baud_rate, clocks); @@ -316,28 +319,22 @@ where let prescale = prescale as u8; // Set up the prescaler and sync jump width. - self.peripheral - .register_block() - .bus_timing_0() - .modify(|_, w| { - w.baud_presc() - .variant(prescale) - .sync_jump_width() - .variant(sjw) - }); + T::register_block().bus_timing_0().modify(|_, w| { + w.baud_presc() + .variant(prescale) + .sync_jump_width() + .variant(sjw) + }); // Set up the time segment 1, time segment 2, and triple sample. - self.peripheral - .register_block() - .bus_timing_1() - .modify(|_, w| { - w.time_seg1() - .variant(tseg_1) - .time_seg2() - .variant(tseg_2) - .time_samp() - .bit(triple_sample) - }); + T::register_block().bus_timing_1().modify(|_, w| { + w.time_seg1() + .variant(tseg_1) + .time_seg2() + .variant(tseg_2) + .time_samp() + .bit(triple_sample) + }); } /// Set up the acceptance filter on the device. @@ -351,8 +348,7 @@ where pub fn set_filter(&mut self, filter: impl Filter) { // Set or clear the rx filter mode bit depending on the filter type. let filter_mode_bit = filter.filter_type() == FilterType::Single; - self.peripheral - .register_block() + T::register_block() .mode() .modify(|_, w| w.rx_filter_mode().bit(filter_mode_bit)); @@ -362,10 +358,7 @@ where // Copy the filter to the peripheral. unsafe { - copy_to_data_register( - self.peripheral.register_block().data_0().as_ptr(), - ®isters, - ); + copy_to_data_register(T::register_block().data_0().as_ptr(), ®isters); } } @@ -376,8 +369,7 @@ where /// warning interrupt will be triggered (given the enable signal is /// valid). pub fn set_error_warning_limit(&mut self, limit: u8) { - self.peripheral - .register_block() + T::register_block() .err_warning_limit() .write(|w| w.err_warning_limit().variant(limit)); } @@ -386,13 +378,17 @@ where /// reception of packets using the new object. pub fn start(self) -> Twai<'d, T> { // Put the peripheral into operation mode by clearing the reset mode bit. - self.peripheral - .register_block() + T::register_block() .mode() .modify(|_, w| w.reset_mode().clear_bit()); Twai { - peripheral: self.peripheral, + tx: TwaiTx { + _peripheral: PhantomData, + }, + rx: TwaiRx { + _peripheral: PhantomData, + }, } } } @@ -402,50 +398,39 @@ where /// In this mode, the TWAI controller can transmit and receive messages /// including error signals (such as error and overload frames). pub struct Twai<'d, T> { - peripheral: PeripheralRef<'d, T>, + tx: TwaiTx<'d, T>, + rx: TwaiRx<'d, T>, } impl<'d, T> Twai<'d, T> where - T: Instance, + T: OperationInstance, { /// Stop the peripheral, putting it into reset mode and enabling /// reconfiguration. pub fn stop(self) -> TwaiConfiguration<'d, T> { // Put the peripheral into reset/configuration mode by setting the reset mode // bit. - self.peripheral - .register_block() + T::register_block() .mode() .modify(|_, w| w.reset_mode().set_bit()); TwaiConfiguration { - peripheral: self.peripheral, + peripheral: PhantomData, } } pub fn receive_error_count(&self) -> u8 { - self.peripheral - .register_block() - .rx_err_cnt() - .read() - .rx_err_cnt() - .bits() + T::register_block().rx_err_cnt().read().rx_err_cnt().bits() } pub fn transmit_error_count(&self) -> u8 { - self.peripheral - .register_block() - .tx_err_cnt() - .read() - .tx_err_cnt() - .bits() + T::register_block().tx_err_cnt().read().tx_err_cnt().bits() } /// Check if the controller is in a bus off state. pub fn is_bus_off(&self) -> bool { - self.peripheral - .register_block() + T::register_block() .status() .read() .bus_off_st() @@ -458,8 +443,7 @@ where /// Note that this may not be the number of valid messages in the receive /// FIFO due to fifo overflow/overrun. pub fn num_available_messages(&self) -> u8 { - self.peripheral - .register_block() + T::register_block() .rx_message_cnt() .read() .rx_message_counter() @@ -475,18 +459,102 @@ where /// error states. pub fn clear_receive_fifo(&self) { while self.num_available_messages() > 0 { - self.release_receive_fifo(); + T::release_receive_fifo(); } } - /// Release the message in the buffer. This will decrement the received - /// message counter and prepare the next message in the FIFO for - /// reading. - fn release_receive_fifo(&self) { - self.peripheral - .register_block() - .cmd() - .write(|w| w.release_buf().set_bit()); + pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> { + self.tx.transmit(frame) + } + + pub fn receive(&mut self) -> nb::Result { + self.rx.receive() + } + + /// Consumes this `Twai` instance and splits it into transmitting and + /// receiving halves. + pub fn split(self) -> (TwaiTx<'d, T>, TwaiRx<'d, T>) { + (self.tx, self.rx) + } +} + +/// Interface to the CAN transmitter part. +pub struct TwaiTx<'d, T> { + _peripheral: PhantomData<&'d T>, +} + +impl<'d, T> TwaiTx<'d, T> +where + T: OperationInstance, +{ + /// Transmit a frame. + /// + /// Because of how the TWAI registers are set up, we have to do some + /// assembly of bytes. Note that these registers serve a filter + /// configuration role when the device is in configuration mode so + /// patching the svd files to improve this may be non-trivial. + /// + /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.4.2) + /// + /// NOTE: TODO: This may not work if using the self reception/self test + /// functionality. See notes 1 and 2 in the "Frame Identifier" section + /// of the reference manual. + pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> { + let register_block = T::register_block(); + let status = register_block.status().read(); + + // Check that the peripheral is not in a bus off state. + if status.bus_off_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); + } + // Check that the peripheral is not already transmitting a packet. + if !status.tx_buf_st().bit_is_set() { + return nb::Result::Err(nb::Error::WouldBlock); + } + + T::write_frame(frame); + + Ok(()) + } +} + +/// Interface to the CAN receiver part. +pub struct TwaiRx<'d, T> { + _peripheral: PhantomData<&'d T>, +} + +impl<'d, T> TwaiRx<'d, T> +where + T: OperationInstance, +{ + // Receive a frame + pub fn receive(&mut self) -> nb::Result { + let register_block = T::register_block(); + let status = register_block.status().read(); + + // Check that the peripheral is not in a bus off state. + if status.bus_off_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); + } + + // Check that we actually have packets to receive. + if !status.rx_buf_st().bit_is_set() { + return nb::Result::Err(nb::Error::WouldBlock); + } + + // Check if the packet in the receive buffer is valid or overrun. + if status.miss_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL( + ErrorKind::Overrun, + ))); + } + + // Safety: + // - We have a `&mut self` and have unique access to the peripheral. + // - There is a message in the FIFO because we checked status + let frame = T::read_frame(); + + Ok(frame) } } @@ -537,36 +605,58 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl Can for Twai<'_, T> +impl<'d, T> Can for Twai<'d, T> where - T: Instance, + T: OperationInstance, { type Frame = EspTwaiFrame; type Error = EspTwaiError; + /// Transmit a frame. - /// - /// Because of how the TWAI registers are set up, we have to do some - /// assembly of bytes. Note that these registers serve a filter - /// configuration role when the device is in configuration mode so - /// patching the svd files to improve this may be non-trivial. - /// - /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.4.2) - /// - /// NOTE: TODO: This may not work if using the self reception/self test - /// functionality. See notes 1 and 2 in the "Frame Identifier" section - /// of the reference manual. fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { - let status = self.peripheral.register_block().status().read(); + self.tx.transmit(frame)?; - // Check that the peripheral is not in a bus off state. - if status.bus_off_st().bit_is_set() { - return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); - } - // Check that the peripheral is not already transmitting a packet. - if !status.tx_buf_st().bit_is_set() { - return nb::Result::Err(nb::Error::WouldBlock); - } + // Success in readying packet for transmit. No packets can be replaced in the + // transmit buffer so return None in accordance with the + // embedded-can/embedded-hal trait. + nb::Result::Ok(None) + } + + /// Return a received frame if there are any available. + fn receive(&mut self) -> nb::Result { + self.rx.receive() + } +} + +pub trait Instance { + const SYSTEM_PERIPHERAL: system::Peripheral; + const NUMBER: usize; + + const INPUT_SIGNAL: InputSignal; + const OUTPUT_SIGNAL: OutputSignal; + + fn register_block() -> &'static RegisterBlock; + + fn enable_peripheral(); +} + +pub trait OperationInstance: Instance { + #[cfg(feature = "async")] + fn async_state() -> &'static asynch::TwaiAsyncState { + &asynch::TWAI_STATE[Self::NUMBER] + } + /// Release the message in the buffer. This will decrement the received + /// message counter and prepare the next message in the FIFO for + /// reading. + fn release_receive_fifo() { + Self::register_block() + .cmd() + .write(|w| w.release_buf().set_bit()); + } + + /// Write a frame to the peripheral. + fn write_frame(frame: &EspTwaiFrame) { // Assemble the frame information into the data_0 byte. let frame_format: u8 = frame.is_extended() as u8; let rtr_bit: u8 = frame.is_remote_frame() as u8; @@ -574,8 +664,9 @@ where let data_0: u8 = frame_format << 7 | rtr_bit << 6 | dlc_bits; - self.peripheral - .register_block() + let register_block = Self::register_block(); + + register_block .data_0() .write(|w| w.tx_byte_0().variant(data_0)); @@ -584,33 +675,27 @@ where Id::Standard(id) => { let id = id.as_raw(); - self.peripheral - .register_block() + register_block .data_1() .write(|w| w.tx_byte_1().variant((id >> 3) as u8)); - self.peripheral - .register_block() + register_block .data_2() .write(|w| w.tx_byte_2().variant((id << 5) as u8)); } Id::Extended(id) => { let id = id.as_raw(); - self.peripheral - .register_block() + register_block .data_1() .write(|w| w.tx_byte_1().variant((id >> 21) as u8)); - self.peripheral - .register_block() + register_block .data_2() .write(|w| w.tx_byte_2().variant((id >> 13) as u8)); - self.peripheral - .register_block() + register_block .data_3() .write(|w| w.tx_byte_3().variant((id >> 5) as u8)); - self.peripheral - .register_block() + register_block .data_4() .write(|w| w.tx_byte_4().variant((id << 3) as u8)); } @@ -620,16 +705,10 @@ where if frame.is_data_frame() { match frame.id() { Id::Standard(_) => unsafe { - copy_to_data_register( - self.peripheral.register_block().data_3().as_ptr(), - frame.data(), - ) + copy_to_data_register(register_block.data_3().as_ptr(), frame.data()) }, Id::Extended(_) => unsafe { - copy_to_data_register( - self.peripheral.register_block().data_5().as_ptr(), - frame.data(), - ) + copy_to_data_register(register_block.data_5().as_ptr(), frame.data()) }, } } else { @@ -638,46 +717,15 @@ where // Set the transmit request command, this will lock the transmit buffer until // the transmission is complete or aborted. - self.peripheral - .register_block() - .cmd() - .write(|w| w.tx_req().set_bit()); - - // Success in readying packet for transmit. No packets can be replaced in the - // transmit buffer so return None in accordance with the - // embedded-can/embedded-hal trait. - nb::Result::Ok(None) + register_block.cmd().write(|w| w.tx_req().set_bit()); } - /// Return a received frame if there are any available. - fn receive(&mut self) -> nb::Result { - let status = self.peripheral.register_block().status().read(); - - // Check that the peripheral is not in a bus off state. - if status.bus_off_st().bit_is_set() { - return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); - } - - // Check that we actually have packets to receive. - if !status.rx_buf_st().bit_is_set() { - return nb::Result::Err(nb::Error::WouldBlock); - } - - // Check if the packet in the receive buffer is valid or overrun. - if status.miss_st().bit_is_set() { - return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL( - ErrorKind::Overrun, - ))); - } + /// Read a frame from the peripheral. + fn read_frame() -> EspTwaiFrame { + let register_block = Self::register_block(); // Read the frame information and extract the frame id format and dlc. - let data_0 = self - .peripheral - .register_block() - .data_0() - .read() - .tx_byte_0() - .bits(); + let data_0 = register_block.data_0().read().tx_byte_0().bits(); let is_standard_format = data_0 & 0b1 << 7 == 0; let is_data_frame = data_0 & 0b1 << 6 == 0; @@ -686,21 +734,9 @@ where // Read the payload from the packet and construct a frame. let frame = if is_standard_format { // Frame uses standard 11 bit id. - let data_1 = self - .peripheral - .register_block() - .data_1() - .read() - .tx_byte_1() - .bits(); - - let data_2 = self - .peripheral - .register_block() - .data_2() - .read() - .tx_byte_2() - .bits(); + let data_1 = register_block.data_1().read().tx_byte_1().bits(); + + let data_2 = register_block.data_2().read().tx_byte_2().bits(); let raw_id: u16 = ((data_1 as u16) << 3) | ((data_2 as u16) >> 5); @@ -710,48 +746,20 @@ where // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG // registers. unsafe { - EspTwaiFrame::new_from_data_registers( - id, - self.peripheral.register_block().data_3().as_ptr(), - dlc, - ) + EspTwaiFrame::new_from_data_registers(id, register_block.data_3().as_ptr(), dlc) } } else { EspTwaiFrame::new_remote(id, dlc).unwrap() } } else { // Frame uses extended 29 bit id. - let data_1 = self - .peripheral - .register_block() - .data_1() - .read() - .tx_byte_1() - .bits(); - - let data_2 = self - .peripheral - .register_block() - .data_2() - .read() - .tx_byte_2() - .bits(); - - let data_3 = self - .peripheral - .register_block() - .data_3() - .read() - .tx_byte_3() - .bits(); - - let data_4 = self - .peripheral - .register_block() - .data_4() - .read() - .tx_byte_4() - .bits(); + let data_1 = register_block.data_1().read().tx_byte_1().bits(); + + let data_2 = register_block.data_2().read().tx_byte_2().bits(); + + let data_3 = register_block.data_3().read().tx_byte_3().bits(); + + let data_4 = register_block.data_4().read().tx_byte_4().bits(); let raw_id: u32 = (data_1 as u32) << 21 | (data_2 as u32) << 13 @@ -762,11 +770,7 @@ where if is_data_frame { unsafe { - EspTwaiFrame::new_from_data_registers( - id, - self.peripheral.register_block().data_5().as_ptr(), - dlc, - ) + EspTwaiFrame::new_from_data_registers(id, register_block.data_5().as_ptr(), dlc) } } else { EspTwaiFrame::new_remote(id, dlc).unwrap() @@ -775,56 +779,315 @@ where // Release the packet we read from the FIFO, allowing the peripheral to prepare // the next packet. - self.release_receive_fifo(); + register_block.cmd().write(|w| w.release_buf().set_bit()); - nb::Result::Ok(frame) + frame } } -pub trait Instance { - const SYSTEM_PERIPHERAL: system::Peripheral; - - const INPUT_SIGNAL: InputSignal; - const OUTPUT_SIGNAL: OutputSignal; - - fn register_block(&self) -> &RegisterBlock; -} - #[cfg(any(esp32c3, esp32s3))] impl Instance for crate::peripherals::TWAI0 { const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0; + const NUMBER: usize = 0; const INPUT_SIGNAL: InputSignal = InputSignal::TWAI_RX; const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI_TX; #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self + fn register_block() -> &'static RegisterBlock { + unsafe { &*crate::peripherals::TWAI0::PTR } + } + + fn enable_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::Twai0); } } +#[cfg(any(esp32c3, esp32s3))] +impl OperationInstance for crate::peripherals::TWAI0 {} + #[cfg(esp32c6)] impl Instance for crate::peripherals::TWAI0 { const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0; + const NUMBER: usize = 0; const INPUT_SIGNAL: InputSignal = InputSignal::TWAI0_RX; const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI0_TX; #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self + fn register_block() -> &'static RegisterBlock { + unsafe { &*crate::peripherals::TWAI0::PTR } + } + + fn enable_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::Twai0); } } +#[cfg(esp32c6)] +impl OperationInstance for crate::peripherals::TWAI0 {} + #[cfg(esp32c6)] impl Instance for crate::peripherals::TWAI1 { const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai1; + const NUMBER: usize = 1; const INPUT_SIGNAL: InputSignal = InputSignal::TWAI1_RX; const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI1_TX; #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self + fn register_block() -> &'static RegisterBlock { + unsafe { &*crate::peripherals::TWAI1::PTR } + } + + fn enable_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::Twai1); + } +} + +#[cfg(esp32c6)] +impl OperationInstance for crate::peripherals::TWAI1 {} + +#[cfg(feature = "async")] +mod asynch { + use core::{future::poll_fn, task::Poll}; + + use embassy_sync::{ + blocking_mutex::raw::CriticalSectionRawMutex, + channel::Channel, + waitqueue::AtomicWaker, + }; + use procmacros::interrupt; + + use super::*; + use crate::peripherals::TWAI0; + #[cfg(esp32c6)] + use crate::peripherals::TWAI1; + + pub struct TwaiAsyncState { + pub tx_waker: AtomicWaker, + pub err_waker: AtomicWaker, + pub rx_queue: Channel, 32>, + } + + impl TwaiAsyncState { + pub const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + err_waker: AtomicWaker::new(), + rx_queue: Channel::new(), + } + } + } + + const NUM_TWAI: usize = 2; + const NEW_STATE: TwaiAsyncState = TwaiAsyncState::new(); + pub(crate) static TWAI_STATE: [TwaiAsyncState; NUM_TWAI] = [NEW_STATE; NUM_TWAI]; + + impl Twai<'_, T> + where + T: OperationInstance, + { + pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { + self.tx.transmit_async(frame).await + } + + pub async fn receive_async(&mut self) -> Result { + self.rx.receive_async().await + } + } + + impl<'d, T> TwaiTx<'d, T> + where + T: OperationInstance, + { + pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { + poll_fn(|cx| { + T::async_state().tx_waker.register(cx.waker()); + + let register_block = T::register_block(); + let status = register_block.status().read(); + + // Check that the peripheral is not in a bus off state. + if status.bus_off_st().bit_is_set() { + return Poll::Ready(Err(EspTwaiError::BusOff)); + } + // Check that the peripheral is not already transmitting a packet. + if !status.tx_buf_st().bit_is_set() { + return Poll::Pending; + } + + T::write_frame(frame); + + return Poll::Ready(Ok(())); + }) + .await + } + } + + impl<'d, T> TwaiRx<'d, T> + where + T: OperationInstance, + { + pub async fn receive_async(&mut self) -> Result { + poll_fn(|cx| { + T::async_state().err_waker.register(cx.waker()); + + if let Poll::Ready(result) = T::async_state().rx_queue.poll_receive(cx) { + return Poll::Ready(result); + } else { + let register_block = T::register_block(); + let status = register_block.status().read(); + + // Check that the peripheral is not in a bus off state. + if status.bus_off_st().bit_is_set() { + return Poll::Ready(Err(EspTwaiError::BusOff)); + } + + // Check if the packet in the receive buffer is valid or overrun. + if status.miss_st().bit_is_set() { + return Poll::Ready(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun))); + } + } + + Poll::Pending + }) + .await + } + } + + #[cfg(any(esp32c3, esp32s3))] + #[interrupt] + fn TWAI0() { + let register_block = TWAI0::register_block(); + + let intr_enable = register_block.int_ena().read(); + let intr_status = register_block.int_raw().read(); + + let async_state = TWAI0::async_state(); + + if intr_status.tx_int_st().bit_is_set() { + async_state.tx_waker.wake(); + } + + if intr_status.rx_int_st().bit_is_set() { + let status = register_block.status().read(); + + let rx_queue = &async_state.rx_queue; + + if status.bus_off_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::BusOff)); + } + + if status.miss_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun))); + } + + let frame = TWAI0::read_frame(); + + let _ = rx_queue.try_send(Ok(frame)); + + register_block.cmd().write(|w| w.release_buf().set_bit()); + } + + if intr_status.bits() & 0b11111100 > 0 { + async_state.err_waker.wake(); + } + + unsafe { + register_block + .int_ena() + .modify(|_, w| w.bits(intr_enable.bits() & (!intr_status.bits() | 1))); + } + } + + #[cfg(esp32c6)] + #[interrupt] + fn TWAI0() { + let register_block = TWAI0::register_block(); + + let intr_enable = register_block.interrupt_enable().read(); + let intr_status = register_block.interrupt().read(); + + let async_state = TWAI0::async_state(); + + if intr_status.transmit_int_st().bit_is_set() { + async_state.tx_waker.wake(); + } + + if intr_status.receive_int_st().bit_is_set() { + let status = register_block.status().read(); + + let rx_queue = &async_state.rx_queue; + + if status.bus_off_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::BusOff)); + } + + if status.miss_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun))); + } + + let frame = TWAI0::read_frame(); + + let _ = rx_queue.try_send(Ok(frame)); + + register_block.cmd().write(|w| w.release_buf().set_bit()); + } + + if intr_status.bits() & 0b11111100 > 0 { + async_state.err_waker.wake(); + } + + unsafe { + register_block + .interrupt_enable() + .modify(|_, w| w.bits(intr_enable.bits() & (!intr_status.bits() | 1))); + } + } + + #[cfg(esp32c6)] + #[interrupt] + fn TWAI1() { + let register_block = TWAI1::register_block(); + + let intr_enable = register_block.interrupt_enable().read(); + let intr_status = register_block.interrupt().read(); + + let async_state = TWAI1::async_state(); + + if intr_status.transmit_int_st().bit_is_set() { + async_state.tx_waker.wake(); + } + + if intr_status.receive_int_st().bit_is_set() { + let status = register_block.status().read(); + + let rx_queue = &async_state.rx_queue; + + if status.bus_off_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::BusOff)); + } + + if status.miss_st().bit_is_set() { + let _ = rx_queue.try_send(Err(EspTwaiError::EmbeddedHAL(ErrorKind::Overrun))); + } + + let frame = TWAI1::read_frame(); + + let _ = rx_queue.try_send(Ok(frame)); + + register_block.cmd().write(|w| w.release_buf().set_bit()); + } + + if intr_status.bits() & 0b11111100 > 0 { + async_state.err_waker.wake(); + } + + unsafe { + register_block + .interrupt_enable() + .modify(|_, w| w.bits(intr_enable.bits() & (!intr_status.bits() | 1))); + } } } diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 90673b8eb02..e06ad8e46fe 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -167,3 +167,7 @@ required-features = ["embassy", "async", "embassy-executor-thread"] [[example]] name = "direct-vectoring" required-features = ["direct-vectoring"] + +[[example]] +name = "embassy_twai" +required-features = ["embassy", "async", "embassy-executor-thread"] diff --git a/esp32c3-hal/examples/embassy_twai.rs b/esp32c3-hal/examples/embassy_twai.rs new file mode 100644 index 00000000000..0620217e926 --- /dev/null +++ b/esp32c3-hal/examples/embassy_twai.rs @@ -0,0 +1,159 @@ +//! 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] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use embedded_can::{Frame, Id}; +use esp32c3_hal::{ + clock::ClockControl, + embassy, + interrupt, + peripherals::{self, Peripherals, TWAI0}, + prelude::*, + twai::{self, EspTwaiFrame, TwaiRx, TwaiTx}, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use static_cell::make_static; + +type TwaiOutbox = Channel; + +#[embassy_executor::task] +async fn receiver(mut rx: TwaiRx<'static, TWAI0>, 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>, 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); + } + } + } +} + +#[main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN + // transceiver. + let can_tx_pin = io.pins.gpio2; + let can_rx_pin = io.pins.gpio3; + + // The speed of the CAN bus. + const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; + + // 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( + peripherals.TWAI0, + can_tx_pin, + can_rx_pin, + &clocks, + CAN_BAUDRATE, + ); + + // 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(); + + let channel = &*make_static!(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()); + } +} diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 6c15e5b464b..42f6b3312fe 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -1,16 +1,34 @@ +//! 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 #[cfg(feature = "eh1")] -use embedded_can::{nb::Can, Frame, Id}; +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::{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/Cargo.toml b/esp32s3-hal/Cargo.toml index d41bfa4022b..068b7d94bce 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -188,3 +188,7 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_usb_serial_jtag" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_twai" +required-features = ["embassy", "embassy-executor-thread", "async"] diff --git a/esp32s3-hal/examples/embassy_twai.rs b/esp32s3-hal/examples/embassy_twai.rs new file mode 100644 index 00000000000..c283f623b0e --- /dev/null +++ b/esp32s3-hal/examples/embassy_twai.rs @@ -0,0 +1,159 @@ +//! 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] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use embedded_can::{Frame, Id}; +use esp32s3_hal::{ + clock::ClockControl, + embassy, + interrupt, + peripherals::{self, Peripherals, TWAI0}, + prelude::*, + twai::{self, EspTwaiFrame, TwaiRx, TwaiTx}, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use static_cell::make_static; + +type TwaiOutbox = Channel; + +#[embassy_executor::task] +async fn receiver(mut rx: TwaiRx<'static, TWAI0>, 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>, 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); + } + } + } +} + +#[main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = esp32s3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN + // transceiver. + let can_tx_pin = io.pins.gpio2; + let can_rx_pin = io.pins.gpio3; + + // The speed of the CAN bus. + const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; + + // 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( + peripherals.TWAI0, + can_tx_pin, + can_rx_pin, + &clocks, + CAN_BAUDRATE, + ); + + // 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(); + + let channel = &*make_static!(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()); + } +} diff --git a/esp32s3-hal/examples/twai.rs b/esp32s3-hal/examples/twai.rs index 962f3c17678..9a295c25d36 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::{nb::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::{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()); + } }