From 1f6803776c10cfbb0fa444b45d3d88f5b68d9121 Mon Sep 17 00:00:00 2001 From: Alex Bohm Date: Wed, 21 Dec 2022 18:54:06 -0600 Subject: [PATCH] Initial TWAI Driver Implementation (#192) * wip: initial implementation of transmission only. * Moved TWAI to its own directory and added initial reception of packets. * Added extended id transmit and receive. * Added maybe better code for making packet filters. * Fixed bug with ids and improved methods of copying data to the peripheral. * Added some guards against Bus Off * Added reception of remote frames. * Clean up of comments, etc * Updated TWAI naming and cleaned up example a bit. * Updated bitselector to include better unpacking methods. * Add embedded-can and limit initial TWAI implementation to esp32c3. * Added embedded-can to esp32c3 twai example. * Switched twai filter to using bytestrings. Co-authored-by: dimi * Implemented new() for twai filters. * Clean up TWAI docs and example. * Fix filter constructors and add examples. * pre driver PeripheralRef update. * PeripheralRef/twai * Format comments with nightly rustfmt. * Add gpio PeripheralRef and use volatile for direct register access. Co-authored-by: dimi --- esp-hal-common/Cargo.toml | 3 +- esp-hal-common/src/lib.rs | 2 + esp-hal-common/src/peripherals/esp32c3.rs | 1 + esp-hal-common/src/system.rs | 7 + esp-hal-common/src/twai/filter.rs | 512 ++++++++++++++++ esp-hal-common/src/twai/mod.rs | 714 ++++++++++++++++++++++ esp32c3-hal/Cargo.toml | 3 +- esp32c3-hal/examples/twai.rs | 108 ++++ esp32c3-hal/src/lib.rs | 1 + 9 files changed, 1349 insertions(+), 2 deletions(-) create mode 100644 esp-hal-common/src/twai/filter.rs create mode 100644 esp-hal-common/src/twai/mod.rs create mode 100644 esp32c3-hal/examples/twai.rs 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 b8ccd5e79b9..89070691f34 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -73,6 +73,8 @@ pub mod system; #[cfg(systimer)] pub mod systimer; pub mod timer; +#[cfg(any(esp32c3))] +pub mod twai; pub mod uart; #[cfg(usb_serial_jtag)] pub mod usb_serial_jtag; 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/system.rs b/esp-hal-common/src/system.rs index 920fa7d0938..bce01d8178d 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -42,6 +42,8 @@ pub enum Peripheral { I2s1, #[cfg(usb_otg)] Usb, + #[cfg(any(esp32c3))] + Twai, } /// Controls the enablement of peripheral clocks. @@ -149,6 +151,11 @@ 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/filter.rs b/esp-hal-common/src/twai/filter.rs new file mode 100644 index 00000000000..f2636e320c3 --- /dev/null +++ b/esp-hal-common/src/twai/filter.rs @@ -0,0 +1,512 @@ +//! # 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"))] +use embedded_hal::can::{ExtendedId, StandardId}; + +#[derive(Debug, PartialEq, Eq)] +pub enum FilterType { + Single, + Dual, +} + +pub trait 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 { + 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. +/// +/// 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], +} + +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. + /// + /// 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; + 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; + set_bit_from_byte!(acceptance_code, acceptance_mask, rtr[0], shift); + } + // 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; + set_bit_from_byte!( + acceptance_code, + acceptance_mask, + payload[payload_index][idx], + shift + ); + idx += 1; + } + payload_index += 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. + /// + /// + /// # 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; + + // 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_mask_to_register_array(acceptance_code, acceptance_mask), + } + } +} + +impl Filter for SingleStandardFilter { + const FILTER_TYPE: FilterType = FilterType::Single; + fn to_registers(&self) -> [u8; 8] { + self.raw + } +} +/// 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], +} +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. + /// + /// # 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; + // 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] { + self.raw + } +} + +/// 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. +/// +/// 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. + /// + /// # 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>, + ) -> 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; + + // 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] { + self.raw + } +} +/// 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 { + raw: [u8; 8], +} +impl DualExtendedFilter { + /// 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 + /// ``` + /// 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; + // 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), + } + } + /// 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. + 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] { + self.raw + } +} diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs new file mode 100644 index 00000000000..8bf7dc08d6c --- /dev/null +++ b/esp-hal-common/src/twai/mod.rs @@ -0,0 +1,714 @@ +//! # 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. + +#[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; + +/// Structure backing the embedded_hal::can::Frame/embedded_can::Frame trait. +#[derive(Debug)] +pub struct EspTwaiFrame { + id: Id, + dlc: usize, + data: [u8; 8], + is_remote: bool, +} + +impl EspTwaiFrame { + /// 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. + copy_from_data_register(&mut data[..dlc], registers); + + Self { + id: id.into(), + data, + dlc: dlc, + is_remote: false, + } + } +} + +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; + } + + let mut d: [u8; 8] = [0; 8]; + 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 { + // CAN2.0 frames cannot have more than 8 bytes. + if dlc > 8 { + return None; + } + Some(EspTwaiFrame { + id: id.into(), + data: [0; 8], + dlc, + is_remote: true, + }) + } + + fn is_extended(&self) -> bool { + match self.id { + Id::Standard(_) => false, + Id::Extended(_) => true, + } + } + + fn is_remote_frame(&self) -> bool { + self.is_remote + } + + fn id(&self) -> Id { + self.id + } + + fn dlc(&self) -> usize { + self.dlc + } + + 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], + } + } +} + +/// 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. +/// Currently these timings are sourced from the ESP IDF C driver which assumes +/// an APB clock of 80MHz. +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} + const 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<'d, T> { + peripheral: PeripheralRef<'d, T>, +} + +impl<'d, T> TwaiConfiguration<'d, T> +where + T: Instance, +{ + pub fn new( + peripheral: impl Peripheral

+ 'd, + tx_pin: impl Peripheral

+ 'd, + rx_pin: impl Peripheral

+ 'd, + clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + baud_rate: BaudRate, + ) -> Self { + // Enable the peripheral clock for the TWAI peripheral. + 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); + + crate::into_ref!(peripheral); + let mut cfg = TwaiConfiguration { peripheral }; + + cfg.set_baud_rate(baud_rate, clocks); + + cfg + } + + /// 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. + 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. + 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) + }); + } + + /// 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) { + // Set or clear the rx filter mode bit depending on the filter type. + let filter_mode_bit = filter.filter_type() == FilterType::Single; + self.peripheral + .register_block() + .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(); + + // 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. + /// + /// 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<'d, T> { + // 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. +/// +/// 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>, +} + +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<'d, T> { + // 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() + } + + /// Check if the controller is in a bus off state. + pub fn is_bus_off(&self) -> bool { + self.peripheral + .register_block() + .status + .read() + .bus_off_st() + .bit_is_set() + } + + /// 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. + pub fn num_available_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. + /// + /// 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. + fn release_receive_fifo(&self) { + self.peripheral + .register_block() + .cmd + .write(|w| w.release_buf().set_bit()); + } +} + +#[derive(Debug)] +pub enum EspTwaiError { + BusOff, + EmbeddedHAL(ErrorKind), +} +impl Error for EspTwaiError { + fn kind(&self) -> ErrorKind { + match self { + Self::BusOff => ErrorKind::Other, + Self::EmbeddedHAL(kind) => *kind, + } + } +} + +/// 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. +#[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; + } +} + +/// 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. +#[inline(always)] +unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { + for (i, src) in src.iter().enumerate() { + // Perform a volatile write to avoid compiler optimizations. + dest.add(i).write_volatile(*src as u32); + } +} + +impl Can for Twai<'_, T> +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: 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(); + + // 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; + 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; + + 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() { + Id::Standard(id) => { + let id = id.as_raw(); + + self.peripheral + .register_block() + .data_1 + .write(|w| w.tx_byte_1().variant((id >> 3) as u8)); + + self.peripheral + .register_block() + .data_2 + .write(|w| w.tx_byte_2().variant((id << 5) as u8)); + } + Id::Extended(id) => { + let id = id.as_raw(); + + self.peripheral + .register_block() + .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)); + } + } + + // Store the data portion of the packet into the transmit buffer. + if frame.is_data_frame() { + match frame.id() { + Id::Standard(_) => unsafe { + copy_to_data_register( + self.peripheral.register_block().data_3.as_ptr(), + frame.data(), + ) + }, + 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. + } + + // Set the transmit request command, this will lock the transmit buffer until + // the transmission is complete or aborted. + self.peripheral + .register_block() + .cmd + .write(|w| w.tx_req().set_bit()); + + // Success in readying packet for transmit. No packets can be replaced in the + // transmit buffer so return None in accordance with the + // embedded-can/embedded-hal trait. + nb::Result::Ok(None) + } + /// Return a received frame if there are any available. + fn receive(&mut self) -> nb::Result { + let status = self.peripheral.register_block().status.read(); + + // Check that the peripheral is not in a bus off state. + if status.bus_off_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(EspTwaiError::BusOff)); + } + + // Check that we actually have packets to receive. + if !status.rx_buf_st().bit_is_set() { + return nb::Result::Err(nb::Error::WouldBlock); + } + + // Check if the packet in the receive buffer is valid or overrun. + if status.miss_st().bit_is_set() { + return nb::Result::Err(nb::Error::Other(EspTwaiError::EmbeddedHAL( + ErrorKind::Overrun, + ))); + } + + // Read the frame information and extract the frame id format and dlc. + let data_0 = self + .peripheral + .register_block() + .data_0 + .read() + .tx_byte_0() + .bits(); + + let 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. + let frame = if is_standard_format { + // Frame uses standard 11 bit id. + let data_1 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .bits(); + + let data_2 = self + .peripheral + .register_block() + .data_2 + .read() + .tx_byte_2() + .bits(); + + let raw_id: u16 = ((data_1 as u16) << 3) | ((data_2 as u16) >> 5); + + 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. + unsafe { + EspTwaiFrame::new_from_data_registers( + id, + self.peripheral.register_block().data_3.as_ptr(), + dlc, + ) + } + } else { + EspTwaiFrame::new_remote(id, dlc).unwrap() + } + } else { + // Frame uses extended 29 bit id. + let data_1 = self + .peripheral + .register_block() + .data_1 + .read() + .tx_byte_1() + .bits(); + + let data_2 = self + .peripheral + .register_block() + .data_2 + .read() + .tx_byte_2() + .bits(); + + let data_3 = self + .peripheral + .register_block() + .data_3 + .read() + .tx_byte_3() + .bits(); + + let data_4 = self + .peripheral + .register_block() + .data_4 + .read() + .tx_byte_4() + .bits(); + + let 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(); + + if is_data_frame { + unsafe { + EspTwaiFrame::new_from_data_registers( + id, + self.peripheral.register_block().data_5.as_ptr(), + dlc, + ) + } + } else { + EspTwaiFrame::new_remote(id, 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(frame) + } +} + +pub trait Instance { + fn register_block(&self) -> &RegisterBlock; +} + +#[cfg(any(esp32c3))] +impl Instance for crate::peripherals::TWAI { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } +} 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 new file mode 100644 index 00000000000..67a9cd4e00f --- /dev/null +++ b/esp32c3-hal/examples/twai.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] + +// 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 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; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + 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); + 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(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN + // transceiver. + let can_tx_pin = io.pins.gpio2; + let can_rx_pin = io.pins.gpio3; + + // The speed of the CAN bus. + const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; + + // Begin configuring the TWAI peripheral. The peripheral is in a reset like + // state that prevents transmission but allows configuration. + let mut can_config = twai::TwaiConfiguration::new( + peripherals.TWAI, + can_tx_pin, + can_rx_pin, + &mut system.peripheral_clock_control, + &clocks, + CAN_BAUDRATE, + ); + + // Partially filter the incoming messages to reduce overhead of receiving + // undesired messages. Note that due to how the hardware filters messages, + // standard ids and extended ids may both match a filter. Frame ids should + // be explicitly checked in the application instead of fully relying on + // these partial acceptance filters to exactly match. A filter that matches + // standard ids of an even value. + const FILTER: twai::filter::SingleStandardFilter = + twai::filter::SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + can_config.set_filter(FILTER); + + // Start the peripheral. This locks the configuration settings of the peripheral + // and puts it into operation mode, allowing packets to be sent and + // received. + let mut can = can_config.start(); + + loop { + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame:"); + + // Print different messages based on the frame id type. + match frame.id() { + Id::Standard(id) => { + println!("\tStandard Id: {:?}", id); + } + Id::Extended(id) => { + println!("\tExtended Id: {:?}", id); + } + } + + // Print out the frame data or the requested data length code for a remote + // transmission request frame. + if frame.is_data_frame() { + println!("\tData: {:?}", frame.data()); + } else { + println!("\tRemote Frame. Data Length Code: {}", frame.dlc()); + } + + // Transmit the frame back. + let _result = block!(can.transmit(&frame)).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,