From e015806c0e4f849f0f743d88b749a42ed6e01ac8 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Tue, 6 Sep 2022 22:52:40 -0500 Subject: [PATCH 01/20] wip: initial implementation of transmission only. --- esp-hal-common/src/system.rs | 5 + esp-hal-common/src/twai.rs | 486 +++++++++++++++++++++++++++++++++++ esp32c3-hal/examples/twai.rs | 111 ++++++++ esp32c3-hal/src/lib.rs | 1 + 4 files changed, 603 insertions(+) create mode 100644 esp-hal-common/src/twai.rs create mode 100644 esp32c3-hal/examples/twai.rs diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 920fa7d0938..8be418372b7 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -42,6 +42,7 @@ pub enum Peripheral { I2s1, #[cfg(usb_otg)] Usb, + TWAI, } /// Controls the enablement of peripheral clocks. @@ -149,6 +150,10 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.usb_rst().clear_bit()); } + Peripheral::TWAI => { + perip_clk_en0.modify(|_, w| w.can_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.can_rst().clear_bit()); + } } } } diff --git a/esp-hal-common/src/twai.rs b/esp-hal-common/src/twai.rs new file mode 100644 index 00000000000..05df79e8109 --- /dev/null +++ b/esp-hal-common/src/twai.rs @@ -0,0 +1,486 @@ +use core::default; + +use crate::{ + clock::Clocks, + pac::twai::RegisterBlock, + system::PeripheralClockControl, + types::{InputSignal, OutputSignal}, + InputPin, OutputPin, +}; + +// use alloc::{format, string::String}; +use embedded_hal::can::{self, Frame}; + +/// Very basic implementation of the Frame trait. +/// +/// TODO: See if data and dlc can be simplified into a slice w/ lifetimes etc. +/// TODO: See if this can be improved. +/// +pub struct ESPTWAIFrame { + id: can::Id, + dlc: usize, + data: [u8; 8], + is_remote: bool, +} + +impl embedded_hal::can::Frame for ESPTWAIFrame { + fn new(id: impl Into, data: &[u8]) -> Option { + // CAN2.0 frames cannot contain more than 8 bytes of data. + if data.len() > 8 { + return None; + } + + // TODO: See struct docs for TODO list, ideally this copy would be eliminated somehow. + let mut d: [u8; 8] = Default::default(); + let (left, _unused) = d.split_at_mut(data.len()); + left.clone_from_slice(data); + + Some(ESPTWAIFrame { + id: id.into(), + data: d, + dlc: data.len(), + is_remote: false, + }) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + Some(ESPTWAIFrame { + id: id.into(), + data: Default::default(), + dlc: dlc, + is_remote: true, + }) + } + + fn is_extended(&self) -> bool { + match self.id { + can::Id::Standard(_) => false, + can::Id::Extended(_) => true, + } + } + + #[inline(always)] + fn is_remote_frame(&self) -> bool { + self.is_remote + } + + #[inline(always)] + fn id(&self) -> can::Id { + self.id + } + + #[inline(always)] + fn dlc(&self) -> usize { + self.dlc + } + + #[inline(always)] + fn data(&self) -> &[u8] { + match self.is_remote_frame() { + true => &[], + false => &self.data[0..self.dlc], + } + } +} + +/// The underlying timings for the TWAI peripheral. +pub struct TimingConfig { + pub baud_rate_prescaler: u16, + pub sync_jump_width: u8, + pub tseg_1: u8, + pub tseg_2: u8, + pub triple_sample: bool, +} + +/// A selection of pre-determined baudrates for the TWAI driver. +pub enum BaudRate { + B125K, + B250K, + B500K, + B1000K, + Custom(TimingConfig), +} +impl BaudRate { + /// Convert the BaudRate into the timings that the peripheral needs. + // These timings are copied from the ESP IDF C driver. + // #define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + fn timing(self) -> TimingConfig { + match self { + Self::B125K => TimingConfig { + baud_rate_prescaler: 32, + sync_jump_width: 3, + tseg_1: 15, + tseg_2: 4, + triple_sample: false, + }, + Self::B250K => TimingConfig { + baud_rate_prescaler: 16, + sync_jump_width: 3, + tseg_1: 15, + tseg_2: 4, + triple_sample: false, + }, + Self::B500K => TimingConfig { + baud_rate_prescaler: 8, + sync_jump_width: 3, + tseg_1: 15, + tseg_2: 4, + triple_sample: false, + }, + Self::B1000K => TimingConfig { + baud_rate_prescaler: 4, + sync_jump_width: 3, + tseg_1: 15, + tseg_2: 4, + triple_sample: false, + }, + Self::Custom(timing_config) => timing_config, + } + } +} + +/// An inactive TWAI peripheral in the "Reset"/configuration state. +pub struct TWAIConfiguration { + peripheral: T, +} + +impl TWAIConfiguration +where + T: Instance, +{ + pub fn new( + peripheral: T, + mut tx_pin: TX, + mut rx_pin: RX, + clock_control: &mut PeripheralClockControl, + baud_rate: BaudRate, + ) -> Self { + // TODO: Probably should do a low level reset. + + // Enable the peripheral clock for the TWAI peripheral. + clock_control.enable(crate::system::Peripheral::TWAI); + + // Set up the GPIO pins. + tx_pin.connect_peripheral_to_output(OutputSignal::TWAI_TX); + rx_pin.connect_input_to_peripheral(InputSignal::TWAI_RX); + + let mut cfg = TWAIConfiguration { + peripheral: peripheral, + }; + + cfg.set_frequency(baud_rate); + + cfg + } + + /// Set the bitrate of the bus. + fn set_frequency(&mut self, baud_rate: BaudRate) { + // TWAI is clocked from the APB_CLK according to Table 6-4 [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) + + // Unpack the baud rate timings and convert them to the values needed for the register. + // Many of the registers have a minimum value of 1 which is represented by having zero + // bits set, therefore many values need to have 1 subtracted from them before being + // stored into the register. + let timing = baud_rate.timing(); + + let prescale = (timing.baud_rate_prescaler / 2) - 1; + let sjw = timing.sync_jump_width - 1; + let tseg_1 = timing.tseg_1 - 1; + let tseg_2 = timing.tseg_2 - 1; + let triple_sample = timing.triple_sample; + + // 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) + }); + + // 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) + }); + } + + /// TODO: Set up the acceptance filter on the device to accept the specified filters. + pub fn set_filter(&mut self) { + panic!("Unimplemented."); + } + + /// Set the Error warning threshold. + /// + /// In the case when any of an error counter value exceeds + /// the threshold, or all the error counter values are below the threshold, an error 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() + .err_warning_limit + .write(|w| w.err_warning_limit().variant(limit)); + } + + /// Put the peripheral into Operation Mode, allowing the transmission and reception of + /// packets using the new object. + pub fn start(self) -> TWAI { + // Put the peripheral into operation mode by clearing the reset mode bit. + self.peripheral + .register_block() + .mode + .modify(|_, w| w.reset_mode().clear_bit()); + + TWAI { + peripheral: self.peripheral, + } + } +} + +/// An active TWAI peripheral in Normal Mode. +/// +/// According to the [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) 29.4.1.2 Operation Mode, in "Normal Mode": +/// The TWAI controller can transmit and receive messages including error signals (such as error and overload Frames) +/// +/// +pub struct TWAI { + peripheral: T, +} + +impl TWAI +where + T: Instance, +{ + pub fn stop(self) -> TWAIConfiguration { + // Put the peripheral into reset/configuration mode by setting the reset mode bit. + self.peripheral + .register_block() + .mode + .modify(|_, w| w.reset_mode().set_bit()); + + TWAIConfiguration { + peripheral: self.peripheral, + } + } + + pub fn receive_error_count(&self) -> u8 { + self.peripheral + .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() + } + + pub fn status(&self) -> u32 { + self.peripheral.register_block().status.read().bits() + } + + /// Test if the transmit buffer is available for writing. + pub fn transmit_buffer_is_empty(&self) -> bool { + self.peripheral + .register_block() + .status + .read() + .tx_buf_st() + .bit() + } +} + +#[derive(Debug)] +pub struct ESPTWAIError { + kind: embedded_hal::can::ErrorKind, +} +impl embedded_hal::can::Error for ESPTWAIError { + fn kind(&self) -> can::ErrorKind { + self.kind + } +} + +impl embedded_hal::can::Can for TWAI +where + T: Instance, +{ + 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: 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> { + // TODO: Check that the peripheral is not already transmitting a packet, + // if so return WouldBlock, or figure out a way to cancel a transmission and return + // the attempted packet. + if !self.transmit_buffer_is_empty() { + return nb::Result::Err(nb::Error::WouldBlock); + } + + // Assemble the frame information into the data_0 byte. + let frame_format: u8 = if frame.is_extended() { 0x1 } else { 0x0 }; + let rtr_bit: u8 = if frame.is_remote_frame() { 0x1 } else { 0x0 }; + let dlc_bits: u8 = frame.dlc() as u8 & 0b1111; + + let data_0: u8 = frame_format << 7 | rtr_bit << 6 | dlc_bits; + + self.peripheral + .register_block() + .data_0 + .write(|w| w.tx_byte_0().variant(data_0)); + + // Assemble the identifier information of the packet. + match frame.id() { + can::Id::Standard(id) => { + let id = id.as_raw(); + + // Upper 8 bits go into byte_1. + self.peripheral + .register_block() + .data_1 + .write(|w| w.tx_byte_1().variant((id >> 3) as u8)); + + // Lower 3 bits go into the upper 3 bits of byte_2. + self.peripheral + .register_block() + .data_2 + .write(|w| w.tx_byte_2().variant((id << 5) as u8)); + } + can::Id::Extended(id) => { + let _id = id.as_raw(); + panic!("Unimplemented"); + } + } + + // Assemble the data portion of the packet. + if frame.is_data_frame() { + match frame.id() { + can::Id::Standard(_) => { + // TODO: Copy data to the appropriate registers in a better method. Verified in --release asm that this is bad. + + // Byte 0 of the payload. + if frame.dlc() > 0 { + let data_byte_1 = &frame.data()[0]; + self.peripheral + .register_block() + .data_3 + .write(|w| w.tx_byte_3().variant(*data_byte_1)); + } + // Byte 1 of the payload. + if frame.dlc() > 1 { + let data_byte_2 = &frame.data()[1]; + self.peripheral + .register_block() + .data_4 + .write(|w| w.tx_byte_4().variant(*data_byte_2)); + } + // Byte 2 of the payload. + if frame.dlc() > 2 { + let data_byte_3 = &frame.data()[2]; + self.peripheral + .register_block() + .data_5 + .write(|w| w.tx_byte_5().variant(*data_byte_3)); + } + // Byte 3 of the payload. + if frame.dlc() > 3 { + let data_byte_4 = &frame.data()[3]; + self.peripheral + .register_block() + .data_6 + .write(|w| w.tx_byte_6().variant(*data_byte_4)); + } + // Byte 4 of the payload. + if frame.dlc() > 4 { + let data_byte_5 = &frame.data()[4]; + self.peripheral + .register_block() + .data_7 + .write(|w| w.tx_byte_7().variant(*data_byte_5)); + } + // Byte 5 of the payload. + if frame.dlc() > 5 { + let data_byte_6 = &frame.data()[5]; + self.peripheral + .register_block() + .data_8 + .write(|w| w.tx_byte_8().variant(*data_byte_6)); + } + // Byte 6 of the payload. + if frame.dlc() > 6 { + let data_byte_7 = &frame.data()[6]; + self.peripheral + .register_block() + .data_9 + .write(|w| w.tx_byte_9().variant(*data_byte_7)); + } + // Byte 7 of the payload. + if frame.dlc() > 7 { + let data_byte_8 = &frame.data()[7]; + self.peripheral + .register_block() + .data_10 + .write(|w| w.tx_byte_10().variant(*data_byte_8)); + } + } + can::Id::Extended(_) => { + panic!("Unimplemented"); + } + } + } else { + // Is RTR frame, so no data is included. + } + + // Set the transmit request command, this will lock the transmit buffer until the + // transmission is complete, aborted + self.peripheral + .register_block() + .cmd + .write(|w| w.tx_req().set_bit()); + + nb::Result::Ok(None) + } + fn receive(&mut self) -> nb::Result { + panic!("Not implemented"); + } +} + +pub trait Instance { + fn register_block(&self) -> &RegisterBlock; +} + +impl Instance for crate::pac::TWAI { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } +} diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs new file mode 100644 index 00000000000..8bbbb85f488 --- /dev/null +++ b/esp32c3-hal/examples/twai.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; +use esp32c3_hal::{ + clock::{ClockControl, CpuClock}, + gpio::IO, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + twai, Rtc, UsbSerialJtag, +}; + +use embedded_hal::can::{Can, Frame, Id::Standard, StandardId}; +use esp_backtrace as _; +use nb::{block, Error}; +use riscv_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + // let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + timer0.start(500u64.millis()); + + writeln!( + UsbSerialJtag, + "Clocks: apb: {} cpu: {} xtal: {}", + clocks.apb_clock, clocks.cpu_clock, clocks.xtal_clock + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let can_config = twai::TWAIConfiguration::new( + peripherals.TWAI, + io.pins.gpio2, + io.pins.gpio3, + &mut system.peripheral_clock_control, + twai::BaudRate::B1000K, + ); + + let mut can = can_config.start(); + + let mut packets_sent = 0u64; + + loop { + let d = packets_sent as u8; + let data = [d, 1, 2, 3, 4, 5, 6, 7]; + + let id = (packets_sent as u16) & 0x7FF; + + let mut frame = + twai::ESPTWAIFrame::new(Standard(StandardId::new(id).unwrap()), &data).unwrap(); + + let result = block!(can.transmit(&mut frame)); + + // match result { + // Err(err) => match err { + // Error::WouldBlock => { + // writeln!(UsbSerialJtag, "TWAI would block!").unwrap(); + // } + // Error::Other(err) => { + // writeln!(UsbSerialJtag, "TWAI hit a different error: {:?}", err).unwrap(); + // } + // }, + // _ => {} + // } + + if let Err(err) = result { + writeln!(UsbSerialJtag, "TWAI hit a different error: {:?}", err).unwrap(); + } else { + packets_sent += 1; + writeln!(UsbSerialJtag, "Sent. {}", packets_sent).unwrap(); + } + + // let status = can.status(); + // writeln!( + // UsbSerialJtag, + // "CAN Status: {:b}\n\t RX_BUF_ST {}\n\t OVERRUN_ST {}\n\t TX_BUF_ST {}\n\t TX_COMPLETE {}\n\t RX_ST {}\n\t TX_ST {}\n\t ERR_ST {}\n\t BUS_OFF_ST {}\n\t MISS_ST {}", + // status, + // (status >> 0) & 0b1 != 0, + // (status >> 1) & 0b1 != 0, + // (status >> 2) & 0b1 != 0, + // (status >> 3) & 0b1 != 0, + // (status >> 4) & 0b1 != 0, + // (status >> 5) & 0b1 != 0, + // (status >> 6) & 0b1 != 0, + // (status >> 7) & 0b1 != 0, + // (status >> 8) & 0b1 != 0, + // ) + // .ok(); + + // block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 5601e49c036..90a94c7df93 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -28,6 +28,7 @@ pub use esp_hal_common::{ system, systimer, timer, + twai, uart, utils, Cpu, From b8ec8b4803fd94a3dec7c6e6ad5ff5910de596d3 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 7 Sep 2022 22:32:07 -0500 Subject: [PATCH 02/20] Moved TWAI to its own directory and added initial reception of packets. --- esp-hal-common/src/twai/filter.rs | 115 +++++++++++++++ esp-hal-common/src/{twai.rs => twai/mod.rs} | 155 ++++++++++++++++++-- esp32c3-hal/examples/twai.rs | 73 ++++----- 3 files changed, 301 insertions(+), 42 deletions(-) create mode 100644 esp-hal-common/src/twai/filter.rs rename esp-hal-common/src/{twai.rs => twai/mod.rs} (75%) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs new file mode 100644 index 00000000000..b0dfeb25f74 --- /dev/null +++ b/esp-hal-common/src/twai/filter.rs @@ -0,0 +1,115 @@ +use embedded_hal::can::{ExtendedId, StandardId}; + +pub struct ValueMask { + pub value: T, + pub mask: T, +} +pub trait FilterToRegisters { + fn to_registers(self) -> [u8; 8]; +} + +pub struct SingleStandardFilter { + pub id: ValueMask, + pub rtr: ValueMask, + pub data: ValueMask<[u8; 2]>, +} +impl FilterToRegisters for SingleStandardFilter { + fn to_registers(self) -> [u8; 8] { + [ + // Value. + (self.id.value.as_raw() >> 3) as u8, + ((self.id.value.as_raw() << 5) as u8 + | if self.rtr.value { 0b1 << 4 } else { 0b0 << 4 }) + & 0b11110000, + self.data.value[0], + self.data.value[1], + // Mask. + (self.id.mask.as_raw() >> 3) as u8, + ((self.id.mask.as_raw() << 5) as u8 | if self.rtr.mask { 0b1 << 4 } else { 0b0 << 4 }) + & 0b11110000, + self.data.mask[0], + self.data.mask[1], + ] + } +} + +pub struct SingleExtendedFilter { + pub id: ValueMask, + pub rtr: ValueMask, +} +impl FilterToRegisters for SingleExtendedFilter { + fn to_registers(self) -> [u8; 8] { + [ + // Value. + (self.id.value.as_raw() >> 21) as u8, + (self.id.value.as_raw() >> 13) as u8, + (self.id.value.as_raw() >> 5) as u8, + ((self.id.value.as_raw() << 3) as u8 + | if self.rtr.value { 0b1 << 2 } else { 0b0 << 2 }) + & 0b11111100, + // Mask. + (self.id.mask.as_raw() >> 21) as u8, + (self.id.mask.as_raw() >> 13) as u8, + (self.id.mask.as_raw() >> 5) as u8, + ((self.id.mask.as_raw() << 3) as u8 | if self.rtr.mask { 0b1 << 2 } else { 0b0 << 2 }) + & 0b11111100, + ] + } +} + +// TODO: how do we actually want to store the two filters? + +pub struct DualStandardFilter { + pub id: ValueMask, + pub rtr: ValueMask, + // TODO: only the first filter can match on the data. + pub data: ValueMask<[u8; 1]>, +} +impl FilterToRegisters for DualStandardFilter { + fn to_registers(self) -> [u8; 8] { + // TODO: this. + panic!("Unimplemented"); + } +} +/// +/// NOTE: The dual extended id acceptance filter can only match "the first 16 bits of the 29-bit ID". +pub struct DualExtendedFilter { + pub id: ValueMask, +} +impl FilterToRegisters for DualExtendedFilter { + fn to_registers(self) -> [u8; 8] { + // TODO: this. + panic!("Unimplemented"); + } +} + +pub enum FilterIdFormat { + Standard(Std), + Extended(Ext), +} +impl FilterToRegisters for FilterIdFormat +where + Std: FilterToRegisters, + Ext: FilterToRegisters, +{ + fn to_registers(self) -> [u8; 8] { + match self { + FilterIdFormat::Standard(filter) => filter.to_registers(), + FilterIdFormat::Extended(filter) => filter.to_registers(), + } + } +} + +pub enum Filter { + Single(FilterIdFormat), + Dual(FilterIdFormat), +} + +impl FilterToRegisters for Filter { + fn to_registers(self) -> [u8; 8] { + match self { + Self::Single(single) => single.to_registers(), + Self::Dual(dual) => dual.to_registers(), + } + } +} diff --git a/esp-hal-common/src/twai.rs b/esp-hal-common/src/twai/mod.rs similarity index 75% rename from esp-hal-common/src/twai.rs rename to esp-hal-common/src/twai/mod.rs index 05df79e8109..977f437a7d7 100644 --- a/esp-hal-common/src/twai.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -1,21 +1,26 @@ -use core::default; +use core::slice::from_raw_parts; use crate::{ - clock::Clocks, + // clock::Clocks, pac::twai::RegisterBlock, system::PeripheralClockControl, types::{InputSignal, OutputSignal}, - InputPin, OutputPin, + InputPin, + OutputPin, }; -// use alloc::{format, string::String}; -use embedded_hal::can::{self, Frame}; +use embedded_hal::can::{self, ErrorKind, Frame, StandardId}; + +use self::filter::{Filter, FilterToRegisters}; + +pub mod filter; /// Very basic implementation of the Frame trait. /// /// TODO: See if data and dlc can be simplified into a slice w/ lifetimes etc. /// TODO: See if this can be improved. /// +#[derive(Debug)] pub struct ESPTWAIFrame { id: can::Id, dlc: usize, @@ -221,9 +226,55 @@ where }); } - /// TODO: Set up the acceptance filter on the device to accept the specified filters. - pub fn set_filter(&mut self) { - panic!("Unimplemented."); + /// Set up the acceptance filter on the device to accept the specified filters. + /// + /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6) + pub fn set_filter(&mut self, filter: Filter) { + // Set or clear the rx filter mode bit depending on the filter type. + let filter_mode_bit = match filter { + Filter::Single(_) => true, + Filter::Dual(_) => false, + }; + self.peripheral + .register_block() + .mode + .modify(|_, w| w.rx_filter_mode().bit(filter_mode_bit)); + + // Convert the filter into values for the registers and store them to the registers. + let registers = filter.to_registers(); + // TODO: Use something better for copying, probably something similar to memcpy. + self.peripheral + .register_block() + .data_0 + .write(|w| w.tx_byte_0().variant(registers[0])); + self.peripheral + .register_block() + .data_1 + .write(|w| w.tx_byte_1().variant(registers[1])); + self.peripheral + .register_block() + .data_2 + .write(|w| w.tx_byte_2().variant(registers[2])); + self.peripheral + .register_block() + .data_3 + .write(|w| w.tx_byte_3().variant(registers[3])); + self.peripheral + .register_block() + .data_4 + .write(|w| w.tx_byte_4().variant(registers[4])); + self.peripheral + .register_block() + .data_5 + .write(|w| w.tx_byte_5().variant(registers[5])); + self.peripheral + .register_block() + .data_6 + .write(|w| w.tx_byte_6().variant(registers[6])); + self.peripheral + .register_block() + .data_7 + .write(|w| w.tx_byte_7().variant(registers[7])); } /// Set the Error warning threshold. @@ -309,6 +360,34 @@ where .tx_buf_st() .bit() } + + /// Get the number of messages that the peripheral has received. + /// + /// Note that this may not be the number of messages in the receive FIFO due to + /// fifo overflow/overrun. + pub fn num_messages(&self) -> u8 { + self.peripheral + .register_block() + .rx_message_cnt + .read() + .rx_message_counter() + .bits() + } + /// Clear the receive FIFO, discarding any valid, partial, or invalid packets. + /// + /// This is typically used to clear an overrun receive FIFO. + pub fn clear_receive_fifo(&self) { + while self.num_messages() > 0 {} + } + + /// Release the message in the buffer. This will decrement the received message + /// counter and prepare the next message in the FIFO for reading. + pub fn release_receive_fifo(&self) { + self.peripheral + .register_block() + .cmd + .write(|w| w.release_buf().set_bit()); + } } #[derive(Debug)] @@ -470,7 +549,65 @@ where nb::Result::Ok(None) } fn receive(&mut self) -> nb::Result { - panic!("Not implemented"); + // Check that we actually have packets to receive. + if self.num_messages() == 0 { + return nb::Result::Err(nb::Error::WouldBlock); + } + + // Check if the packet in the receive buffer is valid or overrun. + let is_overrun = self + .peripheral + .register_block() + .status + .read() + .miss_st() + .bit_is_set(); + + if is_overrun { + return nb::Result::Err(nb::Error::Other(ESPTWAIError { + kind: ErrorKind::Overrun, + })); + } + + // TODO: Read the actual data. + // TODO: patch the svd files :/. + let data_0 = + unsafe { (self.peripheral.register_block().data_0.as_ptr() as *const u8).read() }; + + let is_standard_format = data_0 & 0b1 << 7 == 0; + let dlc = (data_0 & 0b1111) as usize; + + let maybe_frame = if is_standard_format { + // Frame uses standard 11 bit id. + let data_1 = + unsafe { (self.peripheral.register_block().data_1.as_ptr() as *const u8).read() }; + let data_2 = + unsafe { (self.peripheral.register_block().data_2.as_ptr() as *const u8).read() }; + + let id = StandardId::new((data_1 as u16) << 3 | (data_2 as u16) >> 5).unwrap(); + + // Copy the packet payload from the peripheral into memory. + // TODO: find a better way of doing this, basically a memcpy, but the + // destination and source have different strides. + let raw_payload = + unsafe { from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) }; + + let mut payload: [u8; 8] = [0; 8]; + for i in 0..dlc { + payload[i] = raw_payload[i] as u8; + } + + ESPTWAIFrame::new(id, &payload[..dlc]) + } else { + // Frame uses extended 29 bit id. + panic!("Unimplemented"); + }; + + // Release the packet we read from the FIFO, allowing the peripheral to prepare + // the next packet. + self.release_receive_fifo(); + + nb::Result::Ok(maybe_frame.unwrap()) } } diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 8bbbb85f488..abc0dbef204 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -8,12 +8,13 @@ use esp32c3_hal::{ pac::Peripherals, prelude::*, timer::TimerGroup, - twai, Rtc, UsbSerialJtag, + twai::{self, ESPTWAIFrame}, + Rtc, UsbSerialJtag, }; -use embedded_hal::can::{Can, Frame, Id::Standard, StandardId}; +use embedded_hal::can::{Can, Frame, StandardId}; use esp_backtrace as _; -use nb::{block, Error}; +use nb::block; use riscv_rt::entry; #[entry] @@ -47,7 +48,7 @@ fn main() -> ! { let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - let can_config = twai::TWAIConfiguration::new( + let mut can_config = twai::TWAIConfiguration::new( peripherals.TWAI, io.pins.gpio2, io.pins.gpio3, @@ -55,40 +56,46 @@ fn main() -> ! { twai::BaudRate::B1000K, ); - let mut can = can_config.start(); + // Set bits in the bitmask means that we don't care about that bit. + let filter = twai::filter::Filter::Single(twai::filter::FilterIdFormat::Standard( + twai::filter::SingleStandardFilter { + id: twai::filter::ValueMask { + value: StandardId::new(0x000).unwrap(), + mask: StandardId::new(0x000).unwrap(), + }, + rtr: twai::filter::ValueMask { + value: false, + mask: true, + }, + data: twai::filter::ValueMask { + value: [0x00, 0x00], + mask: [0xff, 0xff], + }, + }, + )); + + can_config.set_filter(filter); - let mut packets_sent = 0u64; + let mut can = can_config.start(); loop { - let d = packets_sent as u8; - let data = [d, 1, 2, 3, 4, 5, 6, 7]; - - let id = (packets_sent as u16) & 0x7FF; - - let mut frame = - twai::ESPTWAIFrame::new(Standard(StandardId::new(id).unwrap()), &data).unwrap(); - - let result = block!(can.transmit(&mut frame)); - - // match result { - // Err(err) => match err { - // Error::WouldBlock => { - // writeln!(UsbSerialJtag, "TWAI would block!").unwrap(); - // } - // Error::Other(err) => { - // writeln!(UsbSerialJtag, "TWAI hit a different error: {:?}", err).unwrap(); - // } - // }, - // _ => {} - // } - - if let Err(err) = result { - writeln!(UsbSerialJtag, "TWAI hit a different error: {:?}", err).unwrap(); - } else { - packets_sent += 1; - writeln!(UsbSerialJtag, "Sent. {}", packets_sent).unwrap(); + // writeln!(UsbSerialJtag, "Waiting for packet...").unwrap(); + let frame = block!(can.receive()).unwrap(); + writeln!(UsbSerialJtag, "Received: {:?}", frame).unwrap(); + + // Increment the payload bytes by one. + let mut data: [u8; 8] = [0; 8]; + data[..frame.dlc()].copy_from_slice(frame.data()); + + for b in data[..frame.dlc()].iter_mut() { + *b += 1; } + let frame = ESPTWAIFrame::new(frame.id(), &data[..frame.dlc()]).unwrap(); + // Transmit the frame back. + writeln!(UsbSerialJtag, "Transmitting: {:?}", frame).unwrap(); + let _result = block!(can.transmit(&frame)).unwrap(); + // let status = can.status(); // writeln!( // UsbSerialJtag, From c8a2291849eaa750bb9c5fec90be5ed32a12cc01 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Thu, 8 Sep 2022 23:34:36 -0500 Subject: [PATCH 03/20] Added extended id transmit and receive. --- esp-hal-common/src/twai/mod.rs | 223 +++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 38 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 977f437a7d7..d91fe447642 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -9,7 +9,7 @@ use crate::{ OutputPin, }; -use embedded_hal::can::{self, ErrorKind, Frame, StandardId}; +use embedded_hal::can::{self, ErrorKind, ExtendedId, Frame, StandardId}; use self::filter::{Filter, FilterToRegisters}; @@ -377,7 +377,9 @@ where /// /// This is typically used to clear an overrun receive FIFO. pub fn clear_receive_fifo(&self) { - while self.num_messages() > 0 {} + while self.num_messages() > 0 { + self.release_receive_fifo(); + } } /// Release the message in the buffer. This will decrement the received message @@ -391,12 +393,16 @@ where } #[derive(Debug)] -pub struct ESPTWAIError { - kind: embedded_hal::can::ErrorKind, +pub enum ESPTWAIError { + BusOff, + EmbeddedHAL(embedded_hal::can::ErrorKind), } impl embedded_hal::can::Error for ESPTWAIError { fn kind(&self) -> can::ErrorKind { - self.kind + match self { + Self::BusOff => can::ErrorKind::Other, + Self::EmbeddedHAL(kind) => *kind, + } } } @@ -418,12 +424,16 @@ where /// notes 1 and 2 in the Frame Identifier section of the reference manual. /// fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { - // TODO: Check that the peripheral is not already transmitting a packet, - // if so return WouldBlock, or figure out a way to cancel a transmission and return - // the attempted packet. - if !self.transmit_buffer_is_empty() { + let status = self.peripheral.register_block().status.read(); + + // 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); } + // 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)); + } // Assemble the frame information into the data_0 byte. let frame_format: u8 = if frame.is_extended() { 0x1 } else { 0x0 }; @@ -442,30 +452,43 @@ where can::Id::Standard(id) => { let id = id.as_raw(); - // Upper 8 bits go into byte_1. self.peripheral .register_block() .data_1 .write(|w| w.tx_byte_1().variant((id >> 3) as u8)); - // Lower 3 bits go into the upper 3 bits of byte_2. self.peripheral .register_block() .data_2 .write(|w| w.tx_byte_2().variant((id << 5) as u8)); } can::Id::Extended(id) => { - let _id = id.as_raw(); - panic!("Unimplemented"); + let id = id.as_raw(); + + self.peripheral + .register_block() + .data_1 + .write(|w| w.tx_byte_1().variant((id >> 21) as u8)); + self.peripheral + .register_block() + .data_2 + .write(|w| w.tx_byte_2().variant((id >> 13) as u8)); + self.peripheral + .register_block() + .data_3 + .write(|w| w.tx_byte_3().variant((id >> 5) as u8)); + self.peripheral + .register_block() + .data_4 + .write(|w| w.tx_byte_4().variant((id << 3) as u8)); } } - // Assemble the data portion of the packet. + // Store the data portion of the packet into the transmit buffer. if frame.is_data_frame() { match frame.id() { can::Id::Standard(_) => { // TODO: Copy data to the appropriate registers in a better method. Verified in --release asm that this is bad. - // Byte 0 of the payload. if frame.dlc() > 0 { let data_byte_1 = &frame.data()[0]; @@ -532,7 +555,71 @@ where } } can::Id::Extended(_) => { - panic!("Unimplemented"); + // TODO: Copy data to the appropriate registers in a better method. Verified in --release asm that this is bad. + // Byte 0 of the payload. + if frame.dlc() > 0 { + let data_byte_1 = &frame.data()[0]; + self.peripheral + .register_block() + .data_5 + .write(|w| w.tx_byte_5().variant(*data_byte_1)); + } + // Byte 1 of the payload. + if frame.dlc() > 1 { + let data_byte_2 = &frame.data()[1]; + self.peripheral + .register_block() + .data_6 + .write(|w| w.tx_byte_6().variant(*data_byte_2)); + } + // Byte 2 of the payload. + if frame.dlc() > 2 { + let data_byte_3 = &frame.data()[2]; + self.peripheral + .register_block() + .data_7 + .write(|w| w.tx_byte_7().variant(*data_byte_3)); + } + // Byte 3 of the payload. + if frame.dlc() > 3 { + let data_byte_4 = &frame.data()[3]; + self.peripheral + .register_block() + .data_8 + .write(|w| w.tx_byte_8().variant(*data_byte_4)); + } + // Byte 4 of the payload. + if frame.dlc() > 4 { + let data_byte_5 = &frame.data()[4]; + self.peripheral + .register_block() + .data_9 + .write(|w| w.tx_byte_9().variant(*data_byte_5)); + } + // Byte 5 of the payload. + if frame.dlc() > 5 { + let data_byte_6 = &frame.data()[5]; + self.peripheral + .register_block() + .data_10 + .write(|w| w.tx_byte_10().variant(*data_byte_6)); + } + // Byte 6 of the payload. + if frame.dlc() > 6 { + let data_byte_7 = &frame.data()[6]; + self.peripheral + .register_block() + .data_11 + .write(|w| w.tx_byte_11().variant(*data_byte_7)); + } + // Byte 7 of the payload. + if frame.dlc() > 7 { + let data_byte_8 = &frame.data()[7]; + self.peripheral + .register_block() + .data_12 + .write(|w| w.tx_byte_12().variant(*data_byte_8)); + } } } } else { @@ -540,7 +627,7 @@ where } // Set the transmit request command, this will lock the transmit buffer until the - // transmission is complete, aborted + // transmission is complete or aborted. self.peripheral .register_block() .cmd @@ -549,40 +636,50 @@ where nb::Result::Ok(None) } fn receive(&mut self) -> nb::Result { + let status = self.peripheral.register_block().status.read(); + // Check that we actually have packets to receive. - if self.num_messages() == 0 { + 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. - let is_overrun = self + if status.miss_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(ESPTWAIError::EmbeddedHAL( + ErrorKind::Overrun, + ))); + } + + // Read the frame information and extract the frame id format and dlc. + let data_0 = self .peripheral .register_block() - .status + .data_0 .read() - .miss_st() - .bit_is_set(); - - if is_overrun { - return nb::Result::Err(nb::Error::Other(ESPTWAIError { - kind: ErrorKind::Overrun, - })); - } - - // TODO: Read the actual data. - // TODO: patch the svd files :/. - let data_0 = - unsafe { (self.peripheral.register_block().data_0.as_ptr() as *const u8).read() }; + .tx_byte_0() + .bits(); let is_standard_format = data_0 & 0b1 << 7 == 0; let dlc = (data_0 & 0b1111) as usize; + // Read the payload from the packet and construct a frame. let maybe_frame = if is_standard_format { // Frame uses standard 11 bit id. - let data_1 = - unsafe { (self.peripheral.register_block().data_1.as_ptr() as *const u8).read() }; - let data_2 = - unsafe { (self.peripheral.register_block().data_2.as_ptr() as *const u8).read() }; + let data_1 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .bits(); + + let data_2 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .bits(); let id = StandardId::new((data_1 as u16) << 3 | (data_2 as u16) >> 5).unwrap(); @@ -600,7 +697,57 @@ where ESPTWAIFrame::new(id, &payload[..dlc]) } else { // Frame uses extended 29 bit id. - panic!("Unimplemented"); + let data_1 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .bits(); + + let data_2 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .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 id = ExtendedId::new( + (data_1 as u32) << 21 + | (data_2 as u32) << 13 + | (data_3 as u32) << 5 + | (data_4 as u32) >> 3, + ) + .unwrap(); + + // Copy the packet payload from the peripheral into memory. + // TODO: find a better way of doing this, basically a memcpy, but the + // destination and source have different strides. + let raw_payload = + unsafe { from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) }; + + let mut payload: [u8; 8] = [0; 8]; + for i in 0..dlc { + payload[i] = raw_payload[i] as u8; + } + ESPTWAIFrame::new(id, &payload[..dlc]) }; // Release the packet we read from the FIFO, allowing the peripheral to prepare From 0e5e04f9afc00b64199bc6dfbed585638749f7a7 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 10 Sep 2022 22:30:24 -0500 Subject: [PATCH 04/20] Added maybe better code for making packet filters. --- esp-hal-common/src/twai/bitselector.rs | 94 ++++++++++++ esp-hal-common/src/twai/filter.rs | 200 ++++++++++++++----------- esp-hal-common/src/twai/mod.rs | 43 +++--- esp32c3-hal/examples/twai.rs | 71 ++++++--- 4 files changed, 280 insertions(+), 128 deletions(-) create mode 100644 esp-hal-common/src/twai/bitselector.rs diff --git a/esp-hal-common/src/twai/bitselector.rs b/esp-hal-common/src/twai/bitselector.rs new file mode 100644 index 00000000000..f688686eb40 --- /dev/null +++ b/esp-hal-common/src/twai/bitselector.rs @@ -0,0 +1,94 @@ +#[derive(Debug, Clone, Copy)] +pub enum Selector { + Set, + Reset, + Any, +} + +impl Selector { + pub fn into_value(&self) -> u8 { + match self { + Selector::Set => 0b1, + Selector::Reset => 0b0, + Selector::Any => 0b0, + } + } + pub fn into_mask(&self) -> u8 { + // Set bits in the mask mean we don't care about the value of that bit. The bit could be any value. + // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 + match self { + Selector::Set => 0b0, + Selector::Reset => 0b0, + Selector::Any => 0b1, + } + } +} + +impl Into for bool { + fn into(self) -> Selector { + match self { + true => Selector::Set, + false => Selector::Reset, + } + } +} + +pub trait BitSelectorNewExact { + fn new_exact(value: T) -> BitSelector; +} + +#[derive(Debug, Clone, Copy)] +pub struct BitSelector { + pub bits: [Selector; N], +} + +impl BitSelector { + pub fn new_any() -> Self { + Self { + bits: [Selector::Any; N], + } + } +} +// TODO: improve this. +impl BitSelectorNewExact for BitSelector<11> { + fn new_exact(value: u16) -> BitSelector<11> { + Self { + bits: [ + (value & 0b00000000001 != 0).into(), + (value & 0b00000000010 != 0).into(), + (value & 0b00000000100 != 0).into(), + (value & 0b00000001000 != 0).into(), + (value & 0b00000010000 != 0).into(), + (value & 0b00000100000 != 0).into(), + (value & 0b00001000000 != 0).into(), + (value & 0b00010000000 != 0).into(), + (value & 0b00100000000 != 0).into(), + (value & 0b01000000000 != 0).into(), + (value & 0b10000000000 != 0).into(), + ], + } + } +} +impl BitSelectorNewExact for BitSelector<8> { + fn new_exact(value: u8) -> BitSelector<8> { + Self { + bits: [ + (value & 0b00000000001 != 0).into(), + (value & 0b00000000010 != 0).into(), + (value & 0b00000000100 != 0).into(), + (value & 0b00000001000 != 0).into(), + (value & 0b00000010000 != 0).into(), + (value & 0b00000100000 != 0).into(), + (value & 0b00001000000 != 0).into(), + (value & 0b00010000000 != 0).into(), + ], + } + } +} +impl BitSelectorNewExact for BitSelector<1> { + fn new_exact(value: u8) -> BitSelector<1> { + Self { + bits: [(value & 0b1 != 0).into()], + } + } +} diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index b0dfeb25f74..ee82394570d 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,115 +1,145 @@ -use embedded_hal::can::{ExtendedId, StandardId}; +use super::bitselector::BitSelector; -pub struct ValueMask { - pub value: T, - pub mask: T, +#[derive(Debug, PartialEq)] +pub enum FilterType { + Single, + Dual, } -pub trait FilterToRegisters { - fn to_registers(self) -> [u8; 8]; + +pub trait Filter { + // The type of the filter. + const FILTER_TYPE: FilterType; + fn filter_type(&self) -> FilterType { + Self::FILTER_TYPE + } + + fn to_registers(&self) -> [u8; 8]; } +/// +/// +/// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter +/// will also be accepted. pub struct SingleStandardFilter { - pub id: ValueMask, - pub rtr: ValueMask, - pub data: ValueMask<[u8; 2]>, + pub id: BitSelector<11>, + pub rtr: BitSelector<1>, + pub data: [BitSelector<8>; 2], } -impl FilterToRegisters for SingleStandardFilter { - fn to_registers(self) -> [u8; 8] { + +impl Filter for SingleStandardFilter { + const FILTER_TYPE: FilterType = FilterType::Single; + fn to_registers(&self) -> [u8; 8] { [ // Value. - (self.id.value.as_raw() >> 3) as u8, - ((self.id.value.as_raw() << 5) as u8 - | if self.rtr.value { 0b1 << 4 } else { 0b0 << 4 }) - & 0b11110000, - self.data.value[0], - self.data.value[1], + // TODO: Implement some sort of into for slices of bits so that we can simplify some of this code. + self.id.bits[10].into_value() << 7 + | self.id.bits[9].into_value() << 6 + | self.id.bits[8].into_value() << 5 + | self.id.bits[7].into_value() << 4 + | self.id.bits[6].into_value() << 3 + | self.id.bits[5].into_value() << 2 + | self.id.bits[4].into_value() << 1 + | self.id.bits[3].into_value(), + self.id.bits[2].into_value() << 7 + | self.id.bits[1].into_value() << 6 + | self.id.bits[0].into_value() << 5 + | self.rtr.bits[0].into_value() << 4, + self.data[0].bits[7].into_value() << 7 + | self.data[0].bits[6].into_value() << 6 + | self.data[0].bits[5].into_value() << 5 + | self.data[0].bits[4].into_value() << 4 + | self.data[0].bits[3].into_value() << 3 + | self.data[0].bits[2].into_value() << 2 + | self.data[0].bits[1].into_value() << 1 + | self.data[0].bits[0].into_value() << 0, + self.data[1].bits[7].into_value() << 7 + | self.data[1].bits[6].into_value() << 6 + | self.data[1].bits[5].into_value() << 5 + | self.data[1].bits[4].into_value() << 4 + | self.data[1].bits[3].into_value() << 3 + | self.data[1].bits[2].into_value() << 2 + | self.data[1].bits[1].into_value() << 1 + | self.data[1].bits[0].into_value() << 0, // Mask. - (self.id.mask.as_raw() >> 3) as u8, - ((self.id.mask.as_raw() << 5) as u8 | if self.rtr.mask { 0b1 << 4 } else { 0b0 << 4 }) - & 0b11110000, - self.data.mask[0], - self.data.mask[1], + self.id.bits[10].into_mask() << 7 + | self.id.bits[9].into_mask() << 6 + | self.id.bits[8].into_mask() << 5 + | self.id.bits[7].into_mask() << 4 + | self.id.bits[6].into_mask() << 3 + | self.id.bits[5].into_mask() << 2 + | self.id.bits[4].into_mask() << 1 + | self.id.bits[3].into_mask(), + self.id.bits[2].into_mask() << 7 + | self.id.bits[1].into_mask() << 6 + | self.id.bits[0].into_mask() << 5 + | self.rtr.bits[0].into_mask() << 4, + self.data[0].bits[7].into_mask() << 7 + | self.data[0].bits[6].into_mask() << 6 + | self.data[0].bits[5].into_mask() << 5 + | self.data[0].bits[4].into_mask() << 4 + | self.data[0].bits[3].into_mask() << 3 + | self.data[0].bits[2].into_mask() << 2 + | self.data[0].bits[1].into_mask() << 1 + | self.data[0].bits[0].into_mask() << 0, + self.data[1].bits[7].into_mask() << 7 + | self.data[1].bits[6].into_mask() << 6 + | self.data[1].bits[5].into_mask() << 5 + | self.data[1].bits[4].into_mask() << 4 + | self.data[1].bits[3].into_mask() << 3 + | self.data[1].bits[2].into_mask() << 2 + | self.data[1].bits[1].into_mask() << 1 + | self.data[1].bits[0].into_mask() << 0, ] } } - +/// +/// +/// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter +/// will also be accepted. pub struct SingleExtendedFilter { - pub id: ValueMask, - pub rtr: ValueMask, + pub id: BitSelector<29>, + pub rtr: BitSelector<1>, } -impl FilterToRegisters for SingleExtendedFilter { - fn to_registers(self) -> [u8; 8] { - [ - // Value. - (self.id.value.as_raw() >> 21) as u8, - (self.id.value.as_raw() >> 13) as u8, - (self.id.value.as_raw() >> 5) as u8, - ((self.id.value.as_raw() << 3) as u8 - | if self.rtr.value { 0b1 << 2 } else { 0b0 << 2 }) - & 0b11111100, - // Mask. - (self.id.mask.as_raw() >> 21) as u8, - (self.id.mask.as_raw() >> 13) as u8, - (self.id.mask.as_raw() >> 5) as u8, - ((self.id.mask.as_raw() << 3) as u8 | if self.rtr.mask { 0b1 << 2 } else { 0b0 << 2 }) - & 0b11111100, - ] +impl Filter for SingleExtendedFilter { + const FILTER_TYPE: FilterType = FilterType::Single; + fn to_registers(&self) -> [u8; 8] { + panic!("Unimplemented"); } } -// TODO: how do we actually want to store the two filters? - +/// +/// TODO: is this how we actually want to store the two filters? +/// +/// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter +/// will also be accepted. pub struct DualStandardFilter { - pub id: ValueMask, - pub rtr: ValueMask, - // TODO: only the first filter can match on the data. - pub data: ValueMask<[u8; 1]>, + pub first_id: BitSelector<11>, + pub first_rtr: BitSelector<1>, + pub first_data: BitSelector<8>, + + pub second_id: BitSelector<11>, + pub second_rtr: BitSelector<1>, } -impl FilterToRegisters for DualStandardFilter { - fn to_registers(self) -> [u8; 8] { +impl Filter for DualStandardFilter { + const FILTER_TYPE: FilterType = FilterType::Dual; + fn to_registers(&self) -> [u8; 8] { // TODO: this. panic!("Unimplemented"); } } /// -/// NOTE: The dual extended id acceptance filter can only match "the first 16 bits of the 29-bit ID". +/// NOTE: The dual extended id acceptance filters can only match "the first 16 bits of the 29-bit ID". +/// +/// +/// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter +/// will also be accepted. pub struct DualExtendedFilter { - pub id: ValueMask, + pub id: [BitSelector<16>; 2], } -impl FilterToRegisters for DualExtendedFilter { - fn to_registers(self) -> [u8; 8] { +impl Filter for DualExtendedFilter { + const FILTER_TYPE: FilterType = FilterType::Dual; + fn to_registers(&self) -> [u8; 8] { // TODO: this. panic!("Unimplemented"); } } - -pub enum FilterIdFormat { - Standard(Std), - Extended(Ext), -} -impl FilterToRegisters for FilterIdFormat -where - Std: FilterToRegisters, - Ext: FilterToRegisters, -{ - fn to_registers(self) -> [u8; 8] { - match self { - FilterIdFormat::Standard(filter) => filter.to_registers(), - FilterIdFormat::Extended(filter) => filter.to_registers(), - } - } -} - -pub enum Filter { - Single(FilterIdFormat), - Dual(FilterIdFormat), -} - -impl FilterToRegisters for Filter { - fn to_registers(self) -> [u8; 8] { - match self { - Self::Single(single) => single.to_registers(), - Self::Dual(dual) => dual.to_registers(), - } - } -} diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index d91fe447642..736437038c9 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -1,18 +1,19 @@ use core::slice::from_raw_parts; use crate::{ - // clock::Clocks, + clock::Clocks, pac::twai::RegisterBlock, system::PeripheralClockControl, types::{InputSignal, OutputSignal}, - InputPin, - OutputPin, + InputPin, OutputPin, }; use embedded_hal::can::{self, ErrorKind, ExtendedId, Frame, StandardId}; +use fugit::HertzU32; -use self::filter::{Filter, FilterToRegisters}; +use self::filter::{Filter, FilterType}; +pub mod bitselector; pub mod filter; /// Very basic implementation of the Frame trait. @@ -165,6 +166,7 @@ where mut tx_pin: TX, mut rx_pin: RX, clock_control: &mut PeripheralClockControl, + clocks: &Clocks, baud_rate: BaudRate, ) -> Self { // TODO: Probably should do a low level reset. @@ -180,14 +182,19 @@ where peripheral: peripheral, }; - cfg.set_frequency(baud_rate); + cfg.set_baud_rate(baud_rate, clocks); cfg } /// Set the bitrate of the bus. - fn set_frequency(&mut self, baud_rate: BaudRate) { + /// + /// Note: The timings currently assume a APB_CLK of 80MHz. + /// + fn set_baud_rate(&mut self, baud_rate: BaudRate, clocks: &Clocks) { // TWAI is clocked from the APB_CLK according to Table 6-4 [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) + // Included timings are all for 80MHz so assert that we are running at 80MHz. + assert!(clocks.apb_clock == HertzU32::MHz(80)); // Unpack the baud rate timings and convert them to the values needed for the register. // Many of the registers have a minimum value of 1 which is represented by having zero @@ -229,12 +236,9 @@ where /// Set up the acceptance filter on the device to accept the specified filters. /// /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6) - pub fn set_filter(&mut self, filter: Filter) { + 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 = match filter { - Filter::Single(_) => true, - Filter::Dual(_) => false, - }; + let filter_mode_bit = filter.filter_type() == FilterType::Single; self.peripheral .register_block() .mode @@ -318,6 +322,8 @@ impl TWAI where T: Instance, { + /// Stop the peripheral, putting it into reset mode and enabling reconfiguration. + /// pub fn stop(self) -> TWAIConfiguration { // Put the peripheral into reset/configuration mode by setting the reset mode bit. self.peripheral @@ -329,7 +335,6 @@ where peripheral: self.peripheral, } } - pub fn receive_error_count(&self) -> u8 { self.peripheral .register_block() @@ -347,10 +352,6 @@ where .bits() } - pub fn status(&self) -> u32 { - self.peripheral.register_block().status.read().bits() - } - /// Test if the transmit buffer is available for writing. pub fn transmit_buffer_is_empty(&self) -> bool { self.peripheral @@ -663,7 +664,7 @@ where let dlc = (data_0 & 0b1111) as usize; // Read the payload from the packet and construct a frame. - let maybe_frame = if is_standard_format { + let frame = if is_standard_format { // Frame uses standard 11 bit id. let data_1 = self .peripheral @@ -694,7 +695,7 @@ where payload[i] = raw_payload[i] as u8; } - ESPTWAIFrame::new(id, &payload[..dlc]) + ESPTWAIFrame::new(id, &payload[..dlc]).unwrap() } else { // Frame uses extended 29 bit id. let data_1 = self @@ -742,19 +743,19 @@ where // destination and source have different strides. let raw_payload = unsafe { from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) }; - let mut payload: [u8; 8] = [0; 8]; for i in 0..dlc { payload[i] = raw_payload[i] as u8; } - ESPTWAIFrame::new(id, &payload[..dlc]) + + ESPTWAIFrame::new(id, &payload[..dlc]).unwrap() }; // Release the packet we read from the FIFO, allowing the peripheral to prepare // the next packet. self.release_receive_fifo(); - nb::Result::Ok(maybe_frame.unwrap()) + nb::Result::Ok(frame) } } diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index abc0dbef204..8b0c2cfd032 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -3,16 +3,20 @@ use core::fmt::Write; use esp32c3_hal::{ - clock::{ClockControl, CpuClock}, + clock::ClockControl, gpio::IO, pac::Peripherals, prelude::*, timer::TimerGroup, - twai::{self, ESPTWAIFrame}, + twai::{ + self, + bitselector::{BitSelectorNewExact, Selector}, + ESPTWAIFrame, + }, Rtc, UsbSerialJtag, }; -use embedded_hal::can::{Can, Frame, StandardId}; +use embedded_hal::can::{Can, Frame}; use esp_backtrace as _; use nb::block; use riscv_rt::entry; @@ -21,8 +25,7 @@ use riscv_rt::entry; fn main() -> ! { let peripherals = Peripherals::take().unwrap(); let mut system = peripherals.SYSTEM.split(); - // let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); @@ -53,26 +56,50 @@ fn main() -> ! { io.pins.gpio2, io.pins.gpio3, &mut system.peripheral_clock_control, + &clocks, twai::BaudRate::B1000K, ); - // Set bits in the bitmask means that we don't care about that bit. - let filter = twai::filter::Filter::Single(twai::filter::FilterIdFormat::Standard( - twai::filter::SingleStandardFilter { - id: twai::filter::ValueMask { - value: StandardId::new(0x000).unwrap(), - mask: StandardId::new(0x000).unwrap(), - }, - rtr: twai::filter::ValueMask { - value: false, - mask: true, - }, - data: twai::filter::ValueMask { - value: [0x00, 0x00], - mask: [0xff, 0xff], - }, + // let filter = twai::filter::SingleStandardFilter { + // id: twai::filter::BitSelector::new_exact(0b10101010101), + // rtr: twai::filter::BitSelector::new_exact(0b0), + // data: [ + // twai::filter::BitSelector::new_exact(0x24), + // twai::filter::BitSelector::new_any(), + // ], + // }; + // TODO: even though this is a single, standard id filter, extended ids will also match this filter. + let filter = twai::filter::SingleStandardFilter { + id: twai::bitselector::BitSelector { + bits: [ + Selector::Set, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + Selector::Any, + ], }, - )); + rtr: twai::bitselector::BitSelector::new_exact(0b0), + data: [ + twai::bitselector::BitSelector::new_any(), + twai::bitselector::BitSelector::new_any(), + ], + }; + + // // Dump the generated regs. + // let regs = filter.to_registers(); + // writeln!( + // UsbSerialJtag, + // "Filter Registers:\n\t{:08b} {:08b} {:08b} {:08b}\n\t{:08b} {:08b} {:08b} {:08b}", + // regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7], + // ) + // .unwrap(); can_config.set_filter(filter); @@ -81,7 +108,7 @@ fn main() -> ! { loop { // writeln!(UsbSerialJtag, "Waiting for packet...").unwrap(); let frame = block!(can.receive()).unwrap(); - writeln!(UsbSerialJtag, "Received: {:?}", frame).unwrap(); + writeln!(UsbSerialJtag, " Received: {:?}", frame).unwrap(); // Increment the payload bytes by one. let mut data: [u8; 8] = [0; 8]; From 69613ccac4990510b4676f87900179260dbe34c7 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sun, 11 Sep 2022 21:19:01 -0500 Subject: [PATCH 05/20] Fixed bug with ids and improved methods of copying data to the peripheral. --- esp-hal-common/src/twai/mod.rs | 293 ++++++++++----------------------- esp32c3-hal/examples/twai.rs | 2 +- 2 files changed, 90 insertions(+), 205 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 736437038c9..f3a77074ffa 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -1,4 +1,4 @@ -use core::slice::from_raw_parts; +use core::slice::{from_raw_parts, from_raw_parts_mut}; use crate::{ clock::Clocks, @@ -29,6 +29,25 @@ pub struct ESPTWAIFrame { is_remote: bool, } +impl ESPTWAIFrame { + /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG registers. + fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { + let mut d: [u8; 8] = [0; 8]; + + // Copy the data from the memory mapped peripheral into actual memory. + for (src, dest) in data.iter().zip(d.iter_mut()) { + *dest = *src as u8; + } + + Self { + id: id.into(), + data: d, + dlc: data.len(), + is_remote: false, + } + } +} + impl embedded_hal::can::Frame for ESPTWAIFrame { fn new(id: impl Into, data: &[u8]) -> Option { // CAN2.0 frames cannot contain more than 8 bytes of data. @@ -246,39 +265,11 @@ where // Convert the filter into values for the registers and store them to the registers. let registers = filter.to_registers(); - // TODO: Use something better for copying, probably something similar to memcpy. - self.peripheral - .register_block() - .data_0 - .write(|w| w.tx_byte_0().variant(registers[0])); - self.peripheral - .register_block() - .data_1 - .write(|w| w.tx_byte_1().variant(registers[1])); - self.peripheral - .register_block() - .data_2 - .write(|w| w.tx_byte_2().variant(registers[2])); - self.peripheral - .register_block() - .data_3 - .write(|w| w.tx_byte_3().variant(registers[3])); - self.peripheral - .register_block() - .data_4 - .write(|w| w.tx_byte_4().variant(registers[4])); - self.peripheral - .register_block() - .data_5 - .write(|w| w.tx_byte_5().variant(registers[5])); - self.peripheral - .register_block() - .data_6 - .write(|w| w.tx_byte_6().variant(registers[6])); - self.peripheral - .register_block() - .data_7 - .write(|w| w.tx_byte_7().variant(registers[7])); + + // Copy the filter to the peripheral. + unsafe { + copy_to_data_register(self.peripheral.register_block().data_0.as_ptr(), ®isters); + } } /// Set the Error warning threshold. @@ -407,6 +398,31 @@ impl embedded_hal::can::Error for ESPTWAIError { } } +/// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into the destination. +/// +/// This function will step through the source memory by STEP bytes copying an individual byte +/// into each element of the destination. +/// +unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) { + let src = from_raw_parts(src, dest.len()); + + for (dest, src) in dest.iter_mut().zip(src.iter()) { + *dest = *src as u8; + } +} +/// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into the destination. +/// +/// This function will step through the source memory by STEP bytes copying an individual byte +/// into each element of the destination. +/// +unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { + let dest = from_raw_parts_mut(dest, src.len()); + + for (dest, src) in dest.iter_mut().zip(src.iter()) { + *dest = *src as u32; + } +} + impl embedded_hal::can::Can for TWAI where T: Instance, @@ -421,7 +437,7 @@ where /// /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.4.2) /// - /// NOTE: This may not work if using the self reception/self test functionality. See + /// 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> { @@ -437,8 +453,8 @@ where } // Assemble the frame information into the data_0 byte. - let frame_format: u8 = if frame.is_extended() { 0x1 } else { 0x0 }; - let rtr_bit: u8 = if frame.is_remote_frame() { 0x1 } else { 0x0 }; + let frame_format: u8 = frame.is_extended() as u8; + let rtr_bit: u8 = frame.is_remote_frame() as u8; let dlc_bits: u8 = frame.dlc() as u8 & 0b1111; let data_0: u8 = frame_format << 7 | rtr_bit << 6 | dlc_bits; @@ -488,140 +504,18 @@ where // Store the data portion of the packet into the transmit buffer. if frame.is_data_frame() { match frame.id() { - can::Id::Standard(_) => { - // TODO: Copy data to the appropriate registers in a better method. Verified in --release asm that this is bad. - // Byte 0 of the payload. - if frame.dlc() > 0 { - let data_byte_1 = &frame.data()[0]; - self.peripheral - .register_block() - .data_3 - .write(|w| w.tx_byte_3().variant(*data_byte_1)); - } - // Byte 1 of the payload. - if frame.dlc() > 1 { - let data_byte_2 = &frame.data()[1]; - self.peripheral - .register_block() - .data_4 - .write(|w| w.tx_byte_4().variant(*data_byte_2)); - } - // Byte 2 of the payload. - if frame.dlc() > 2 { - let data_byte_3 = &frame.data()[2]; - self.peripheral - .register_block() - .data_5 - .write(|w| w.tx_byte_5().variant(*data_byte_3)); - } - // Byte 3 of the payload. - if frame.dlc() > 3 { - let data_byte_4 = &frame.data()[3]; - self.peripheral - .register_block() - .data_6 - .write(|w| w.tx_byte_6().variant(*data_byte_4)); - } - // Byte 4 of the payload. - if frame.dlc() > 4 { - let data_byte_5 = &frame.data()[4]; - self.peripheral - .register_block() - .data_7 - .write(|w| w.tx_byte_7().variant(*data_byte_5)); - } - // Byte 5 of the payload. - if frame.dlc() > 5 { - let data_byte_6 = &frame.data()[5]; - self.peripheral - .register_block() - .data_8 - .write(|w| w.tx_byte_8().variant(*data_byte_6)); - } - // Byte 6 of the payload. - if frame.dlc() > 6 { - let data_byte_7 = &frame.data()[6]; - self.peripheral - .register_block() - .data_9 - .write(|w| w.tx_byte_9().variant(*data_byte_7)); - } - // Byte 7 of the payload. - if frame.dlc() > 7 { - let data_byte_8 = &frame.data()[7]; - self.peripheral - .register_block() - .data_10 - .write(|w| w.tx_byte_10().variant(*data_byte_8)); - } - } - can::Id::Extended(_) => { - // TODO: Copy data to the appropriate registers in a better method. Verified in --release asm that this is bad. - // Byte 0 of the payload. - if frame.dlc() > 0 { - let data_byte_1 = &frame.data()[0]; - self.peripheral - .register_block() - .data_5 - .write(|w| w.tx_byte_5().variant(*data_byte_1)); - } - // Byte 1 of the payload. - if frame.dlc() > 1 { - let data_byte_2 = &frame.data()[1]; - self.peripheral - .register_block() - .data_6 - .write(|w| w.tx_byte_6().variant(*data_byte_2)); - } - // Byte 2 of the payload. - if frame.dlc() > 2 { - let data_byte_3 = &frame.data()[2]; - self.peripheral - .register_block() - .data_7 - .write(|w| w.tx_byte_7().variant(*data_byte_3)); - } - // Byte 3 of the payload. - if frame.dlc() > 3 { - let data_byte_4 = &frame.data()[3]; - self.peripheral - .register_block() - .data_8 - .write(|w| w.tx_byte_8().variant(*data_byte_4)); - } - // Byte 4 of the payload. - if frame.dlc() > 4 { - let data_byte_5 = &frame.data()[4]; - self.peripheral - .register_block() - .data_9 - .write(|w| w.tx_byte_9().variant(*data_byte_5)); - } - // Byte 5 of the payload. - if frame.dlc() > 5 { - let data_byte_6 = &frame.data()[5]; - self.peripheral - .register_block() - .data_10 - .write(|w| w.tx_byte_10().variant(*data_byte_6)); - } - // Byte 6 of the payload. - if frame.dlc() > 6 { - let data_byte_7 = &frame.data()[6]; - self.peripheral - .register_block() - .data_11 - .write(|w| w.tx_byte_11().variant(*data_byte_7)); - } - // Byte 7 of the payload. - if frame.dlc() > 7 { - let data_byte_8 = &frame.data()[7]; - self.peripheral - .register_block() - .data_12 - .write(|w| w.tx_byte_12().variant(*data_byte_8)); - } - } + can::Id::Standard(_) => unsafe { + copy_to_data_register( + self.peripheral.register_block().data_3.as_ptr(), + frame.data(), + ) + }, + can::Id::Extended(_) => unsafe { + copy_to_data_register( + self.peripheral.register_block().data_5.as_ptr(), + frame.data(), + ) + }, } } else { // Is RTR frame, so no data is included. @@ -677,25 +571,22 @@ where let data_2 = self .peripheral .register_block() - .data_1 + .data_2 .read() - .tx_byte_1() + .tx_byte_2() .bits(); - let id = StandardId::new((data_1 as u16) << 3 | (data_2 as u16) >> 5).unwrap(); + let raw_id: u16 = ((data_1 as u16) << 3) | ((data_2 as u16) >> 5); - // Copy the packet payload from the peripheral into memory. - // TODO: find a better way of doing this, basically a memcpy, but the - // destination and source have different strides. - let raw_payload = - unsafe { from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) }; + let id = StandardId::new(raw_id).unwrap(); - let mut payload: [u8; 8] = [0; 8]; - for i in 0..dlc { - payload[i] = raw_payload[i] as u8; + // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG registers. + unsafe { + ESPTWAIFrame::new_from_data_registers( + id, + from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc), + ) } - - ESPTWAIFrame::new(id, &payload[..dlc]).unwrap() } else { // Frame uses extended 29 bit id. let data_1 = self @@ -709,9 +600,9 @@ where let data_2 = self .peripheral .register_block() - .data_1 + .data_2 .read() - .tx_byte_1() + .tx_byte_2() .bits(); let data_3 = self @@ -730,25 +621,19 @@ where .tx_byte_4() .bits(); - let id = ExtendedId::new( - (data_1 as u32) << 21 - | (data_2 as u32) << 13 - | (data_3 as u32) << 5 - | (data_4 as u32) >> 3, - ) - .unwrap(); - - // Copy the packet payload from the peripheral into memory. - // TODO: find a better way of doing this, basically a memcpy, but the - // destination and source have different strides. - let raw_payload = - unsafe { from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) }; - let mut payload: [u8; 8] = [0; 8]; - for i in 0..dlc { - payload[i] = raw_payload[i] as u8; - } + let raw_id: u32 = (data_1 as u32) << 21 + | (data_2 as u32) << 13 + | (data_3 as u32) << 5 + | (data_4 as u32) >> 3; + + let id = ExtendedId::new(raw_id).unwrap(); - ESPTWAIFrame::new(id, &payload[..dlc]).unwrap() + unsafe { + ESPTWAIFrame::new_from_data_registers( + id, + from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc), + ) + } }; // Release the packet we read from the FIFO, allowing the peripheral to prepare diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 8b0c2cfd032..fbe5336447e 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -115,7 +115,7 @@ fn main() -> ! { data[..frame.dlc()].copy_from_slice(frame.data()); for b in data[..frame.dlc()].iter_mut() { - *b += 1; + (*b, _) = (*b).overflowing_add(1); } let frame = ESPTWAIFrame::new(frame.id(), &data[..frame.dlc()]).unwrap(); From c9404bcaa33a11d5df6973474a6bccbc035fa4e3 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Mon, 12 Sep 2022 21:52:33 -0500 Subject: [PATCH 06/20] Added some guards against Bus Off --- esp-hal-common/src/twai/mod.rs | 41 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index f3a77074ffa..eb0fe5077b8 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -343,21 +343,21 @@ where .bits() } - /// Test if the transmit buffer is available for writing. - pub fn transmit_buffer_is_empty(&self) -> bool { + /// Check if the controller is in a bus off state. + pub fn is_bus_off(&self) -> bool { self.peripheral .register_block() .status .read() - .tx_buf_st() - .bit() + .bus_off_st() + .bit_is_set() } - /// Get the number of messages that the peripheral has received. + /// Get the number of messages that the peripheral has available in the receive FIFO. /// /// Note that this may not be the number of messages in the receive FIFO due to /// fifo overflow/overrun. - pub fn num_messages(&self) -> u8 { + pub fn num_available_messages(&self) -> u8 { self.peripheral .register_block() .rx_message_cnt @@ -368,15 +368,17 @@ where /// Clear the receive FIFO, discarding any valid, partial, or invalid packets. /// /// This is typically used to clear an overrun receive FIFO. + /// + /// TODO: Not sure if this needs to be guarded against Bus Off. pub fn clear_receive_fifo(&self) { - while self.num_messages() > 0 { + while self.num_available_messages() > 0 { self.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. - pub fn release_receive_fifo(&self) { + fn release_receive_fifo(&self) { self.peripheral .register_block() .cmd @@ -399,11 +401,7 @@ impl embedded_hal::can::Error for ESPTWAIError { } /// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into the destination. -/// -/// This function will step through the source memory by STEP bytes copying an individual byte -/// into each element of the destination. -/// -unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) { +unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { let src = from_raw_parts(src, dest.len()); for (dest, src) in dest.iter_mut().zip(src.iter()) { @@ -411,10 +409,6 @@ unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) { } } /// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into the destination. -/// -/// This function will step through the source memory by STEP bytes copying an individual byte -/// into each element of the destination. -/// unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { let dest = from_raw_parts_mut(dest, src.len()); @@ -443,14 +437,14 @@ where fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { let status = self.peripheral.register_block().status.read(); - // 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); - } // 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); + } // Assemble the frame information into the data_0 byte. let frame_format: u8 = frame.is_extended() as u8; @@ -533,6 +527,11 @@ where 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); From d675ac9ae020055f34259a54c67d0fc2e7be7184 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 17 Sep 2022 19:53:51 -0500 Subject: [PATCH 07/20] Added reception of remote frames. --- esp-hal-common/src/twai/mod.rs | 42 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index eb0fe5077b8..2a45c921acb 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -18,12 +18,10 @@ pub mod filter; /// Very basic implementation of the Frame trait. /// -/// TODO: See if data and dlc can be simplified into a slice w/ lifetimes etc. -/// TODO: See if this can be improved. -/// #[derive(Debug)] pub struct ESPTWAIFrame { id: can::Id, + // TODO: Might be nice to have a heapless::Vec for the data. dlc: usize, data: [u8; 8], is_remote: bool, @@ -401,6 +399,11 @@ impl embedded_hal::can::Error for ESPTWAIError { } /// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into the destination. +/// +/// # Safety +/// This function is marked unsafe because it reads arbitrarily from memory-mapped registers. +/// Specifically, this function is used with the TWAI_DATA_x_REG registers which has different +/// results based on the mode of the peripheral. unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { let src = from_raw_parts(src, dest.len()); @@ -408,7 +411,13 @@ unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { *dest = *src as u8; } } + /// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into the destination. +/// +/// # Safety +/// This function is marked unsafe because it writes arbitrarily to memory-mapped registers. +/// Specifically, this function is used with the TWAI_DATA_x_REG registers which has different +/// results based on the mode of the peripheral. unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { let dest = from_raw_parts_mut(dest, src.len()); @@ -554,6 +563,7 @@ where .bits(); let is_standard_format = data_0 & 0b1 << 7 == 0; + let is_data_frame = data_0 & 0b1 << 6 == 0; let dlc = (data_0 & 0b1111) as usize; // Read the payload from the packet and construct a frame. @@ -579,12 +589,14 @@ where let id = StandardId::new(raw_id).unwrap(); - // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG registers. - unsafe { - ESPTWAIFrame::new_from_data_registers( - id, - from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc), - ) + if is_data_frame { + // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG registers. + let register_data = unsafe { + from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) + }; + ESPTWAIFrame::new_from_data_registers(id, register_data) + } else { + ESPTWAIFrame::new_remote(id, dlc).unwrap() } } else { // Frame uses extended 29 bit id. @@ -627,11 +639,13 @@ where let id = ExtendedId::new(raw_id).unwrap(); - unsafe { - ESPTWAIFrame::new_from_data_registers( - id, - from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc), - ) + if is_data_frame { + let register_data = unsafe { + from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) + }; + ESPTWAIFrame::new_from_data_registers(id, register_data) + } else { + ESPTWAIFrame::new_remote(id, dlc).unwrap() } }; From e08ef1fd4b889b2002e082e730c98eca04388e1a Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 17 Sep 2022 20:27:21 -0500 Subject: [PATCH 08/20] Clean up of comments, etc --- esp-hal-common/src/twai/filter.rs | 8 ++-- esp-hal-common/src/twai/mod.rs | 24 ++++------- esp32c3-hal/examples/twai.rs | 72 ++++++++++++------------------- 3 files changed, 39 insertions(+), 65 deletions(-) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index ee82394570d..4abbf7f29a0 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -103,7 +103,7 @@ pub struct SingleExtendedFilter { impl Filter for SingleExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Single; fn to_registers(&self) -> [u8; 8] { - panic!("Unimplemented"); + todo!(); } } @@ -123,8 +123,7 @@ pub struct DualStandardFilter { impl Filter for DualStandardFilter { const FILTER_TYPE: FilterType = FilterType::Dual; fn to_registers(&self) -> [u8; 8] { - // TODO: this. - panic!("Unimplemented"); + todo!(); } } /// @@ -139,7 +138,6 @@ pub struct DualExtendedFilter { impl Filter for DualExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Dual; fn to_registers(&self) -> [u8; 8] { - // TODO: this. - panic!("Unimplemented"); + todo!(); } } diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 2a45c921acb..b8f2b31c7ab 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -16,8 +16,7 @@ use self::filter::{Filter, FilterType}; pub mod bitselector; pub mod filter; -/// Very basic implementation of the Frame trait. -/// +/// Very basic structure backing the embedded_hal::can::Frame trait. #[derive(Debug)] pub struct ESPTWAIFrame { id: can::Id, @@ -53,8 +52,7 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { return None; } - // TODO: See struct docs for TODO list, ideally this copy would be eliminated somehow. - let mut d: [u8; 8] = Default::default(); + let mut d: [u8; 8] = [0; 8]; let (left, _unused) = d.split_at_mut(data.len()); left.clone_from_slice(data); @@ -67,9 +65,13 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { } fn new_remote(id: impl Into, dlc: usize) -> Option { + // CAN2.0 frames cannot have more than 8 bytes. + if dlc > 8 { + return None; + } Some(ESPTWAIFrame { id: id.into(), - data: Default::default(), + data: [0; 8], dlc: dlc, is_remote: true, }) @@ -82,22 +84,18 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { } } - #[inline(always)] fn is_remote_frame(&self) -> bool { self.is_remote } - #[inline(always)] fn id(&self) -> can::Id { self.id } - #[inline(always)] fn dlc(&self) -> usize { self.dlc } - #[inline(always)] fn data(&self) -> &[u8] { match self.is_remote_frame() { true => &[], @@ -186,8 +184,6 @@ where clocks: &Clocks, baud_rate: BaudRate, ) -> Self { - // TODO: Probably should do a low level reset. - // Enable the peripheral clock for the TWAI peripheral. clock_control.enable(crate::system::Peripheral::TWAI); @@ -207,7 +203,6 @@ where /// Set the bitrate of the bus. /// /// Note: The timings currently assume a APB_CLK of 80MHz. - /// fn set_baud_rate(&mut self, baud_rate: BaudRate, clocks: &Clocks) { // TWAI is clocked from the APB_CLK according to Table 6-4 [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) // Included timings are all for 80MHz so assert that we are running at 80MHz. @@ -301,8 +296,6 @@ where /// /// According to the [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) 29.4.1.2 Operation Mode, in "Normal Mode": /// The TWAI controller can transmit and receive messages including error signals (such as error and overload Frames) -/// -/// pub struct TWAI { peripheral: T, } @@ -312,7 +305,6 @@ where T: Instance, { /// Stop the peripheral, putting it into reset mode and enabling reconfiguration. - /// pub fn stop(self) -> TWAIConfiguration { // Put the peripheral into reset/configuration mode by setting the reset mode bit. self.peripheral @@ -367,7 +359,7 @@ where /// /// This is typically used to clear an overrun receive FIFO. /// - /// TODO: Not sure if this needs to be guarded against Bus Off. + /// TODO: Not sure if this needs to be guarded against Bus Off or other error states. pub fn clear_receive_fifo(&self) { while self.num_available_messages() > 0 { self.release_receive_fifo(); diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index fbe5336447e..82054c8d213 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -8,11 +8,7 @@ use esp32c3_hal::{ pac::Peripherals, prelude::*, timer::TimerGroup, - twai::{ - self, - bitselector::{BitSelectorNewExact, Selector}, - ESPTWAIFrame, - }, + twai::{self, ESPTWAIFrame}, Rtc, UsbSerialJtag, }; @@ -69,23 +65,24 @@ fn main() -> ! { // ], // }; // TODO: even though this is a single, standard id filter, extended ids will also match this filter. + // A filter that matches standard ids of an even value. let filter = twai::filter::SingleStandardFilter { id: twai::bitselector::BitSelector { bits: [ - Selector::Set, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, - Selector::Any, + twai::bitselector::Selector::Reset, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, + twai::bitselector::Selector::Any, ], }, - rtr: twai::bitselector::BitSelector::new_exact(0b0), + rtr: twai::bitselector::BitSelector::new_any(), data: [ twai::bitselector::BitSelector::new_any(), twai::bitselector::BitSelector::new_any(), @@ -110,36 +107,23 @@ fn main() -> ! { let frame = block!(can.receive()).unwrap(); writeln!(UsbSerialJtag, " Received: {:?}", frame).unwrap(); - // Increment the payload bytes by one. - let mut data: [u8; 8] = [0; 8]; - data[..frame.dlc()].copy_from_slice(frame.data()); + let frame = if frame.is_data_frame() { + // Increment the payload bytes by one. + let mut data: [u8; 8] = [0; 8]; + data[..frame.dlc()].copy_from_slice(frame.data()); - for b in data[..frame.dlc()].iter_mut() { - (*b, _) = (*b).overflowing_add(1); - } + for b in data[..frame.dlc()].iter_mut() { + (*b, _) = (*b).overflowing_add(1); + } + + ESPTWAIFrame::new(frame.id(), &data[..frame.dlc()]).unwrap() + } else { + // Echo back the request. + frame + }; - let frame = ESPTWAIFrame::new(frame.id(), &data[..frame.dlc()]).unwrap(); // Transmit the frame back. - writeln!(UsbSerialJtag, "Transmitting: {:?}", frame).unwrap(); + // writeln!(UsbSerialJtag, "Transmitting: {:?}", frame).unwrap(); let _result = block!(can.transmit(&frame)).unwrap(); - - // let status = can.status(); - // writeln!( - // UsbSerialJtag, - // "CAN Status: {:b}\n\t RX_BUF_ST {}\n\t OVERRUN_ST {}\n\t TX_BUF_ST {}\n\t TX_COMPLETE {}\n\t RX_ST {}\n\t TX_ST {}\n\t ERR_ST {}\n\t BUS_OFF_ST {}\n\t MISS_ST {}", - // status, - // (status >> 0) & 0b1 != 0, - // (status >> 1) & 0b1 != 0, - // (status >> 2) & 0b1 != 0, - // (status >> 3) & 0b1 != 0, - // (status >> 4) & 0b1 != 0, - // (status >> 5) & 0b1 != 0, - // (status >> 6) & 0b1 != 0, - // (status >> 7) & 0b1 != 0, - // (status >> 8) & 0b1 != 0, - // ) - // .ok(); - - // block!(timer0.wait()).unwrap(); } } From cd343de78c6e3f0df14935461fc63ecb9deddc06 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 21 Sep 2022 00:33:12 -0500 Subject: [PATCH 09/20] Updated TWAI naming and cleaned up example a bit. --- esp-hal-common/src/lib.rs | 1 + esp-hal-common/src/system.rs | 4 +- esp-hal-common/src/twai/mod.rs | 63 +++++++++++------------ esp32c3-hal/examples/twai.rs | 94 +++++++++++++++------------------- 4 files changed, 76 insertions(+), 86 deletions(-) diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index b8ccd5e79b9..ef4ecfa26fa 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -73,6 +73,7 @@ pub mod system; #[cfg(systimer)] pub mod systimer; pub mod timer; +pub mod twai; pub mod uart; #[cfg(usb_serial_jtag)] pub mod usb_serial_jtag; diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 8be418372b7..47b5c49bf49 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -42,7 +42,7 @@ pub enum Peripheral { I2s1, #[cfg(usb_otg)] Usb, - TWAI, + Twai, } /// Controls the enablement of peripheral clocks. @@ -150,7 +150,7 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.usb_rst().clear_bit()); } - Peripheral::TWAI => { + Peripheral::Twai => { perip_clk_en0.modify(|_, w| w.can_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.can_rst().clear_bit()); } diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index b8f2b31c7ab..f90f014dd52 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -16,17 +16,16 @@ use self::filter::{Filter, FilterType}; pub mod bitselector; pub mod filter; -/// Very basic structure backing the embedded_hal::can::Frame trait. +/// Structure backing the embedded_hal::can::Frame trait. #[derive(Debug)] -pub struct ESPTWAIFrame { +pub struct EspTwaiFrame { id: can::Id, - // TODO: Might be nice to have a heapless::Vec for the data. dlc: usize, data: [u8; 8], is_remote: bool, } -impl ESPTWAIFrame { +impl EspTwaiFrame { /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG registers. fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { let mut d: [u8; 8] = [0; 8]; @@ -45,7 +44,7 @@ impl ESPTWAIFrame { } } -impl embedded_hal::can::Frame for ESPTWAIFrame { +impl embedded_hal::can::Frame for EspTwaiFrame { fn new(id: impl Into, data: &[u8]) -> Option { // CAN2.0 frames cannot contain more than 8 bytes of data. if data.len() > 8 { @@ -56,7 +55,7 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { let (left, _unused) = d.split_at_mut(data.len()); left.clone_from_slice(data); - Some(ESPTWAIFrame { + Some(EspTwaiFrame { id: id.into(), data: d, dlc: data.len(), @@ -69,10 +68,10 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { if dlc > 8 { return None; } - Some(ESPTWAIFrame { + Some(EspTwaiFrame { id: id.into(), data: [0; 8], - dlc: dlc, + dlc, is_remote: true, }) } @@ -97,6 +96,8 @@ impl embedded_hal::can::Frame for ESPTWAIFrame { } fn data(&self) -> &[u8] { + // Remote frames do not contain data, yet have a value for the dlc so return + // an empty slice for remote frames. match self.is_remote_frame() { true => &[], false => &self.data[0..self.dlc], @@ -168,11 +169,11 @@ impl BaudRate { } /// An inactive TWAI peripheral in the "Reset"/configuration state. -pub struct TWAIConfiguration { +pub struct TwaiConfiguration { peripheral: T, } -impl TWAIConfiguration +impl TwaiConfiguration where T: Instance, { @@ -185,15 +186,13 @@ where baud_rate: BaudRate, ) -> Self { // Enable the peripheral clock for the TWAI peripheral. - clock_control.enable(crate::system::Peripheral::TWAI); + clock_control.enable(crate::system::Peripheral::Twai); // Set up the GPIO pins. tx_pin.connect_peripheral_to_output(OutputSignal::TWAI_TX); rx_pin.connect_input_to_peripheral(InputSignal::TWAI_RX); - let mut cfg = TWAIConfiguration { - peripheral: peripheral, - }; + let mut cfg = TwaiConfiguration { peripheral }; cfg.set_baud_rate(baud_rate, clocks); @@ -279,14 +278,14 @@ where /// Put the peripheral into Operation Mode, allowing the transmission and reception of /// packets using the new object. - pub fn start(self) -> TWAI { + pub fn start(self) -> Twai { // Put the peripheral into operation mode by clearing the reset mode bit. self.peripheral .register_block() .mode .modify(|_, w| w.reset_mode().clear_bit()); - TWAI { + Twai { peripheral: self.peripheral, } } @@ -296,23 +295,23 @@ where /// /// According to the [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) 29.4.1.2 Operation Mode, in "Normal Mode": /// The TWAI controller can transmit and receive messages including error signals (such as error and overload Frames) -pub struct TWAI { +pub struct Twai { peripheral: T, } -impl TWAI +impl Twai where T: Instance, { /// Stop the peripheral, putting it into reset mode and enabling reconfiguration. - pub fn stop(self) -> TWAIConfiguration { + pub fn stop(self) -> TwaiConfiguration { // Put the peripheral into reset/configuration mode by setting the reset mode bit. self.peripheral .register_block() .mode .modify(|_, w| w.reset_mode().set_bit()); - TWAIConfiguration { + TwaiConfiguration { peripheral: self.peripheral, } } @@ -377,11 +376,11 @@ where } #[derive(Debug)] -pub enum ESPTWAIError { +pub enum EspTwaiError { BusOff, EmbeddedHAL(embedded_hal::can::ErrorKind), } -impl embedded_hal::can::Error for ESPTWAIError { +impl embedded_hal::can::Error for EspTwaiError { fn kind(&self) -> can::ErrorKind { match self { Self::BusOff => can::ErrorKind::Other, @@ -418,12 +417,12 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl embedded_hal::can::Can for TWAI +impl embedded_hal::can::Can for Twai where T: Instance, { - type Frame = ESPTWAIFrame; - type Error = ESPTWAIError; + 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 @@ -440,7 +439,7 @@ where // 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)); + 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() { @@ -530,7 +529,7 @@ where // 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)); + return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); } // Check that we actually have packets to receive. @@ -540,7 +539,7 @@ where // 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( + return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL( ErrorKind::Overrun, ))); } @@ -586,9 +585,9 @@ where let register_data = unsafe { from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) }; - ESPTWAIFrame::new_from_data_registers(id, register_data) + EspTwaiFrame::new_from_data_registers(id, register_data) } else { - ESPTWAIFrame::new_remote(id, dlc).unwrap() + EspTwaiFrame::new_remote(id, dlc).unwrap() } } else { // Frame uses extended 29 bit id. @@ -635,9 +634,9 @@ where let register_data = unsafe { from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) }; - ESPTWAIFrame::new_from_data_registers(id, register_data) + EspTwaiFrame::new_from_data_registers(id, register_data) } else { - ESPTWAIFrame::new_remote(id, dlc).unwrap() + EspTwaiFrame::new_remote(id, dlc).unwrap() } }; diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 82054c8d213..fe46e70a1c3 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -3,16 +3,11 @@ use core::fmt::Write; use esp32c3_hal::{ - clock::ClockControl, - gpio::IO, - pac::Peripherals, - prelude::*, - timer::TimerGroup, - twai::{self, ESPTWAIFrame}, - Rtc, UsbSerialJtag, + clock::ClockControl, gpio::IO, pac::Peripherals, prelude::*, timer::TimerGroup, twai, Rtc, + UsbSerialJtag, }; -use embedded_hal::can::{Can, Frame}; +use embedded_hal::can::{Can, Frame, Id, StandardId}; use esp_backtrace as _; use nb::block; use riscv_rt::entry; @@ -25,7 +20,6 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let mut timer0 = timer_group0.timer0; let mut wdt0 = timer_group0.wdt; let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); let mut wdt1 = timer_group1.wdt; @@ -36,34 +30,30 @@ fn main() -> ! { wdt0.disable(); wdt1.disable(); - timer0.start(500u64.millis()); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - writeln!( - UsbSerialJtag, - "Clocks: apb: {} cpu: {} xtal: {}", - clocks.apb_clock, clocks.cpu_clock, clocks.xtal_clock - ) - .unwrap(); + // 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; - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + // The speed of the CAN bus. + let can_baudrate = twai::BaudRate::B1000K; - let mut can_config = twai::TWAIConfiguration::new( + // 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.TWAI, - io.pins.gpio2, - io.pins.gpio3, + can_tx_pin, + can_rx_pin, &mut system.peripheral_clock_control, &clocks, - twai::BaudRate::B1000K, + can_baudrate, ); - // let filter = twai::filter::SingleStandardFilter { - // id: twai::filter::BitSelector::new_exact(0b10101010101), - // rtr: twai::filter::BitSelector::new_exact(0b0), - // data: [ - // twai::filter::BitSelector::new_exact(0x24), - // twai::filter::BitSelector::new_any(), - // ], - // }; + // 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. // TODO: even though this is a single, standard id filter, extended ids will also match this filter. // A filter that matches standard ids of an even value. let filter = twai::filter::SingleStandardFilter { @@ -88,42 +78,42 @@ fn main() -> ! { twai::bitselector::BitSelector::new_any(), ], }; - - // // Dump the generated regs. - // let regs = filter.to_registers(); - // writeln!( - // UsbSerialJtag, - // "Filter Registers:\n\t{:08b} {:08b} {:08b} {:08b}\n\t{:08b} {:08b} {:08b} {:08b}", - // regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7], - // ) - // .unwrap(); - 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 mut can = can_config.start(); loop { - // writeln!(UsbSerialJtag, "Waiting for packet...").unwrap(); + // Wait for a frame to be received. let frame = block!(can.receive()).unwrap(); - writeln!(UsbSerialJtag, " Received: {:?}", frame).unwrap(); - let frame = if frame.is_data_frame() { - // Increment the payload bytes by one. - let mut data: [u8; 8] = [0; 8]; - data[..frame.dlc()].copy_from_slice(frame.data()); + writeln!(UsbSerialJtag, "Received a frame:").unwrap(); - for b in data[..frame.dlc()].iter_mut() { - (*b, _) = (*b).overflowing_add(1); + // Print different messages based on the frame id type. + match frame.id() { + Id::Standard(id) => { + writeln!(UsbSerialJtag, "\tStandard Id: {:?}", id).unwrap(); + } + Id::Extended(id) => { + writeln!(UsbSerialJtag, "\tExtended Id: {:?}", id).unwrap(); } + } - ESPTWAIFrame::new(frame.id(), &data[..frame.dlc()]).unwrap() + // Print out the frame data or the requested data length code for a remote + // transmission request frame. + if frame.is_data_frame() { + writeln!(UsbSerialJtag, "\tData: {:?}", frame.data()).unwrap(); } else { - // Echo back the request. - frame - }; + writeln!( + UsbSerialJtag, + "\tRemote Frame. Data Length Code: {}", + frame.dlc() + ) + .unwrap(); + } // Transmit the frame back. - // writeln!(UsbSerialJtag, "Transmitting: {:?}", frame).unwrap(); let _result = block!(can.transmit(&frame)).unwrap(); } } From 336e104a56739f45c7fa245ed4d24fe0720aeea8 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 28 Sep 2022 23:05:08 -0500 Subject: [PATCH 10/20] Updated bitselector to include better unpacking methods. --- esp-hal-common/src/twai/bitselector.rs | 198 +++++++++++++++++-------- esp-hal-common/src/twai/filter.rs | 92 +++--------- 2 files changed, 159 insertions(+), 131 deletions(-) diff --git a/esp-hal-common/src/twai/bitselector.rs b/esp-hal-common/src/twai/bitselector.rs index f688686eb40..37040dcdccd 100644 --- a/esp-hal-common/src/twai/bitselector.rs +++ b/esp-hal-common/src/twai/bitselector.rs @@ -1,94 +1,168 @@ +use core::ops::Shr; + +/// A single bit of a bitmask filter. +/// +/// A Set bit will match only a set bit, a Reset bit will match only a reset bit, and Any +/// will match any bit value. +/// +/// The EXACT const generic determines the interpretation of the mask bit. A true value means +/// that set bits in the mask match the value exactly. A false value means that reset +/// bits in the mask match exactly. +/// #[derive(Debug, Clone, Copy)] -pub enum Selector { +pub enum Selector { Set, Reset, Any, } -impl Selector { - pub fn into_value(&self) -> u8 { - match self { - Selector::Set => 0b1, - Selector::Reset => 0b0, - Selector::Any => 0b0, +impl From for Selector { + fn from(value: u8) -> Self { + if value & 0b1 != 0 { + Selector::Set + } else { + Selector::Reset } } - pub fn into_mask(&self) -> u8 { - // Set bits in the mask mean we don't care about the value of that bit. The bit could be any value. - // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 - match self { - Selector::Set => 0b0, - Selector::Reset => 0b0, - Selector::Any => 0b1, - } +} +impl From for Selector { + fn from(value: u16) -> Self { + (value as u8).into() } } - -impl Into for bool { - fn into(self) -> Selector { - match self { - true => Selector::Set, - false => Selector::Reset, - } +impl From for Selector { + fn from(value: u32) -> Self { + (value as u8).into() } } - -pub trait BitSelectorNewExact { - fn new_exact(value: T) -> BitSelector; +impl From for Selector { + fn from(value: u64) -> Self { + (value as u8).into() + } } +/// A bitmask filter. #[derive(Debug, Clone, Copy)] -pub struct BitSelector { - pub bits: [Selector; N], +pub struct BitSelector { + pub bits: [Selector; N], } -impl BitSelector { +impl BitSelector { + pub fn new(bits: [Selector; N]) -> Self { + Self { bits } + } + /// Create a new selector that matches any value. pub fn new_any() -> Self { Self { bits: [Selector::Any; N], } } } -// TODO: improve this. -impl BitSelectorNewExact for BitSelector<11> { - fn new_exact(value: u16) -> BitSelector<11> { - Self { - bits: [ - (value & 0b00000000001 != 0).into(), - (value & 0b00000000010 != 0).into(), - (value & 0b00000000100 != 0).into(), - (value & 0b00000001000 != 0).into(), - (value & 0b00000010000 != 0).into(), - (value & 0b00000100000 != 0).into(), - (value & 0b00001000000 != 0).into(), - (value & 0b00010000000 != 0).into(), - (value & 0b00100000000 != 0).into(), - (value & 0b01000000000 != 0).into(), - (value & 0b10000000000 != 0).into(), - ], + +/// A trait to convert a value into a selector of a specified size that exactly matches the value. +/// +/// This will convert type T into a selector of size N bits. The EXACT is forwarded to the +/// selector and it's meaning is further explained in the selector docs. +pub trait BitSelectorNewExact +where + T: Into> + Shr + Copy, +{ + /// Create a new selector that matches exactly the given value. + fn new_exact(mut value: T) -> BitSelector { + let mut bits = [Selector::Any; N]; + + for bit in bits.iter_mut() { + *bit = value.into(); + value = value >> 1; } + + BitSelector { bits } } } -impl BitSelectorNewExact for BitSelector<8> { - fn new_exact(value: u8) -> BitSelector<8> { - Self { - bits: [ - (value & 0b00000000001 != 0).into(), - (value & 0b00000000010 != 0).into(), - (value & 0b00000000100 != 0).into(), - (value & 0b00000001000 != 0).into(), - (value & 0b00000010000 != 0).into(), - (value & 0b00000100000 != 0).into(), - (value & 0b00001000000 != 0).into(), - (value & 0b00010000000 != 0).into(), - ], + +impl BitSelectorNewExact for BitSelector {} +impl BitSelectorNewExact for BitSelector {} +impl BitSelectorNewExact for BitSelector {} + +/// Trait to convert selector type into values and masks. +pub trait SelectorInto { + /// Convert the selector into the value portion of the filter. + fn to_value(&self) -> T; + /// Convert the selector into the mask portion of the filter. + fn to_mask(&self) -> T; +} + +impl SelectorInto for Selector { + fn to_value(&self) -> u8 { + match self { + Selector::Set => 0b1, + Selector::Reset => 0b0, + Selector::Any => 0b0, + } + } + fn to_mask(&self) -> u8 { + match self { + Selector::Set => EXACT as u8, + Selector::Reset => EXACT as u8, + Selector::Any => (!EXACT) as u8, } } } -impl BitSelectorNewExact for BitSelector<1> { - fn new_exact(value: u8) -> BitSelector<1> { - Self { - bits: [(value & 0b1 != 0).into()], + +impl SelectorInto for [Selector] { + fn to_value(&self) -> u8 { + let mut value: u8 = 0; + + for bit in self.iter().rev() { + let v: u8 = bit.to_value(); + value = (value << 1) | v; + } + + value + } + fn to_mask(&self) -> u8 { + let mut mask: u8 = 0; + + // Fill the unused portion of the mask with bits that match against anything. + for _ in 0..8 { + mask = (mask << 1) | ((!EXACT) as u8); + } + + // Insert the bits from the slice into the mask. + for bit in self.iter().rev() { + let m: u8 = bit.to_mask(); + mask = (mask << 1) | m; + } + + mask + } +} + +impl SelectorInto for BitSelector { + fn to_value(&self) -> u8 { + self.bits.as_slice().to_value() + } + fn to_mask(&self) -> u8 { + self.bits.as_slice().to_mask() + } +} +impl core::fmt::Display for Selector { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let b_str = match self { + Selector::Any => "x", + Selector::Reset => "0", + Selector::Set => "1", + }; + write!(f, "{}", b_str) + } +} + +impl core::fmt::Display for BitSelector { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "0b")?; + for bit in self.bits.iter().rev() { + write!(f, "{}", bit)?; } + Ok(()) } } diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 4abbf7f29a0..8715f5e8f48 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,4 +1,4 @@ -use super::bitselector::BitSelector; +use super::bitselector::{BitSelector, SelectorInto}; #[derive(Debug, PartialEq)] pub enum FilterType { @@ -6,6 +6,9 @@ pub enum FilterType { Dual, } +// Set bits in the mask mean we don't care about the value of that bit. The bit could be any value. +// https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 + pub trait Filter { // The type of the filter. const FILTER_TYPE: FilterType; @@ -21,9 +24,9 @@ pub trait Filter { /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. pub struct SingleStandardFilter { - pub id: BitSelector<11>, - pub rtr: BitSelector<1>, - pub data: [BitSelector<8>; 2], + pub id: BitSelector, + pub rtr: BitSelector, + pub data: [BitSelector; 2], } impl Filter for SingleStandardFilter { @@ -31,64 +34,15 @@ impl Filter for SingleStandardFilter { fn to_registers(&self) -> [u8; 8] { [ // Value. - // TODO: Implement some sort of into for slices of bits so that we can simplify some of this code. - self.id.bits[10].into_value() << 7 - | self.id.bits[9].into_value() << 6 - | self.id.bits[8].into_value() << 5 - | self.id.bits[7].into_value() << 4 - | self.id.bits[6].into_value() << 3 - | self.id.bits[5].into_value() << 2 - | self.id.bits[4].into_value() << 1 - | self.id.bits[3].into_value(), - self.id.bits[2].into_value() << 7 - | self.id.bits[1].into_value() << 6 - | self.id.bits[0].into_value() << 5 - | self.rtr.bits[0].into_value() << 4, - self.data[0].bits[7].into_value() << 7 - | self.data[0].bits[6].into_value() << 6 - | self.data[0].bits[5].into_value() << 5 - | self.data[0].bits[4].into_value() << 4 - | self.data[0].bits[3].into_value() << 3 - | self.data[0].bits[2].into_value() << 2 - | self.data[0].bits[1].into_value() << 1 - | self.data[0].bits[0].into_value() << 0, - self.data[1].bits[7].into_value() << 7 - | self.data[1].bits[6].into_value() << 6 - | self.data[1].bits[5].into_value() << 5 - | self.data[1].bits[4].into_value() << 4 - | self.data[1].bits[3].into_value() << 3 - | self.data[1].bits[2].into_value() << 2 - | self.data[1].bits[1].into_value() << 1 - | self.data[1].bits[0].into_value() << 0, + self.id.bits[3..11].to_value(), + self.id.bits[0..3].to_value() << 5 | self.rtr.bits[0].to_value() << 4, + self.data[0].to_value(), + self.data[1].to_value(), // Mask. - self.id.bits[10].into_mask() << 7 - | self.id.bits[9].into_mask() << 6 - | self.id.bits[8].into_mask() << 5 - | self.id.bits[7].into_mask() << 4 - | self.id.bits[6].into_mask() << 3 - | self.id.bits[5].into_mask() << 2 - | self.id.bits[4].into_mask() << 1 - | self.id.bits[3].into_mask(), - self.id.bits[2].into_mask() << 7 - | self.id.bits[1].into_mask() << 6 - | self.id.bits[0].into_mask() << 5 - | self.rtr.bits[0].into_mask() << 4, - self.data[0].bits[7].into_mask() << 7 - | self.data[0].bits[6].into_mask() << 6 - | self.data[0].bits[5].into_mask() << 5 - | self.data[0].bits[4].into_mask() << 4 - | self.data[0].bits[3].into_mask() << 3 - | self.data[0].bits[2].into_mask() << 2 - | self.data[0].bits[1].into_mask() << 1 - | self.data[0].bits[0].into_mask() << 0, - self.data[1].bits[7].into_mask() << 7 - | self.data[1].bits[6].into_mask() << 6 - | self.data[1].bits[5].into_mask() << 5 - | self.data[1].bits[4].into_mask() << 4 - | self.data[1].bits[3].into_mask() << 3 - | self.data[1].bits[2].into_mask() << 2 - | self.data[1].bits[1].into_mask() << 1 - | self.data[1].bits[0].into_mask() << 0, + self.id.bits[3..11].to_mask(), + self.id.bits[0..3].to_mask() << 5 | self.rtr.bits[0].to_mask() << 4, + self.data[0].to_mask(), + self.data[1].to_mask(), ] } } @@ -97,8 +51,8 @@ impl Filter for SingleStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct SingleExtendedFilter { - pub id: BitSelector<29>, - pub rtr: BitSelector<1>, + pub id: BitSelector, + pub rtr: BitSelector, } impl Filter for SingleExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Single; @@ -113,12 +67,12 @@ impl Filter for SingleExtendedFilter { /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. pub struct DualStandardFilter { - pub first_id: BitSelector<11>, - pub first_rtr: BitSelector<1>, - pub first_data: BitSelector<8>, + pub first_id: BitSelector, + pub first_rtr: BitSelector, + pub first_data: BitSelector, - pub second_id: BitSelector<11>, - pub second_rtr: BitSelector<1>, + pub second_id: BitSelector, + pub second_rtr: BitSelector, } impl Filter for DualStandardFilter { const FILTER_TYPE: FilterType = FilterType::Dual; @@ -133,7 +87,7 @@ impl Filter for DualStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct DualExtendedFilter { - pub id: [BitSelector<16>; 2], + pub id: [BitSelector; 2], } impl Filter for DualExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Dual; From 3e4a46b9f656a8b290436c7e73dbf6c5ccf0c208 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 9 Nov 2022 19:33:19 -0600 Subject: [PATCH 11/20] Add embedded-can and limit initial TWAI implementation to esp32c3. --- esp-hal-common/Cargo.toml | 3 ++- esp-hal-common/src/lib.rs | 1 + esp-hal-common/src/system.rs | 2 ++ esp-hal-common/src/twai/mod.rs | 40 +++++++++++++++++++--------------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 2be2d714ee8..a6a6658aaea 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -17,6 +17,7 @@ critical-section = "1.1.1" embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true } +embedded-can = { version = "0.4.1", optional = true } fugit = "0.3.6" lock_api = { version = "0.4.9", optional = true } nb = "1.0.0" @@ -67,7 +68,7 @@ esp32c2_40mhz = [] esp32c2_26mhz = [] # Implement the `embedded-hal==1.0.0-alpha.x` traits -eh1 = ["embedded-hal-1", "embedded-hal-nb"] +eh1 = ["embedded-hal-1", "embedded-hal-nb", "embedded-can"] # To use the external `smart_led` crate smartled = ["smart-leds-trait"] diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index ef4ecfa26fa..89070691f34 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -73,6 +73,7 @@ pub mod system; #[cfg(systimer)] pub mod systimer; pub mod timer; +#[cfg(any(esp32c3))] pub mod twai; pub mod uart; #[cfg(usb_serial_jtag)] diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 47b5c49bf49..bce01d8178d 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -42,6 +42,7 @@ pub enum Peripheral { I2s1, #[cfg(usb_otg)] Usb, + #[cfg(any(esp32c3))] Twai, } @@ -150,6 +151,7 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.usb_rst().clear_bit()); } + #[cfg(any(esp32c3))] Peripheral::Twai => { perip_clk_en0.modify(|_, w| w.can_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.can_rst().clear_bit()); diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index f90f014dd52..0ba5014b787 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -8,7 +8,11 @@ use crate::{ InputPin, OutputPin, }; -use embedded_hal::can::{self, ErrorKind, ExtendedId, Frame, StandardId}; +#[cfg(feature = "eh1")] +use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; +#[cfg(not(feature = "eh1"))] +use embedded_hal::can::{Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; + use fugit::HertzU32; use self::filter::{Filter, FilterType}; @@ -19,7 +23,7 @@ pub mod filter; /// Structure backing the embedded_hal::can::Frame trait. #[derive(Debug)] pub struct EspTwaiFrame { - id: can::Id, + id: Id, dlc: usize, data: [u8; 8], is_remote: bool, @@ -27,7 +31,7 @@ pub struct EspTwaiFrame { impl EspTwaiFrame { /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG registers. - fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { + fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { let mut d: [u8; 8] = [0; 8]; // Copy the data from the memory mapped peripheral into actual memory. @@ -44,8 +48,8 @@ impl EspTwaiFrame { } } -impl embedded_hal::can::Frame for EspTwaiFrame { - fn new(id: impl Into, data: &[u8]) -> Option { +impl Frame for EspTwaiFrame { + fn new(id: impl Into, data: &[u8]) -> Option { // CAN2.0 frames cannot contain more than 8 bytes of data. if data.len() > 8 { return None; @@ -63,7 +67,7 @@ impl embedded_hal::can::Frame for EspTwaiFrame { }) } - fn new_remote(id: impl Into, dlc: usize) -> Option { + fn new_remote(id: impl Into, dlc: usize) -> Option { // CAN2.0 frames cannot have more than 8 bytes. if dlc > 8 { return None; @@ -78,8 +82,8 @@ impl embedded_hal::can::Frame for EspTwaiFrame { fn is_extended(&self) -> bool { match self.id { - can::Id::Standard(_) => false, - can::Id::Extended(_) => true, + Id::Standard(_) => false, + Id::Extended(_) => true, } } @@ -87,7 +91,7 @@ impl embedded_hal::can::Frame for EspTwaiFrame { self.is_remote } - fn id(&self) -> can::Id { + fn id(&self) -> Id { self.id } @@ -378,12 +382,12 @@ where #[derive(Debug)] pub enum EspTwaiError { BusOff, - EmbeddedHAL(embedded_hal::can::ErrorKind), + EmbeddedHAL(ErrorKind), } -impl embedded_hal::can::Error for EspTwaiError { - fn kind(&self) -> can::ErrorKind { +impl Error for EspTwaiError { + fn kind(&self) -> ErrorKind { match self { - Self::BusOff => can::ErrorKind::Other, + Self::BusOff => ErrorKind::Other, Self::EmbeddedHAL(kind) => *kind, } } @@ -417,7 +421,7 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl embedded_hal::can::Can for Twai +impl Can for Twai where T: Instance, { @@ -460,7 +464,7 @@ where // Assemble the identifier information of the packet. match frame.id() { - can::Id::Standard(id) => { + Id::Standard(id) => { let id = id.as_raw(); self.peripheral @@ -473,7 +477,7 @@ where .data_2 .write(|w| w.tx_byte_2().variant((id << 5) as u8)); } - can::Id::Extended(id) => { + Id::Extended(id) => { let id = id.as_raw(); self.peripheral @@ -498,13 +502,13 @@ where // Store the data portion of the packet into the transmit buffer. if frame.is_data_frame() { match frame.id() { - can::Id::Standard(_) => unsafe { + Id::Standard(_) => unsafe { copy_to_data_register( self.peripheral.register_block().data_3.as_ptr(), frame.data(), ) }, - can::Id::Extended(_) => unsafe { + Id::Extended(_) => unsafe { copy_to_data_register( self.peripheral.register_block().data_5.as_ptr(), frame.data(), From b28bb96ee3a29f9ca917eb7f06518d429670001a Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 9 Nov 2022 19:58:58 -0600 Subject: [PATCH 12/20] Added embedded-can to esp32c3 twai example. --- esp32c3-hal/Cargo.toml | 3 ++- esp32c3-hal/examples/twai.rs | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index f938f52c428..7df9a8f5cd8 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -31,6 +31,7 @@ embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } embedded-hal-async = { version = "0.1.0-alpha.3", optional = true } embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true } +embedded-can = { version = "0.4.1", optional = true } esp-hal-common = { version = "0.4.0", features = ["esp32c3"], path = "../esp-hal-common" } r0 = "1.0.0" riscv = "0.10.0" @@ -51,7 +52,7 @@ static_cell = "1.0.0" default = ["rt", "vectored"] mcu-boot = [] direct-boot = [] -eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] +eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb", "dep:embedded-can"] rt = ["riscv-rt"] smartled = ["esp-hal-common/smartled"] ufmt = ["esp-hal-common/ufmt"] diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index fe46e70a1c3..b6606c8cde4 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -7,7 +7,17 @@ use esp32c3_hal::{ UsbSerialJtag, }; -use embedded_hal::can::{Can, Frame, Id, StandardId}; +// 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}; + +// 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 esp_backtrace as _; use nb::block; use riscv_rt::entry; From 0389fe8952778fbd7e2872f8049306b9d1048917 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Fri, 11 Nov 2022 18:59:43 -0600 Subject: [PATCH 13/20] Switched twai filter to using bytestrings. Co-authored-by: dimi --- esp-hal-common/src/twai/bitselector.rs | 168 ------------------------- esp-hal-common/src/twai/filter.rs | 143 ++++++++++++++++----- esp-hal-common/src/twai/mod.rs | 1 - esp32c3-hal/examples/twai.rs | 24 +--- 4 files changed, 115 insertions(+), 221 deletions(-) delete mode 100644 esp-hal-common/src/twai/bitselector.rs diff --git a/esp-hal-common/src/twai/bitselector.rs b/esp-hal-common/src/twai/bitselector.rs deleted file mode 100644 index 37040dcdccd..00000000000 --- a/esp-hal-common/src/twai/bitselector.rs +++ /dev/null @@ -1,168 +0,0 @@ -use core::ops::Shr; - -/// A single bit of a bitmask filter. -/// -/// A Set bit will match only a set bit, a Reset bit will match only a reset bit, and Any -/// will match any bit value. -/// -/// The EXACT const generic determines the interpretation of the mask bit. A true value means -/// that set bits in the mask match the value exactly. A false value means that reset -/// bits in the mask match exactly. -/// -#[derive(Debug, Clone, Copy)] -pub enum Selector { - Set, - Reset, - Any, -} - -impl From for Selector { - fn from(value: u8) -> Self { - if value & 0b1 != 0 { - Selector::Set - } else { - Selector::Reset - } - } -} -impl From for Selector { - fn from(value: u16) -> Self { - (value as u8).into() - } -} -impl From for Selector { - fn from(value: u32) -> Self { - (value as u8).into() - } -} -impl From for Selector { - fn from(value: u64) -> Self { - (value as u8).into() - } -} - -/// A bitmask filter. -#[derive(Debug, Clone, Copy)] -pub struct BitSelector { - pub bits: [Selector; N], -} - -impl BitSelector { - pub fn new(bits: [Selector; N]) -> Self { - Self { bits } - } - /// Create a new selector that matches any value. - pub fn new_any() -> Self { - Self { - bits: [Selector::Any; N], - } - } -} - -/// A trait to convert a value into a selector of a specified size that exactly matches the value. -/// -/// This will convert type T into a selector of size N bits. The EXACT is forwarded to the -/// selector and it's meaning is further explained in the selector docs. -pub trait BitSelectorNewExact -where - T: Into> + Shr + Copy, -{ - /// Create a new selector that matches exactly the given value. - fn new_exact(mut value: T) -> BitSelector { - let mut bits = [Selector::Any; N]; - - for bit in bits.iter_mut() { - *bit = value.into(); - value = value >> 1; - } - - BitSelector { bits } - } -} - -impl BitSelectorNewExact for BitSelector {} -impl BitSelectorNewExact for BitSelector {} -impl BitSelectorNewExact for BitSelector {} - -/// Trait to convert selector type into values and masks. -pub trait SelectorInto { - /// Convert the selector into the value portion of the filter. - fn to_value(&self) -> T; - /// Convert the selector into the mask portion of the filter. - fn to_mask(&self) -> T; -} - -impl SelectorInto for Selector { - fn to_value(&self) -> u8 { - match self { - Selector::Set => 0b1, - Selector::Reset => 0b0, - Selector::Any => 0b0, - } - } - fn to_mask(&self) -> u8 { - match self { - Selector::Set => EXACT as u8, - Selector::Reset => EXACT as u8, - Selector::Any => (!EXACT) as u8, - } - } -} - -impl SelectorInto for [Selector] { - fn to_value(&self) -> u8 { - let mut value: u8 = 0; - - for bit in self.iter().rev() { - let v: u8 = bit.to_value(); - value = (value << 1) | v; - } - - value - } - fn to_mask(&self) -> u8 { - let mut mask: u8 = 0; - - // Fill the unused portion of the mask with bits that match against anything. - for _ in 0..8 { - mask = (mask << 1) | ((!EXACT) as u8); - } - - // Insert the bits from the slice into the mask. - for bit in self.iter().rev() { - let m: u8 = bit.to_mask(); - mask = (mask << 1) | m; - } - - mask - } -} - -impl SelectorInto for BitSelector { - fn to_value(&self) -> u8 { - self.bits.as_slice().to_value() - } - fn to_mask(&self) -> u8 { - self.bits.as_slice().to_mask() - } -} -impl core::fmt::Display for Selector { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let b_str = match self { - Selector::Any => "x", - Selector::Reset => "0", - Selector::Set => "1", - }; - write!(f, "{}", b_str) - } -} - -impl core::fmt::Display for BitSelector { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "0b")?; - for bit in self.bits.iter().rev() { - write!(f, "{}", bit)?; - } - Ok(()) - } -} diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 8715f5e8f48..78d3a97879f 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,14 +1,9 @@ -use super::bitselector::{BitSelector, SelectorInto}; - -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum FilterType { Single, Dual, } -// Set bits in the mask mean we don't care about the value of that bit. The bit could be any value. -// https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 - pub trait Filter { // The type of the filter. const FILTER_TYPE: FilterType; @@ -19,31 +14,119 @@ pub trait Filter { fn to_registers(&self) -> [u8; 8]; } -/// +pub type BitFilter = [u8; N]; + +/// A filter that matches against a single 11 bit id, the rtr bit, and the first two bytes of the +/// payload. /// /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. pub struct SingleStandardFilter { - pub id: BitSelector, - pub rtr: BitSelector, - pub data: [BitSelector; 2], + /// The register representation of the filter. + raw: [u8; 8], +} + +impl SingleStandardFilter { + /// Create a new filter that matches against the id, rtr and first two bytes of the payload. + /// + /// Example matching only even ids, allowing any rtr value and any payload data: + /// ``` + /// const FILTER: SingleStandardFilter = + /// SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + /// ``` + pub const fn new(id: &BitFilter<11>, rtr: &BitFilter<1>, payload: [&BitFilter<8>; 2]) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Convert the id filter into the code and mask bits. + { + let mut idx = 0; + while idx < 11 { + let shift = 31 - idx; + + match id[idx] { + b'0' => { + // Code bit is already zero, no need to set it. + acceptance_mask |= 1 << shift; + } + b'1' => { + acceptance_code |= 1 << shift; + acceptance_mask |= 1 << shift; + } + b'x' => {} + _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), + } + + idx += 1; + } + } + // Convert the rtr bit filter into the code and mask bits. + { + let shift = 20; + match rtr[0] { + b'0' => { + // Code bit is already zero, no need to set it. + acceptance_mask |= 1 << shift; + } + b'1' => { + acceptance_code |= 1 << shift; + acceptance_mask |= 1 << shift; + } + b'x' => {} + _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), + } + } + // Convert the payload byte filter into the code and mask bits. + { + let mut payload_index = 0; + while payload_index < 2 { + let mut idx = 0; + while idx < 8 { + let shift = 15 - (8 * payload_index) - idx; + + match payload[payload_index][idx] { + b'0' => { + // Code bit is already zero, no need to set it. + acceptance_mask |= 1 << shift; + } + b'1' => { + acceptance_code |= 1 << shift; + acceptance_mask |= 1 << shift; + } + b'x' => {} + _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), + } + + idx += 1; + } + + payload_index += 1; + } + } + + // Convert the filter code and mask into the full byte array needed for the registers. + let [code_3, code_2, code_1, code_0] = acceptance_code.to_be_bytes(); + + // At a register level, set bits in the mask mean we don't care about the value of that bit. Therefore, we invert the mask. + // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 + let [mask_3, mask_2, mask_1, mask_0] = (!acceptance_mask).to_be_bytes(); + + Self { + raw: [ + code_3, code_2, code_1, code_0, mask_3, mask_2, mask_1, mask_0, + ], + } + } } impl Filter for SingleStandardFilter { const FILTER_TYPE: FilterType = FilterType::Single; fn to_registers(&self) -> [u8; 8] { - [ - // Value. - self.id.bits[3..11].to_value(), - self.id.bits[0..3].to_value() << 5 | self.rtr.bits[0].to_value() << 4, - self.data[0].to_value(), - self.data[1].to_value(), - // Mask. - self.id.bits[3..11].to_mask(), - self.id.bits[0..3].to_mask() << 5 | self.rtr.bits[0].to_mask() << 4, - self.data[0].to_mask(), - self.data[1].to_mask(), - ] + self.raw } } /// @@ -51,8 +134,8 @@ impl Filter for SingleStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct SingleExtendedFilter { - pub id: BitSelector, - pub rtr: BitSelector, + // pub id: BitSelector, + // pub rtr: BitSelector, } impl Filter for SingleExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Single; @@ -67,12 +150,12 @@ impl Filter for SingleExtendedFilter { /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. pub struct DualStandardFilter { - pub first_id: BitSelector, - pub first_rtr: BitSelector, - pub first_data: BitSelector, + // pub first_id: BitSelector, + // pub first_rtr: BitSelector, + // pub first_data: BitSelector, - pub second_id: BitSelector, - pub second_rtr: BitSelector, + // pub second_id: BitSelector, + // pub second_rtr: BitSelector, } impl Filter for DualStandardFilter { const FILTER_TYPE: FilterType = FilterType::Dual; @@ -87,7 +170,7 @@ impl Filter for DualStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct DualExtendedFilter { - pub id: [BitSelector; 2], + // pub id: [BitSelector; 2], } impl Filter for DualExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Dual; diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 0ba5014b787..a6942091889 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -17,7 +17,6 @@ use fugit::HertzU32; use self::filter::{Filter, FilterType}; -pub mod bitselector; pub mod filter; /// Structure backing the embedded_hal::can::Frame trait. diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index b6606c8cde4..d959d923cde 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -66,28 +66,8 @@ fn main() -> ! { // relying on these partial acceptance filters to exactly match. // TODO: even though this is a single, standard id filter, extended ids will also match this filter. // A filter that matches standard ids of an even value. - let filter = twai::filter::SingleStandardFilter { - id: twai::bitselector::BitSelector { - bits: [ - twai::bitselector::Selector::Reset, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - twai::bitselector::Selector::Any, - ], - }, - rtr: twai::bitselector::BitSelector::new_any(), - data: [ - twai::bitselector::BitSelector::new_any(), - twai::bitselector::BitSelector::new_any(), - ], - }; + let filter = + 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 From f26d19a44b51b4abed576eccd7f758091be1096a Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sun, 13 Nov 2022 01:15:50 -0600 Subject: [PATCH 14/20] Implemented new() for twai filters. --- esp-hal-common/src/twai/filter.rs | 400 +++++++++++++++++++++++++----- 1 file changed, 339 insertions(+), 61 deletions(-) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 78d3a97879f..9218ac4705f 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,3 +1,8 @@ +#[cfg(feature = "eh1")] +use embedded_can::{ExtendedId, StandardId}; +#[cfg(not(feature = "eh1"))] +use embedded_hal::can::{ExtendedId, StandardId}; + #[derive(Debug, PartialEq, Eq)] pub enum FilterType { Single, @@ -16,6 +21,41 @@ pub trait Filter { pub type BitFilter = [u8; N]; +macro_rules! set_bit_from_byte { + ($code:expr, $mask:expr, $byte:expr, $shift:expr) => { + match $byte { + b'0' => { + // Code bit is already zero, no need to set it. + $mask |= 1 << $shift; + } + b'1' => { + $code |= 1 << $shift; + $mask |= 1 << $shift; + } + b'x' => {} + _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), + } + }; +} + +/// Convert a code and mask to the byte array needed at a register level. +/// +/// On the input mask, set bits (1) mean we care about the exact value of the +/// corresponding bit in the code, reset bits (0) mean the bit could be any value. +const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] { + // Convert the filter code and mask into the full byte array needed for the registers. + let [code_3, code_2, code_1, code_0] = code.to_be_bytes(); + + // At a register level, set bits in the mask mean we don't care about the value of + // that bit. Therefore, we invert the mask. + // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 + let [mask_3, mask_2, mask_1, mask_0] = (!mask).to_be_bytes(); + + [ + code_3, code_2, code_1, code_0, mask_3, mask_2, mask_1, mask_0, + ] +} + /// A filter that matches against a single 11 bit id, the rtr bit, and the first two bytes of the /// payload. /// @@ -27,7 +67,8 @@ pub struct SingleStandardFilter { } impl SingleStandardFilter { - /// Create a new filter that matches against the id, rtr and first two bytes of the payload. + /// Create a new filter that matches against a single 11-bit standard id. + /// The filter can match against the packet's id, rtr bit, and first two bytes of the payload. /// /// Example matching only even ids, allowing any rtr value and any payload data: /// ``` @@ -47,38 +88,14 @@ impl SingleStandardFilter { let mut idx = 0; while idx < 11 { let shift = 31 - idx; - - match id[idx] { - b'0' => { - // Code bit is already zero, no need to set it. - acceptance_mask |= 1 << shift; - } - b'1' => { - acceptance_code |= 1 << shift; - acceptance_mask |= 1 << shift; - } - b'x' => {} - _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), - } - + set_bit_from_byte!(acceptance_code, acceptance_mask, id[idx], shift); idx += 1; } } // Convert the rtr bit filter into the code and mask bits. { let shift = 20; - match rtr[0] { - b'0' => { - // Code bit is already zero, no need to set it. - acceptance_mask |= 1 << shift; - } - b'1' => { - acceptance_code |= 1 << shift; - acceptance_mask |= 1 << shift; - } - b'x' => {} - _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), - } + set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift); } // Convert the payload byte filter into the code and mask bits. { @@ -87,38 +104,71 @@ impl SingleStandardFilter { let mut idx = 0; while idx < 8 { let shift = 15 - (8 * payload_index) - idx; - - match payload[payload_index][idx] { - b'0' => { - // Code bit is already zero, no need to set it. - acceptance_mask |= 1 << shift; - } - b'1' => { - acceptance_code |= 1 << shift; - acceptance_mask |= 1 << shift; - } - b'x' => {} - _ => panic!("BitFilter bits must be either '1', '0' or 'x'."), - } - + set_bit_from_byte!( + acceptance_code, + acceptance_mask, + payload[payload_index][idx], + shift + ); idx += 1; } - payload_index += 1; } } - // Convert the filter code and mask into the full byte array needed for the registers. - let [code_3, code_2, code_1, code_0] = acceptance_code.to_be_bytes(); + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } + + /// + /// The masks indicate which bits of the code the filter should match against. Set bits + /// in the mask indicate that the corresponding bit in the code should match. + /// + /// + /// # Examples + /// + /// A filter that matches every standard id that is even, is not an rtr frame, with + /// any bytes for the first two payload bytes. + /// ``` + /// let filter = twai::filter::SingleStandardFilter::new_from_code_mask( + /// StandardId::new(0x000).unwrap(), + /// StandardId::new(0x001).unwrap(), + /// false, + /// true, + /// [0x00, 0x00], + /// [0x00, 0x00], + /// ); + /// ``` + pub fn new_from_code_mask( + id_code: StandardId, + id_mask: StandardId, + rtr_code: bool, + rtr_mask: bool, + payload_code: [u8; 2], + payload_mask: [u8; 2], + ) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Pack the id into the full layout. + acceptance_code |= (id_code.as_raw() as u32) << 21; + acceptance_mask |= (id_mask.as_raw() as u32) << 21; - // At a register level, set bits in the mask mean we don't care about the value of that bit. Therefore, we invert the mask. - // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 - let [mask_3, mask_2, mask_1, mask_0] = (!acceptance_mask).to_be_bytes(); + // Pack the rtr bit into the full layout. + acceptance_code |= (rtr_code as u32) << 20; + acceptance_mask |= (rtr_mask as u32) << 20; + + // Pack the payload bytes into the full layout. + acceptance_code |= (payload_code[0] as u32) << 8 | (payload_code[1] as u32); + acceptance_mask |= (payload_mask[0] as u32) << 8 | (payload_mask[1] as u32); Self { - raw: [ - code_3, code_2, code_1, code_0, mask_3, mask_2, mask_1, mask_0, - ], + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), } } } @@ -134,13 +184,73 @@ impl Filter for SingleStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct SingleExtendedFilter { - // pub id: BitSelector, - // pub rtr: BitSelector, + raw: [u8; 8], } +impl SingleExtendedFilter { + /// Create a filter that matches against a single 29-bit extended id. + /// + /// The filter can match against the packet's id and the rtr bit. + pub const fn new(id: BitFilter<29>, rtr: BitFilter<1>) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Convert the id filter into the code and mask bits. + { + let mut idx = 0; + while idx < 29 { + let shift = 31 - idx; + set_bit_from_byte!(acceptance_code, acceptance_mask, id[idx], shift); + idx += 1; + } + } + // Convert the rtr bit filter into the code and mask bits. + { + let shift = 2; + set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift); + } + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } + /// + /// The masks indicate which bits of the code the filter should match against. Set bits + /// in the mask indicate that the corresponding bit in the code should match. + pub fn new_from_code_mask( + id_code: ExtendedId, + id_mask: ExtendedId, + rtr_code: bool, + rtr_mask: bool, + ) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Pack the id into the full layout. + acceptance_code |= (id_code.as_raw() as u32) << 3; + acceptance_mask |= (id_mask.as_raw() as u32) << 3; + + // Pack the rtr bit into the full layout. + acceptance_code |= (rtr_code as u32) << 2; + acceptance_mask |= (rtr_mask as u32) << 2; + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } +} + impl Filter for SingleExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Single; fn to_registers(&self) -> [u8; 8] { - todo!(); + self.raw } } @@ -150,17 +260,128 @@ impl Filter for SingleExtendedFilter { /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. pub struct DualStandardFilter { - // pub first_id: BitSelector, - // pub first_rtr: BitSelector, - // pub first_data: BitSelector, + raw: [u8; 8], +} +impl DualStandardFilter { + /// Create a filter that matches against two standard 11-bit standard ids. + /// + /// The first filter part can match a packet's id, rtr bit, and the first byte of the payload. + /// The second filter part can match a packet's id and rtr bit. + pub const fn new( + first_id: BitFilter<11>, + first_rtr: BitFilter<1>, + first_payload: BitFilter<8>, + second_id: BitFilter<11>, + second_rtr: BitFilter<1>, + ) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Convert the first id filter into the code and mask bits. + { + let mut idx = 0; + while idx < 11 { + let shift = 31 - idx; + set_bit_from_byte!(acceptance_code, acceptance_mask, first_id[idx], shift); + idx += 1; + } + } + // Convert the first rtr bit filter into the code and mask bits. + { + let shift = 20; + set_bit_from_byte!(acceptance_code, acceptance_mask, first_rtr[0], shift); + } + // Convert the first payload byte filter into the code and mask bits. + { + let mut idx = 0; + while idx < 4 { + let shift = 19 - idx; + set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift); + idx += 1; + } + while idx < 8 { + let shift = 3 + 4 - idx; + set_bit_from_byte!(acceptance_code, acceptance_mask, first_payload[idx], shift); + idx += 1; + } + } + // Convert the second id filter into the code and mask bits. + { + let mut idx = 0; + while idx < 11 { + let shift = 15 - idx; + set_bit_from_byte!(acceptance_code, acceptance_mask, second_id[idx], shift); + idx += 1; + } + } + // Convert the second rtr bit filter into the code and mask bits. + { + let shift = 4; + set_bit_from_byte!(acceptance_code, acceptance_mask, second_rtr[0], shift); + } + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } + /// + /// The masks indicate which bits of the code the filter should match against. Set bits + /// in the mask indicate that the corresponding bit in the code should match. + pub fn new_from_code_mask( + first_id_code: StandardId, + first_id_mask: StandardId, + first_rtr_code: bool, + first_rtr_mask: bool, + first_payload_code: u8, + first_payload_mask: u8, + second_id_code: StandardId, + second_id_mask: StandardId, + second_rtr_code: bool, + second_rtr_mask: bool, + ) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Pack the first id into the full layout. + acceptance_code |= (first_id_code.as_raw() as u32) << 21; + acceptance_mask |= (first_id_mask.as_raw() as u32) << 21; + + // Pack the rtr bit into the full layout. + acceptance_code |= (first_rtr_code as u32) << 20; + acceptance_mask |= (first_rtr_mask as u32) << 20; - // pub second_id: BitSelector, - // pub second_rtr: BitSelector, + // Pack the first payload into the full layout. + acceptance_code |= ((first_payload_code & 0xF0) as u32) << 12; + acceptance_mask |= ((first_payload_mask & 0xF0) as u32) << 12; + acceptance_code |= (first_payload_code & 0x0F) as u32; + acceptance_mask |= (first_payload_mask & 0x0F) as u32; + + // Pack the second id into the full layout. + acceptance_code |= (second_id_code.as_raw() as u32) << 5; + acceptance_mask |= (second_id_mask.as_raw() as u32) << 5; + + // Pack the second rtr bit into the full layout. + acceptance_code |= (second_rtr_code as u32) << 4; + acceptance_mask |= (second_rtr_mask as u32) << 4; + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } } + impl Filter for DualStandardFilter { const FILTER_TYPE: FilterType = FilterType::Dual; fn to_registers(&self) -> [u8; 8] { - todo!(); + self.raw } } /// @@ -170,11 +391,68 @@ impl Filter for DualStandardFilter { /// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter /// will also be accepted. pub struct DualExtendedFilter { - // pub id: [BitSelector; 2], + raw: [u8; 8], +} +impl DualExtendedFilter { + /// Create a filter that matches the first 16 bits of two 29-bit extended ids. + pub const fn new(ids: [BitFilter<16>; 2]) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Convert the id filters into the code and mask bits. + { + let mut filter_idx = 0; + while filter_idx < 2 { + let mut idx = 0; + while idx < 16 { + let shift = 31 - (filter_idx * 16) - idx; + set_bit_from_byte!( + acceptance_code, + acceptance_mask, + ids[filter_idx][idx], + shift + ); + idx += 1; + } + filter_idx += 1; + } + } + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } + /// + /// The masks indicate which bits of the code the filter should match against. Set bits + /// in the mask indicate that the corresponding bit in the code should match. + pub fn new_from_code_mask(ids_code: [u16; 2], ids_mask: [u16; 2]) -> Self { + // The bit values we desire to match against. This determines whether we want a set + // bit (1) or a reset bit (0). + let mut acceptance_code: u32 = 0; + // The acceptance mask, set bits (1) mean we care about the exact value of the + // corresponding bit in the code, reset bits (0) mean the bit could be any value. + let mut acceptance_mask: u32 = 0; + + // Pack the first partial id into the full layout. + acceptance_code |= (ids_code[0] as u32) << 16; + acceptance_mask |= (ids_mask[0] as u32) << 16; + + // Pack the second partial id into the full layout. + acceptance_code |= ids_code[1] as u32; + acceptance_mask |= ids_mask[1] as u32; + + Self { + raw: code_mask_to_register_array(acceptance_code, acceptance_mask), + } + } } impl Filter for DualExtendedFilter { const FILTER_TYPE: FilterType = FilterType::Dual; fn to_registers(&self) -> [u8; 8] { - todo!(); + self.raw } } From 3553a0c1c28d337d357302e39470f72e53b43aa6 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Thu, 17 Nov 2022 17:09:53 -0600 Subject: [PATCH 15/20] Clean up TWAI docs and example. --- esp-hal-common/src/twai/filter.rs | 13 +++++++++-- esp-hal-common/src/twai/mod.rs | 36 +++++++++++++++++++++---------- esp32c3-hal/examples/twai.rs | 9 ++++---- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 9218ac4705f..68172ce0d5c 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,3 +1,7 @@ +//! # Two-wire Automotive Interface (TWAI) Filters +//! +//! These are acceptance filters that limit which packets are received by the TWAI peripheral. + #[cfg(feature = "eh1")] use embedded_can::{ExtendedId, StandardId}; #[cfg(not(feature = "eh1"))] @@ -10,17 +14,19 @@ pub enum FilterType { } pub trait Filter { - // The type of the filter. + /// The type of the filter. const FILTER_TYPE: FilterType; fn filter_type(&self) -> FilterType { Self::FILTER_TYPE } + /// Get the register level representation of the filter. fn to_registers(&self) -> [u8; 8]; } pub type BitFilter = [u8; N]; +/// Internal macro used to convert a byte from a bytestring into a bit inside a given code and mask. macro_rules! set_bit_from_byte { ($code:expr, $mask:expr, $byte:expr, $shift:expr) => { match $byte { @@ -254,8 +260,10 @@ impl Filter for SingleExtendedFilter { } } +/// A filter that matches against two standard 11-bit standard ids. /// -/// TODO: is this how we actually want to store the two filters? +/// The first filter part can match a packet's id, rtr bit, and the first byte of the payload. +/// The second filter part can match a packet's id and rtr bit. /// /// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter /// will also be accepted. @@ -426,6 +434,7 @@ impl DualExtendedFilter { raw: code_mask_to_register_array(acceptance_code, acceptance_mask), } } + /// Create a new filter matching the first 16 bits of two 29-bit ids. /// /// The masks indicate which bits of the code the filter should match against. Set bits /// in the mask indicate that the corresponding bit in the code should match. diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index a6942091889..6d30103f78e 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -1,3 +1,8 @@ +//! # Two-wire Automotive Interface (TWAI) +//! +//! This driver manages the ISO 11898-1 (CAN Specification 2.0) compatible TWAI controllers. It +//! supports Standard Frame Format (11-bit) and Extended Frame Format (29-bit) frame identifiers. + use core::slice::{from_raw_parts, from_raw_parts_mut}; use crate::{ @@ -19,7 +24,7 @@ use self::filter::{Filter, FilterType}; pub mod filter; -/// Structure backing the embedded_hal::can::Frame trait. +/// Structure backing the embedded_hal::can::Frame/embedded_can::Frame trait. #[derive(Debug)] pub struct EspTwaiFrame { id: Id, @@ -118,6 +123,7 @@ pub struct TimingConfig { } /// A selection of pre-determined baudrates for the TWAI driver. +/// Currently these timings are sourced from the ESP IDF C driver which assumes an APB clock of 80MHz. pub enum BaudRate { B125K, B250K, @@ -136,7 +142,7 @@ impl BaudRate { // #define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} // #define TWAI_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} // #define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - fn timing(self) -> TimingConfig { + const fn timing(self) -> TimingConfig { match self { Self::B125K => TimingConfig { baud_rate_prescaler: 32, @@ -247,7 +253,11 @@ where }); } - /// Set up the acceptance filter on the device to accept the specified filters. + /// Set up the acceptance filter on the device. + /// + /// NOTE: On a bus with mixed 11-bit and 29-bit packet id's, you may experience an + /// 11-bit filter match against a 29-bit frame and vice versa. Your application should check + /// the id again once a frame has been received to make sure it is the expected value. /// /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6) pub fn set_filter(&mut self, filter: impl Filter) { @@ -267,11 +277,11 @@ where } } - /// Set the Error warning threshold. + /// Set the error warning threshold. /// - /// In the case when any of an error counter value exceeds - /// the threshold, or all the error counter values are below the threshold, an error warning - /// interrupt will be triggered (given the enable signal is valid). + /// In the case when any of an error counter value exceeds the threshold, or all the + /// error counter values are below the threshold, an error 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() @@ -296,8 +306,8 @@ where /// An active TWAI peripheral in Normal Mode. /// -/// According to the [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) 29.4.1.2 Operation Mode, in "Normal Mode": -/// The TWAI controller can transmit and receive messages including error signals (such as error and overload Frames) +/// In this mode, the TWAI controller can transmit and receive messages including error +/// signals (such as error and overload frames). pub struct Twai { peripheral: T, } @@ -347,7 +357,7 @@ where /// Get the number of messages that the peripheral has available in the receive FIFO. /// - /// Note that this may not be the number of messages in the receive FIFO due to + /// 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 @@ -435,7 +445,7 @@ where /// [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. + /// 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(); @@ -525,8 +535,11 @@ where .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) } + /// Return a received frame if there are any available. fn receive(&mut self) -> nb::Result { let status = self.peripheral.register_block().status.read(); @@ -655,6 +668,7 @@ pub trait Instance { fn register_block(&self) -> &RegisterBlock; } +#[cfg(any(esp32c3))] impl Instance for crate::pac::TWAI { #[inline(always)] fn register_block(&self) -> &RegisterBlock { diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index d959d923cde..41d590c453b 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -47,7 +47,7 @@ fn main() -> ! { let can_rx_pin = io.pins.gpio3; // The speed of the CAN bus. - let can_baudrate = twai::BaudRate::B1000K; + 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. @@ -57,18 +57,17 @@ fn main() -> ! { can_rx_pin, &mut system.peripheral_clock_control, &clocks, - can_baudrate, + 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. - // TODO: even though this is a single, standard id filter, extended ids will also match this filter. // A filter that matches standard ids of an even value. - let filter = + const FILTER: twai::filter::SingleStandardFilter = twai::filter::SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); - can_config.set_filter(filter); + 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. From fc7a4282ca887d0d49526b3f57ad71d41a9c04c6 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Thu, 17 Nov 2022 18:27:39 -0600 Subject: [PATCH 16/20] Fix filter constructors and add examples. --- esp-hal-common/src/twai/filter.rs | 44 ++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 68172ce0d5c..5291bc121a8 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -196,7 +196,14 @@ impl SingleExtendedFilter { /// Create a filter that matches against a single 29-bit extended id. /// /// The filter can match against the packet's id and the rtr bit. - pub const fn new(id: BitFilter<29>, rtr: BitFilter<1>) -> Self { + /// + /// # Examples + /// A filter matching any odd extended ids, with any rtr value. + /// ``` + /// const FILTER: twai::filter::SingleExtendedFilter = + /// twai::filter::SingleExtendedFilter::new(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1", b"x"); + /// ``` + pub const fn new(id: &BitFilter<29>, rtr: &BitFilter<1>) -> Self { // The bit values we desire to match against. This determines whether we want a set // bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; @@ -275,12 +282,25 @@ impl DualStandardFilter { /// /// The first filter part can match a packet's id, rtr bit, and the first byte of the payload. /// The second filter part can match a packet's id and rtr bit. + /// + /// # Examples + /// A filter that matches any standard id that ends with a 00 or a 11, with any rtr, and with any + /// payload on the first filter. + /// ``` + /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new( + /// b"xxxxxxxxx00", + /// b"x", + /// b"xxxxxxxx", + /// b"xxxxxxxxx11", + /// b"x", + /// ); + /// ``` pub const fn new( - first_id: BitFilter<11>, - first_rtr: BitFilter<1>, - first_payload: BitFilter<8>, - second_id: BitFilter<11>, - second_rtr: BitFilter<1>, + first_id: &BitFilter<11>, + first_rtr: &BitFilter<1>, + first_payload: &BitFilter<8>, + second_id: &BitFilter<11>, + second_rtr: &BitFilter<1>, ) -> Self { // The bit values we desire to match against. This determines whether we want a set // bit (1) or a reset bit (0). @@ -403,7 +423,17 @@ pub struct DualExtendedFilter { } impl DualExtendedFilter { /// Create a filter that matches the first 16 bits of two 29-bit extended ids. - pub const fn new(ids: [BitFilter<16>; 2]) -> Self { + /// + /// # Examples + /// A filter that matches ids with 4 bits either set or reset in the higher part of the id. For example this id matches: + /// 0x000f000f, 0x000f000a, 0x0000000a, 0x0000000b. + /// But it does not match: + /// 0x000a000a + /// ``` + /// const FILTER: twai::filter::DualExtendedFilter = + /// twai::filter::DualExtendedFilter::new([b"xxxxxxxxx0000xxx", b"xxxxxxxxx1111xxx"]); + /// ``` + pub const fn new(ids: [&BitFilter<16>; 2]) -> Self { // The bit values we desire to match against. This determines whether we want a set // bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; From 54b5e5dc8654df3f9afc2746ec78cfaeced4585c Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 17 Dec 2022 14:33:36 -0600 Subject: [PATCH 17/20] pre driver PeripheralRef update. --- esp-hal-common/src/twai/mod.rs | 4 ++-- esp32c3-hal/examples/twai.rs | 23 +++++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 6d30103f78e..93ac93ee97a 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -7,7 +7,7 @@ use core::slice::{from_raw_parts, from_raw_parts_mut}; use crate::{ clock::Clocks, - pac::twai::RegisterBlock, + peripherals::twai::RegisterBlock, system::PeripheralClockControl, types::{InputSignal, OutputSignal}, InputPin, OutputPin, @@ -669,7 +669,7 @@ pub trait Instance { } #[cfg(any(esp32c3))] -impl Instance for crate::pac::TWAI { +impl Instance for crate::peripherals::TWAI { #[inline(always)] fn register_block(&self) -> &RegisterBlock { self diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 41d590c453b..a7a20386500 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -1,11 +1,11 @@ #![no_std] #![no_main] -use core::fmt::Write; use esp32c3_hal::{ - clock::ClockControl, gpio::IO, pac::Peripherals, prelude::*, timer::TimerGroup, twai, Rtc, - UsbSerialJtag, + clock::ClockControl, gpio::IO, peripherals::Peripherals, prelude::*, timer::TimerGroup, twai, + Rtc, }; +use esp_println::println; // 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. @@ -24,7 +24,7 @@ use riscv_rt::entry; #[entry] fn main() -> ! { - let peripherals = Peripherals::take().unwrap(); + let peripherals = Peripherals::take(); let mut system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); @@ -77,29 +77,24 @@ fn main() -> ! { // Wait for a frame to be received. let frame = block!(can.receive()).unwrap(); - writeln!(UsbSerialJtag, "Received a frame:").unwrap(); + println!("Received a frame:"); // Print different messages based on the frame id type. match frame.id() { Id::Standard(id) => { - writeln!(UsbSerialJtag, "\tStandard Id: {:?}", id).unwrap(); + println!("\tStandard Id: {:?}", id); } Id::Extended(id) => { - writeln!(UsbSerialJtag, "\tExtended Id: {:?}", id).unwrap(); + 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() { - writeln!(UsbSerialJtag, "\tData: {:?}", frame.data()).unwrap(); + println!("\tData: {:?}", frame.data()); } else { - writeln!( - UsbSerialJtag, - "\tRemote Frame. Data Length Code: {}", - frame.dlc() - ) - .unwrap(); + println!("\tRemote Frame. Data Length Code: {}", frame.dlc()); } // Transmit the frame back. From 2b6778d182c7b9e67efc686d966d244fee281e45 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 17 Dec 2022 15:00:12 -0600 Subject: [PATCH 18/20] PeripheralRef/twai --- esp-hal-common/src/peripherals/esp32c3.rs | 1 + esp-hal-common/src/twai/mod.rs | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/esp-hal-common/src/peripherals/esp32c3.rs b/esp-hal-common/src/peripherals/esp32c3.rs index 2c6e955bd40..d0f2459c249 100644 --- a/esp-hal-common/src/peripherals/esp32c3.rs +++ b/esp-hal-common/src/peripherals/esp32c3.rs @@ -67,5 +67,6 @@ mod peripherals { TIMG0, TIMG1, APB_SARADC, + TWAI, } } diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 93ac93ee97a..dcc928ebbec 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -7,6 +7,7 @@ use core::slice::{from_raw_parts, from_raw_parts_mut}; use crate::{ clock::Clocks, + peripheral::{Peripheral, PeripheralRef}, peripherals::twai::RegisterBlock, system::PeripheralClockControl, types::{InputSignal, OutputSignal}, @@ -178,16 +179,16 @@ impl BaudRate { } /// An inactive TWAI peripheral in the "Reset"/configuration state. -pub struct TwaiConfiguration { - peripheral: T, +pub struct TwaiConfiguration<'d, T> { + peripheral: PeripheralRef<'d, T>, } -impl TwaiConfiguration +impl<'d, T> TwaiConfiguration<'d, T> where T: Instance, { pub fn new( - peripheral: T, + peripheral: impl Peripheral

+ 'd, mut tx_pin: TX, mut rx_pin: RX, clock_control: &mut PeripheralClockControl, @@ -201,6 +202,7 @@ where tx_pin.connect_peripheral_to_output(OutputSignal::TWAI_TX); rx_pin.connect_input_to_peripheral(InputSignal::TWAI_RX); + crate::into_ref!(peripheral); let mut cfg = TwaiConfiguration { peripheral }; cfg.set_baud_rate(baud_rate, clocks); @@ -291,7 +293,7 @@ where /// Put the peripheral into Operation Mode, allowing the transmission and reception of /// packets using the new object. - pub fn start(self) -> Twai { + pub fn start(self) -> Twai<'d, T> { // Put the peripheral into operation mode by clearing the reset mode bit. self.peripheral .register_block() @@ -308,16 +310,16 @@ where /// /// In this mode, the TWAI controller can transmit and receive messages including error /// signals (such as error and overload frames). -pub struct Twai { - peripheral: T, +pub struct Twai<'d, T> { + peripheral: PeripheralRef<'d, T>, } -impl Twai +impl<'d, T> Twai<'d, T> where T: Instance, { /// Stop the peripheral, putting it into reset mode and enabling reconfiguration. - pub fn stop(self) -> TwaiConfiguration { + pub fn stop(self) -> TwaiConfiguration<'d, T> { // Put the peripheral into reset/configuration mode by setting the reset mode bit. self.peripheral .register_block() @@ -430,7 +432,7 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl Can for Twai +impl Can for Twai<'_, T> where T: Instance, { From a8e875e8617ed02b2bbeda97654a4befb55ed52b Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Sat, 17 Dec 2022 16:01:50 -0600 Subject: [PATCH 19/20] Format comments with nightly rustfmt. --- esp-hal-common/src/twai/filter.rs | 159 ++++++++++++++++-------------- esp-hal-common/src/twai/mod.rs | 155 +++++++++++++++++------------ esp32c3-hal/examples/twai.rs | 45 +++++---- 3 files changed, 202 insertions(+), 157 deletions(-) diff --git a/esp-hal-common/src/twai/filter.rs b/esp-hal-common/src/twai/filter.rs index 5291bc121a8..f2636e320c3 100644 --- a/esp-hal-common/src/twai/filter.rs +++ b/esp-hal-common/src/twai/filter.rs @@ -1,6 +1,7 @@ //! # Two-wire Automotive Interface (TWAI) Filters //! -//! These are acceptance filters that limit which packets are received by the TWAI peripheral. +//! These are acceptance filters that limit which packets are received by the +//! TWAI peripheral. #[cfg(feature = "eh1")] use embedded_can::{ExtendedId, StandardId}; @@ -26,7 +27,8 @@ pub trait Filter { pub type BitFilter = [u8; N]; -/// Internal macro used to convert a byte from a bytestring into a bit inside a given code and mask. +/// Internal macro used to convert a byte from a bytestring into a bit inside a +/// given code and mask. macro_rules! set_bit_from_byte { ($code:expr, $mask:expr, $byte:expr, $shift:expr) => { match $byte { @@ -47,13 +49,15 @@ macro_rules! set_bit_from_byte { /// Convert a code and mask to the byte array needed at a register level. /// /// On the input mask, set bits (1) mean we care about the exact value of the -/// corresponding bit in the code, reset bits (0) mean the bit could be any value. +/// corresponding bit in the code, reset bits (0) mean the bit could be any +/// value. const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] { - // Convert the filter code and mask into the full byte array needed for the registers. + // Convert the filter code and mask into the full byte array needed for the + // registers. let [code_3, code_2, code_1, code_0] = code.to_be_bytes(); - // At a register level, set bits in the mask mean we don't care about the value of - // that bit. Therefore, we invert the mask. + // At a register level, set bits in the mask mean we don't care about the value + // of that bit. Therefore, we invert the mask. // https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6 let [mask_3, mask_2, mask_1, mask_0] = (!mask).to_be_bytes(); @@ -62,11 +66,11 @@ const fn code_mask_to_register_array(code: u32, mask: u32) -> [u8; 8] { ] } -/// A filter that matches against a single 11 bit id, the rtr bit, and the first two bytes of the -/// payload. +/// A filter that matches against a single 11 bit id, the rtr bit, and the first +/// two bytes of the payload. /// -/// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter -/// will also be accepted. +/// Warning: This is not a perfect filter. Extended ids that match the bit +/// layout of this filter will also be accepted. pub struct SingleStandardFilter { /// The register representation of the filter. raw: [u8; 8], @@ -74,19 +78,22 @@ pub struct SingleStandardFilter { impl SingleStandardFilter { /// Create a new filter that matches against a single 11-bit standard id. - /// The filter can match against the packet's id, rtr bit, and first two bytes of the payload. + /// The filter can match against the packet's id, rtr bit, and first two + /// bytes of the payload. /// - /// Example matching only even ids, allowing any rtr value and any payload data: + /// Example matching only even ids, allowing any rtr value and any payload + /// data: /// ``` /// const FILTER: SingleStandardFilter = /// SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); /// ``` pub const fn new(id: &BitFilter<11>, rtr: &BitFilter<1>, payload: [&BitFilter<8>; 2]) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Convert the id filter into the code and mask bits. @@ -127,15 +134,15 @@ impl SingleStandardFilter { } } - /// - /// The masks indicate which bits of the code the filter should match against. Set bits - /// in the mask indicate that the corresponding bit in the code should match. + /// The masks indicate which bits of the code the filter should match + /// against. Set bits in the mask indicate that the corresponding bit in + /// the code should match. /// /// /// # Examples /// - /// A filter that matches every standard id that is even, is not an rtr frame, with - /// any bytes for the first two payload bytes. + /// A filter that matches every standard id that is even, is not an rtr + /// frame, with any bytes for the first two payload bytes. /// ``` /// let filter = twai::filter::SingleStandardFilter::new_from_code_mask( /// StandardId::new(0x000).unwrap(), @@ -154,11 +161,12 @@ impl SingleStandardFilter { payload_code: [u8; 2], payload_mask: [u8; 2], ) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Pack the id into the full layout. @@ -185,10 +193,8 @@ impl Filter for SingleStandardFilter { self.raw } } -/// -/// -/// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter -/// will also be accepted. +/// Warning: This is not a perfect filter. Standard ids that match the bit +/// layout of this filter will also be accepted. pub struct SingleExtendedFilter { raw: [u8; 8], } @@ -204,11 +210,12 @@ impl SingleExtendedFilter { /// twai::filter::SingleExtendedFilter::new(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1", b"x"); /// ``` pub const fn new(id: &BitFilter<29>, rtr: &BitFilter<1>) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Convert the id filter into the code and mask bits. @@ -230,20 +237,21 @@ impl SingleExtendedFilter { raw: code_mask_to_register_array(acceptance_code, acceptance_mask), } } - /// - /// The masks indicate which bits of the code the filter should match against. Set bits - /// in the mask indicate that the corresponding bit in the code should match. + /// The masks indicate which bits of the code the filter should match + /// against. Set bits in the mask indicate that the corresponding bit in + /// the code should match. pub fn new_from_code_mask( id_code: ExtendedId, id_mask: ExtendedId, rtr_code: bool, rtr_mask: bool, ) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Pack the id into the full layout. @@ -269,25 +277,26 @@ impl Filter for SingleExtendedFilter { /// A filter that matches against two standard 11-bit standard ids. /// -/// The first filter part can match a packet's id, rtr bit, and the first byte of the payload. -/// The second filter part can match a packet's id and rtr bit. +/// The first filter part can match a packet's id, rtr bit, and the first byte +/// of the payload. The second filter part can match a packet's id and rtr bit. /// -/// Warning: This is not a perfect filter. Extended ids that match the bit layout of this filter -/// will also be accepted. +/// Warning: This is not a perfect filter. Extended ids that match the bit +/// layout of this filter will also be accepted. pub struct DualStandardFilter { raw: [u8; 8], } impl DualStandardFilter { /// Create a filter that matches against two standard 11-bit standard ids. /// - /// The first filter part can match a packet's id, rtr bit, and the first byte of the payload. - /// The second filter part can match a packet's id and rtr bit. + /// The first filter part can match a packet's id, rtr bit, and the first + /// byte of the payload. The second filter part can match a packet's id + /// and rtr bit. /// /// # Examples - /// A filter that matches any standard id that ends with a 00 or a 11, with any rtr, and with any - /// payload on the first filter. + /// A filter that matches any standard id that ends with a 00 or a 11, with + /// any rtr, and with any payload on the first filter. /// ``` - /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new( + /// const FILTER: twai::filter::DualStandardFilter = twai::filter::DualStandardFilter::new( /// b"xxxxxxxxx00", /// b"x", /// b"xxxxxxxx", @@ -302,11 +311,12 @@ impl DualStandardFilter { second_id: &BitFilter<11>, second_rtr: &BitFilter<1>, ) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Convert the first id filter into the code and mask bits. @@ -356,9 +366,9 @@ impl DualStandardFilter { raw: code_mask_to_register_array(acceptance_code, acceptance_mask), } } - /// - /// The masks indicate which bits of the code the filter should match against. Set bits - /// in the mask indicate that the corresponding bit in the code should match. + /// The masks indicate which bits of the code the filter should match + /// against. Set bits in the mask indicate that the corresponding bit in + /// the code should match. pub fn new_from_code_mask( first_id_code: StandardId, first_id_mask: StandardId, @@ -371,11 +381,12 @@ impl DualStandardFilter { second_rtr_code: bool, second_rtr_mask: bool, ) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Pack the first id into the full layout. @@ -412,33 +423,35 @@ impl Filter for DualStandardFilter { self.raw } } -/// -/// NOTE: The dual extended id acceptance filters can only match "the first 16 bits of the 29-bit ID". +/// NOTE: The dual extended id acceptance filters can only match "the first 16 +/// bits of the 29-bit ID". /// /// -/// Warning: This is not a perfect filter. Standard ids that match the bit layout of this filter -/// will also be accepted. +/// Warning: This is not a perfect filter. Standard ids that match the bit +/// layout of this filter will also be accepted. pub struct DualExtendedFilter { raw: [u8; 8], } impl DualExtendedFilter { - /// Create a filter that matches the first 16 bits of two 29-bit extended ids. + /// Create a filter that matches the first 16 bits of two 29-bit extended + /// ids. /// /// # Examples - /// A filter that matches ids with 4 bits either set or reset in the higher part of the id. For example this id matches: - /// 0x000f000f, 0x000f000a, 0x0000000a, 0x0000000b. - /// But it does not match: - /// 0x000a000a + /// A filter that matches ids with 4 bits either set or reset in the higher + /// part of the id. For example this id matches: 0x000f000f, 0x000f000a, + /// 0x0000000a, 0x0000000b. + /// But it does not match: 0x000a000a /// ``` /// const FILTER: twai::filter::DualExtendedFilter = - /// twai::filter::DualExtendedFilter::new([b"xxxxxxxxx0000xxx", b"xxxxxxxxx1111xxx"]); + /// twai::filter::DualExtendedFilter::new([b"xxxxxxxxx0000xxx", b"xxxxxxxxx1111xxx"]); /// ``` pub const fn new(ids: [&BitFilter<16>; 2]) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Convert the id filters into the code and mask bits. @@ -466,14 +479,16 @@ impl DualExtendedFilter { } /// Create a new filter matching the first 16 bits of two 29-bit ids. /// - /// The masks indicate which bits of the code the filter should match against. Set bits - /// in the mask indicate that the corresponding bit in the code should match. + /// The masks indicate which bits of the code the filter should match + /// against. Set bits in the mask indicate that the corresponding bit in + /// the code should match. pub fn new_from_code_mask(ids_code: [u16; 2], ids_mask: [u16; 2]) -> Self { - // The bit values we desire to match against. This determines whether we want a set - // bit (1) or a reset bit (0). + // The bit values we desire to match against. This determines whether we want a + // set bit (1) or a reset bit (0). let mut acceptance_code: u32 = 0; // The acceptance mask, set bits (1) mean we care about the exact value of the - // corresponding bit in the code, reset bits (0) mean the bit could be any value. + // corresponding bit in the code, reset bits (0) mean the bit could be any + // value. let mut acceptance_mask: u32 = 0; // Pack the first partial id into the full layout. diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index dcc928ebbec..a10214458b0 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -1,27 +1,27 @@ //! # Two-wire Automotive Interface (TWAI) //! -//! This driver manages the ISO 11898-1 (CAN Specification 2.0) compatible TWAI controllers. It -//! supports Standard Frame Format (11-bit) and Extended Frame Format (29-bit) frame identifiers. +//! This driver manages the ISO 11898-1 (CAN Specification 2.0) compatible TWAI +//! controllers. It supports Standard Frame Format (11-bit) and Extended Frame +//! Format (29-bit) frame identifiers. use core::slice::{from_raw_parts, from_raw_parts_mut}; -use crate::{ - clock::Clocks, - peripheral::{Peripheral, PeripheralRef}, - peripherals::twai::RegisterBlock, - system::PeripheralClockControl, - types::{InputSignal, OutputSignal}, - InputPin, OutputPin, -}; - #[cfg(feature = "eh1")] use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; #[cfg(not(feature = "eh1"))] use embedded_hal::can::{Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; - use fugit::HertzU32; use self::filter::{Filter, FilterType}; +use crate::{ + clock::Clocks, + peripheral::{Peripheral, PeripheralRef}, + peripherals::twai::RegisterBlock, + system::PeripheralClockControl, + types::{InputSignal, OutputSignal}, + InputPin, + OutputPin, +}; pub mod filter; @@ -35,7 +35,8 @@ pub struct EspTwaiFrame { } impl EspTwaiFrame { - /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG registers. + /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG + /// registers. fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { let mut d: [u8; 8] = [0; 8]; @@ -124,7 +125,8 @@ pub struct TimingConfig { } /// A selection of pre-determined baudrates for the TWAI driver. -/// Currently these timings are sourced from the ESP IDF C driver which assumes an APB clock of 80MHz. +/// Currently these timings are sourced from the ESP IDF C driver which assumes +/// an APB clock of 80MHz. pub enum BaudRate { B125K, B250K, @@ -135,14 +137,21 @@ pub enum BaudRate { impl BaudRate { /// Convert the BaudRate into the timings that the peripheral needs. // These timings are copied from the ESP IDF C driver. - // #define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = + // 8, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = + // 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = + // 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = + // 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = + // 4, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, + // .sjw = 3, .triple_sampling = false} #define TWAI_TIMING_CONFIG_800KBITS() + // {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} + // #define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, + // .sjw = 3, .triple_sampling = false} const fn timing(self) -> TimingConfig { match self { Self::B125K => TimingConfig { @@ -218,10 +227,10 @@ where // Included timings are all for 80MHz so assert that we are running at 80MHz. assert!(clocks.apb_clock == HertzU32::MHz(80)); - // Unpack the baud rate timings and convert them to the values needed for the register. - // Many of the registers have a minimum value of 1 which is represented by having zero - // bits set, therefore many values need to have 1 subtracted from them before being - // stored into the register. + // Unpack the baud rate timings and convert them to the values needed for the + // register. Many of the registers have a minimum value of 1 which is + // represented by having zero bits set, therefore many values need to + // have 1 subtracted from them before being stored into the register. let timing = baud_rate.timing(); let prescale = (timing.baud_rate_prescaler / 2) - 1; @@ -257,9 +266,10 @@ where /// Set up the acceptance filter on the device. /// - /// NOTE: On a bus with mixed 11-bit and 29-bit packet id's, you may experience an - /// 11-bit filter match against a 29-bit frame and vice versa. Your application should check - /// the id again once a frame has been received to make sure it is the expected value. + /// NOTE: On a bus with mixed 11-bit and 29-bit packet id's, you may + /// experience an 11-bit filter match against a 29-bit frame and vice + /// versa. Your application should check the id again once a frame has + /// been received to make sure it is the expected value. /// /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6) pub fn set_filter(&mut self, filter: impl Filter) { @@ -270,7 +280,8 @@ where .mode .modify(|_, w| w.rx_filter_mode().bit(filter_mode_bit)); - // Convert the filter into values for the registers and store them to the registers. + // Convert the filter into values for the registers and store them to the + // registers. let registers = filter.to_registers(); // Copy the filter to the peripheral. @@ -281,9 +292,10 @@ where /// Set the error warning threshold. /// - /// In the case when any of an error counter value exceeds the threshold, or all the - /// error counter values are below the threshold, an error warning interrupt will be - /// triggered (given the enable signal is valid). + /// In the case when any of an error counter value exceeds the threshold, or + /// all the error counter values are below the threshold, an error + /// 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() @@ -291,8 +303,8 @@ where .write(|w| w.err_warning_limit().variant(limit)); } - /// Put the peripheral into Operation Mode, allowing the transmission and reception of - /// packets using the new object. + /// Put the peripheral into Operation Mode, allowing the transmission and + /// 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 @@ -308,8 +320,8 @@ where /// An active TWAI peripheral in Normal Mode. /// -/// In this mode, the TWAI controller can transmit and receive messages including error -/// signals (such as error and overload frames). +/// 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>, } @@ -318,9 +330,11 @@ impl<'d, T> Twai<'d, T> where T: Instance, { - /// Stop the peripheral, putting it into reset mode and enabling reconfiguration. + /// 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. + // Put the peripheral into reset/configuration mode by setting the reset mode + // bit. self.peripheral .register_block() .mode @@ -357,10 +371,11 @@ where .bit_is_set() } - /// Get the number of messages that the peripheral has available in the receive FIFO. + /// Get the number of messages that the peripheral has available in the + /// receive FIFO. /// - /// Note that this may not be the number of valid messages in the receive FIFO due to - /// fifo overflow/overrun. + /// 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() @@ -369,19 +384,22 @@ where .rx_message_counter() .bits() } - /// Clear the receive FIFO, discarding any valid, partial, or invalid packets. + /// Clear the receive FIFO, discarding any valid, partial, or invalid + /// packets. /// /// This is typically used to clear an overrun receive FIFO. /// - /// TODO: Not sure if this needs to be guarded against Bus Off or other error states. + /// TODO: Not sure if this needs to be guarded against Bus Off or other + /// error states. pub fn clear_receive_fifo(&self) { while self.num_available_messages() > 0 { self.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. + /// 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() @@ -404,12 +422,14 @@ impl Error for EspTwaiError { } } -/// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into the destination. +/// Copy data from multiple TWAI_DATA_x_REG registers, packing the source into +/// the destination. /// /// # Safety -/// This function is marked unsafe because it reads arbitrarily from memory-mapped registers. -/// Specifically, this function is used with the TWAI_DATA_x_REG registers which has different -/// results based on the mode of the peripheral. +/// This function is marked unsafe because it reads arbitrarily from +/// memory-mapped registers. Specifically, this function is used with the +/// TWAI_DATA_x_REG registers which has different results based on the mode of +/// the peripheral. unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { let src = from_raw_parts(src, dest.len()); @@ -418,12 +438,14 @@ unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { } } -/// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into the destination. +/// Copy data to multiple TWAI_DATA_x_REG registers, unpacking the source into +/// the destination. /// /// # Safety -/// This function is marked unsafe because it writes arbitrarily to memory-mapped registers. -/// Specifically, this function is used with the TWAI_DATA_x_REG registers which has different -/// results based on the mode of the peripheral. +/// This function is marked unsafe because it writes arbitrarily to +/// memory-mapped registers. Specifically, this function is used with the +/// TWAI_DATA_x_REG registers which has different results based on the mode of +/// the peripheral. unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { let dest = from_raw_parts_mut(dest, src.len()); @@ -440,15 +462,16 @@ where 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. + /// 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. - /// + /// 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(); @@ -530,15 +553,16 @@ where // Is RTR frame, so no data is included. } - // Set the transmit request command, this will lock the transmit buffer until the - // transmission is complete or aborted. + // 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. + // 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. @@ -599,7 +623,8 @@ where let id = StandardId::new(raw_id).unwrap(); if is_data_frame { - // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG registers. + // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG + // registers. let register_data = unsafe { from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) }; diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index a7a20386500..67a9cd4e00f 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -1,24 +1,26 @@ #![no_std] #![no_main] -use esp32c3_hal::{ - clock::ClockControl, gpio::IO, peripherals::Peripherals, prelude::*, timer::TimerGroup, twai, - Rtc, -}; -use esp_println::println; - // 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 +// 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}; - // 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::*, + timer::TimerGroup, + twai, + Rtc, +}; use esp_backtrace as _; +use esp_println::println; use nb::block; use riscv_rt::entry; @@ -42,15 +44,16 @@ fn main() -> ! { 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. + // 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. + // 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.TWAI, can_tx_pin, @@ -60,17 +63,19 @@ fn main() -> ! { 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. + // 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. + // 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 mut can = can_config.start(); loop { From d18a5cc96fb952a3cc0c366bedb4370603c11388 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 21 Dec 2022 18:30:01 -0600 Subject: [PATCH 20/20] Add gpio PeripheralRef and use volatile for direct register access. --- esp-hal-common/src/twai/mod.rs | 70 +++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index a10214458b0..8bf7dc08d6c 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -4,8 +4,6 @@ //! controllers. It supports Standard Frame Format (11-bit) and Extended Frame //! Format (29-bit) frame identifiers. -use core::slice::{from_raw_parts, from_raw_parts_mut}; - #[cfg(feature = "eh1")] use embedded_can::{nb::Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId}; #[cfg(not(feature = "eh1"))] @@ -35,20 +33,25 @@ pub struct EspTwaiFrame { } impl EspTwaiFrame { - /// Make a new frame from an id and a slice of the TWAI_DATA_x_REG - /// registers. - fn new_from_data_registers(id: impl Into, data: &[u32]) -> Self { - let mut d: [u8; 8] = [0; 8]; + /// Make a new frame from an id, pointer to the TWAI_DATA_x_REG registers, + /// and the length of the data payload (dlc). + /// + /// # Safety + /// This is unsafe because it directly accesses peripheral registers. + unsafe fn new_from_data_registers( + id: impl Into, + registers: *const u32, + dlc: usize, + ) -> Self { + let mut data: [u8; 8] = [0; 8]; // Copy the data from the memory mapped peripheral into actual memory. - for (src, dest) in data.iter().zip(d.iter_mut()) { - *dest = *src as u8; - } + copy_from_data_register(&mut data[..dlc], registers); Self { id: id.into(), - data: d, - dlc: data.len(), + data, + dlc: dlc, is_remote: false, } } @@ -198,8 +201,8 @@ where { pub fn new( peripheral: impl Peripheral

+ 'd, - mut tx_pin: TX, - mut rx_pin: RX, + tx_pin: impl Peripheral

+ 'd, + rx_pin: impl Peripheral

+ 'd, clock_control: &mut PeripheralClockControl, clocks: &Clocks, baud_rate: BaudRate, @@ -208,6 +211,7 @@ where clock_control.enable(crate::system::Peripheral::Twai); // Set up the GPIO pins. + crate::into_ref!(tx_pin, rx_pin); tx_pin.connect_peripheral_to_output(OutputSignal::TWAI_TX); rx_pin.connect_input_to_peripheral(InputSignal::TWAI_RX); @@ -430,11 +434,11 @@ impl Error for EspTwaiError { /// memory-mapped registers. Specifically, this function is used with the /// TWAI_DATA_x_REG registers which has different results based on the mode of /// the peripheral. -unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { - let src = from_raw_parts(src, dest.len()); - - for (dest, src) in dest.iter_mut().zip(src.iter()) { - *dest = *src as u8; +#[inline(always)] +unsafe fn copy_from_data_register(dest: &mut [u8], src: *const u32) { + for (i, dest) in dest.iter_mut().enumerate() { + // Perform a volatile read to avoid compiler optimizations. + *dest = src.add(i).read_volatile() as u8; } } @@ -446,11 +450,11 @@ unsafe fn _copy_from_data_register(dest: &mut [u8], src: *const u32) { /// memory-mapped registers. Specifically, this function is used with the /// TWAI_DATA_x_REG registers which has different results based on the mode of /// the peripheral. +#[inline(always)] unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { - let dest = from_raw_parts_mut(dest, src.len()); - - for (dest, src) in dest.iter_mut().zip(src.iter()) { - *dest = *src as u32; + for (i, src) in src.iter().enumerate() { + // Perform a volatile write to avoid compiler optimizations. + dest.add(i).write_volatile(*src as u32); } } @@ -625,10 +629,13 @@ where if is_data_frame { // Create a new frame from the contents of the appropriate TWAI_DATA_x_REG // registers. - let register_data = unsafe { - from_raw_parts(self.peripheral.register_block().data_3.as_ptr(), dlc) - }; - EspTwaiFrame::new_from_data_registers(id, register_data) + unsafe { + EspTwaiFrame::new_from_data_registers( + id, + self.peripheral.register_block().data_3.as_ptr(), + dlc, + ) + } } else { EspTwaiFrame::new_remote(id, dlc).unwrap() } @@ -674,10 +681,13 @@ where let id = ExtendedId::new(raw_id).unwrap(); if is_data_frame { - let register_data = unsafe { - from_raw_parts(self.peripheral.register_block().data_5.as_ptr(), dlc) - }; - EspTwaiFrame::new_from_data_registers(id, register_data) + unsafe { + EspTwaiFrame::new_from_data_registers( + id, + self.peripheral.register_block().data_5.as_ptr(), + dlc, + ) + } } else { EspTwaiFrame::new_remote(id, dlc).unwrap() }