()])
+ });
+
+ pub const fn new() -> Self {
+ Self::INIT
+ }
+
+ #[cfg(any(esp32, esp32s2))]
+ pub fn data(&self) -> u16 {
+ unsafe { self.0.__bindgen_anon_1.type1.data() as _ }
+ }
+
+ #[cfg(any(esp32, esp32s2))]
+ pub fn channel(&self) -> adc_channel_t {
+ unsafe { self.0.__bindgen_anon_1.type1.channel() as _ }
+ }
+
+ #[cfg(not(any(esp32, esp32s2)))]
+ pub fn data(&self) -> u16 {
+ unsafe { self.0.__bindgen_anon_1.type2.data() as _ }
+ }
+
+ #[cfg(not(any(esp32, esp32s2)))]
+ pub fn channel(&self) -> adc_channel_t {
+ unsafe { self.0.__bindgen_anon_1.type2.channel() as _ }
+ }
+
+ #[cfg(not(any(esp32, esp32s2, esp32h2, esp32c6)))]
+ pub fn unit(&self) -> adc_unit_t {
+ unsafe { self.0.__bindgen_anon_1.type2.unit() as _ }
+ }
+
+ #[cfg(any(esp32, esp32s2))]
+ pub fn nullify(&mut self) {
+ self.0.__bindgen_anon_1.val = self.data();
+ }
+
+ #[cfg(not(any(esp32, esp32s2)))]
+ pub fn nullify(&mut self) {
+ self.0.__bindgen_anon_1.val = self.data() as _;
+ }
+
+ #[cfg(any(esp32, esp32s2))]
+ pub fn as_pcm16(data: &mut [AdcMeasurement]) -> &mut [u16] {
+ for measurement in data.iter_mut() {
+ measurement.nullify();
+ }
+
+ unsafe { core::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut _, data.len()) }
+ }
+
+ #[cfg(not(any(esp32, esp32s2)))]
+ pub fn as_pcm32(data: &mut [AdcMeasurement]) -> &mut [u32] {
+ for measurement in data.iter_mut() {
+ measurement.nullify();
+ }
+
+ unsafe { core::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut _, data.len()) }
+ }
+ }
+
+ impl Display for AdcMeasurement {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "ADC Reading {{channel={}, data={}}}",
+ self.channel(),
+ self.data()
+ )
+ }
+ }
+
+ impl Debug for AdcMeasurement {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "AdcMeasurement {{channel: {}, data: {}}}",
+ self.channel(),
+ self.data()
+ )
+ }
+ }
+
+ pub mod config {
+ use crate::units::*;
+
+ /// ADC continuous mode driver configurations.
+ /// The default configuration is:
+ /// * [`sample_freq`][Config::sample_freq]: 2000 Hz
+ /// * [`frame_measurements`][Config::frame_measurements]: 100
+ /// * [`frames_count`][Config::frames_count]: 10
+ #[derive(Debug, Copy, Clone)]
+ pub struct Config {
+ pub sample_freq: Hertz,
+ pub frame_measurements: usize,
+ pub frames_count: usize,
+ }
+
+ impl Config {
+ pub const fn new() -> Self {
+ Self {
+ sample_freq: Hertz(20000),
+ frame_measurements: 100,
+ frames_count: 10,
+ }
+ }
+
+ #[must_use]
+ pub fn sample_freq(mut self, sample_freq: Hertz) -> Self {
+ self.sample_freq = sample_freq;
+ self
+ }
+
+ #[must_use]
+ pub fn frame_measurements(mut self, frame_measurements: usize) -> Self {
+ self.frame_measurements = frame_measurements;
+ self
+ }
+
+ #[must_use]
+ pub fn frames_count(mut self, frames_count: usize) -> Self {
+ self.frames_count = frames_count;
+ self
+ }
+ }
+
+ impl Default for Config {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+ }
+
+ pub struct AdcDriver<'d> {
+ handle: adc_continuous_handle_t,
+ adc: u8,
+ _ref: PhantomData<&'d ()>,
+ }
+
+ impl<'d> AdcDriver<'d> {
+ /// Initialize ADC continuous driver with configuration and channels.
+ #[cfg(esp32)]
+ pub fn new(
+ adc: impl Peripheral + 'd,
+ _i2s: impl Peripheral
+ 'd,
+ config: &config::Config,
+ channels: impl AdcChannels + 'd,
+ ) -> Result {
+ Self::internal_new(adc, config, channels)
+ }
+
+ /// Initialize ADC continuous driver with configuration and channels
+ #[cfg(esp32s2)]
+ pub fn new(
+ adc: impl Peripheral + 'd,
+ _spi: impl Peripheral
+ 'd,
+ config: &config::Config,
+ channels: impl AdcChannels + 'd,
+ ) -> Result {
+ Self::internal_new(adc, config, channels)
+ }
+
+ /// Initialize ADC continuous driver with configuration and channels.
+ #[cfg(not(any(esp32, esp32s2)))]
+ pub fn new(
+ adc: impl Peripheral + 'd,
+ config: &config::Config,
+ channels: impl AdcChannels + 'd,
+ ) -> Result {
+ Self::internal_new(adc, config, channels)
+ }
+
+ fn internal_new(
+ _adc: impl Peripheral + 'd,
+ config: &config::Config,
+ channels: impl AdcChannels + 'd,
+ ) -> Result {
+ let mut patterns = [adc_digi_pattern_config_t::default(); 32];
+
+ for (index, (channel, atten)) in channels.iter().enumerate() {
+ if index >= patterns.len() {
+ return Err(EspError::from_infallible::());
+ }
+
+ patterns[index].atten = atten as _;
+ patterns[index].channel = channel as _;
+ patterns[index].unit = A::unit() as _;
+ patterns[index].bit_width = 12; // For now only 12 bits is supported
+ }
+
+ let mut handle: adc_continuous_handle_t = core::ptr::null_mut();
+
+ #[allow(clippy::needless_update)]
+ esp!(unsafe {
+ adc_continuous_new_handle(
+ &adc_continuous_handle_cfg_t {
+ max_store_buf_size: SOC_ADC_DIGI_DATA_BYTES_PER_CONV
+ * (config.frame_measurements as u32)
+ * (config.frames_count as u32),
+ conv_frame_size: SOC_ADC_DIGI_DATA_BYTES_PER_CONV
+ * (config.frame_measurements as u32),
+ ..Default::default()
+ },
+ &mut handle,
+ )
+ })?;
+
+ let conv_mode = if A::unit() == 0 {
+ adc_digi_convert_mode_t_ADC_CONV_SINGLE_UNIT_1
+ } else {
+ adc_digi_convert_mode_t_ADC_CONV_SINGLE_UNIT_2
+ };
+
+ #[cfg(any(esp32, esp32s2))]
+ let format = adc_digi_output_format_t_ADC_DIGI_OUTPUT_FORMAT_TYPE1;
+
+ #[cfg(not(any(esp32, esp32s2)))]
+ let format = adc_digi_output_format_t_ADC_DIGI_OUTPUT_FORMAT_TYPE2;
+
+ esp!(unsafe {
+ adc_continuous_config(
+ handle,
+ &adc_continuous_config_t {
+ pattern_num: channels.iter().count() as _,
+ adc_pattern: &patterns as *const _ as *mut _,
+ sample_freq_hz: config.sample_freq.into(),
+ conv_mode,
+ format,
+ },
+ )
+ })?;
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ {
+ esp!(unsafe {
+ adc_continuous_register_event_callbacks(
+ handle,
+ &adc_continuous_evt_cbs_t {
+ on_conv_done: Some(Self::handle_isr),
+ on_pool_ovf: Some(Self::handle_isr),
+ },
+ &NOTIFIER[A::unit() as usize] as *const _ as *mut _,
+ )
+ })?;
+ }
+
+ Ok(Self {
+ handle,
+ adc: A::unit() as _,
+ _ref: PhantomData,
+ })
+ }
+
+ /// Get the ADC driver handle.
+ pub fn handle(&self) -> adc_continuous_handle_t {
+ self.handle
+ }
+
+ // Get the ADC unit. ADC1 or ADC2.
+ pub fn unit(&self) -> adc_unit_t {
+ self.adc as _
+ }
+
+ /// Start the ADC under continuous mode and generate results.
+ pub fn start(&mut self) -> Result<(), EspError> {
+ esp!(unsafe { adc_continuous_start(self.handle) })
+ }
+
+ /// Stop the ADC.
+ pub fn stop(&mut self) -> Result<(), EspError> {
+ esp!(unsafe { adc_continuous_stop(self.handle) })
+ }
+
+ /// Read `AdcMeasurements` in continuous mode.
+ pub fn read(
+ &mut self,
+ buf: &mut [AdcMeasurement],
+ timeout: TickType_t,
+ ) -> Result {
+ let mut read: u32 = 0;
+
+ esp!(unsafe {
+ adc_continuous_read(
+ self.handle,
+ buf.as_mut_ptr() as *mut _,
+ core::mem::size_of_val(buf) as _,
+ &mut read,
+ TickType(timeout).as_millis_u32(),
+ )
+ })?;
+
+ Ok(read as usize / core::mem::size_of::())
+ }
+
+ /// Read bytes in continuous mode.
+ pub fn read_bytes(
+ &mut self,
+ buf: &mut [u8],
+ timeout: TickType_t,
+ ) -> Result {
+ let mut read: u32 = 0;
+
+ esp!(unsafe {
+ adc_continuous_read(
+ self.handle,
+ buf.as_mut_ptr() as *mut _,
+ core::mem::size_of_val(buf) as _,
+ &mut read,
+ TickType(timeout).as_millis_u32(),
+ )
+ })?;
+
+ Ok(read as usize)
+ }
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ pub async fn read_async(&mut self, buf: &mut [AdcMeasurement]) -> Result {
+ loop {
+ match self.read(buf, delay::NON_BLOCK) {
+ Ok(len) if len > 0 => return Ok(len),
+ Err(e) if e.code() != ESP_ERR_TIMEOUT => return Err(e),
+ _ => {
+ NOTIFIER[self.adc as usize].wait().await;
+ }
+ }
+ }
+ }
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ pub async fn read_bytes_async(&mut self, buf: &mut [u8]) -> Result {
+ loop {
+ match self.read_bytes(buf, delay::NON_BLOCK) {
+ Ok(len) if len > 0 => return Ok(len),
+ Err(e) if e.code() != ESP_ERR_TIMEOUT => return Err(e),
+ _ => {
+ NOTIFIER[self.adc as usize].wait().await;
+ }
+ }
+ }
+ }
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ extern "C" fn handle_isr(
+ _handle: adc_continuous_handle_t,
+ _data: *const adc_continuous_evt_data_t,
+ user_data: *mut c_void,
+ ) -> bool {
+ let notifier: &HalIsrNotification =
+ unsafe { (user_data as *const HalIsrNotification).as_ref() }.unwrap();
+
+ notifier.notify_lsb()
+ }
+ }
+
+ impl Drop for AdcDriver<'_> {
+ fn drop(&mut self) {
+ let _ = self.stop();
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ {
+ esp!(unsafe {
+ adc_continuous_register_event_callbacks(
+ self.handle,
+ &adc_continuous_evt_cbs_t {
+ on_conv_done: None,
+ on_pool_ovf: None,
+ },
+ core::ptr::null_mut(),
+ )
+ })
+ .unwrap();
+ }
+
+ esp!(unsafe { adc_continuous_deinit(self.handle) }).unwrap();
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ NOTIFIER[self.adc as usize].reset();
+ }
+ }
+
+ unsafe impl Send for AdcDriver<'_> {}
+
+ impl embedded_io::ErrorType for AdcDriver<'_> {
+ type Error = EspIOError;
+ }
+
+ impl embedded_io::Read for AdcDriver<'_> {
+ fn read(&mut self, buf: &mut [u8]) -> Result {
+ self.read_bytes(buf, delay::BLOCK).map_err(EspIOError)
+ }
+ }
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ impl embedded_io_async::Read for AdcDriver<'_> {
+ async fn read(&mut self, buf: &mut [u8]) -> Result {
+ self.read_bytes_async(buf).await.map_err(EspIOError)
+ }
+ }
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ #[cfg(any(esp32c2, esp32h2, esp32c5, esp32c6, esp32p4))] // TODO: Check for esp32c5 and esp32p4
+ static NOTIFIER: [HalIsrNotification; 1] = [HalIsrNotification::new()];
+
+ #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))]
+ #[cfg(not(any(esp32c2, esp32h2, esp32c5, esp32c6, esp32p4)))] // TODO: Check for esp32c5 and esp32p4
+ static NOTIFIER: [HalIsrNotification; 2] =
+ [HalIsrNotification::new(), HalIsrNotification::new()];
+}
diff --git a/src/can.rs b/src/can.rs
index 7c0ff37e14b..c03d54e0dba 100644
--- a/src/can.rs
+++ b/src/can.rs
@@ -32,11 +32,23 @@
//! }
//! ```
+use core::borrow::BorrowMut;
+use core::ffi::CStr;
+use core::marker::PhantomData;
+use core::num::NonZeroU32;
+
+use enumset::{EnumSet, EnumSetType};
+
use esp_idf_sys::*;
-use crate::delay::{BLOCK, NON_BLOCK};
-use crate::gpio::*;
+use num_enum::TryFromPrimitive;
+
+use crate::cpu::Core;
+use crate::delay::{self, BLOCK, NON_BLOCK};
+use crate::interrupt::InterruptType;
use crate::peripheral::{Peripheral, PeripheralRef};
+use crate::task::asynch::Notification;
+use crate::{gpio::*, task};
crate::embedded_hal_error!(CanError, embedded_can::Error, embedded_can::ErrorKind);
@@ -49,8 +61,13 @@ crate::embedded_hal_error!(
pub type CanConfig = config::Config;
pub mod config {
+ use enumset::EnumSet;
use esp_idf_sys::*;
+ use crate::interrupt::InterruptType;
+
+ use super::Alert;
+
/// CAN timing
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Timing {
@@ -80,7 +97,6 @@ pub mod config {
tseg_1: 16,
tseg_2: 8,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B50K => twai_timing_config_t {
@@ -88,7 +104,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B100K => twai_timing_config_t {
@@ -96,7 +111,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B125K => twai_timing_config_t {
@@ -104,7 +118,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B250K => twai_timing_config_t {
@@ -112,7 +125,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B500K => twai_timing_config_t {
@@ -120,7 +132,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B800K => twai_timing_config_t {
@@ -128,7 +139,6 @@ pub mod config {
tseg_1: 16,
tseg_2: 8,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::B1M => twai_timing_config_t {
@@ -136,7 +146,6 @@ pub mod config {
tseg_1: 15,
tseg_2: 4,
sjw: 3,
- triple_sampling: false,
..Default::default()
},
Timing::Custom {
@@ -150,7 +159,24 @@ pub mod config {
tseg_1: timing_segment_1,
tseg_2: timing_segment_2,
sjw: synchronization_jump_width,
+ #[cfg(any(
+ esp_idf_version_major = "4",
+ esp_idf_version = "5.0",
+ esp_idf_version = "5.1",
+ esp_idf_version = "5.2",
+ esp_idf_version = "5.3",
+ esp_idf_version = "5.4"
+ ))]
triple_sampling,
+ #[cfg(not(any(
+ esp_idf_version_major = "4",
+ esp_idf_version = "5.0",
+ esp_idf_version = "5.1",
+ esp_idf_version = "5.2",
+ esp_idf_version = "5.3",
+ esp_idf_version = "5.4"
+ )))]
+ __bindgen_anon_1: twai_timing_config_t__bindgen_ty_1 { triple_sampling },
..Default::default()
},
}
@@ -211,9 +237,16 @@ pub mod config {
/// ```
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Filter {
- // Filter for 11 bit standard CAN IDs
+ /// Filter for 11 bit standard CAN IDs
Standard { filter: u16, mask: u16 },
- // Filter for 29 bit extended CAN IDs
+ /// Filter for two 11 bit standard CAN IDs
+ Dual {
+ filter1: u16,
+ mask1: u16,
+ filter2: u16,
+ mask2: u16,
+ },
+ /// Filter for 29 bit extended CAN IDs
Extended { filter: u32, mask: u32 },
}
@@ -236,14 +269,45 @@ pub mod config {
}
#[derive(Debug, Copy, Clone, Default)]
+ pub enum Mode {
+ #[default]
+ Normal,
+ NoAck,
+ ListenOnly,
+ }
+
+ impl From for twai_mode_t {
+ fn from(val: Mode) -> Self {
+ match val {
+ Mode::Normal => twai_mode_t_TWAI_MODE_NORMAL,
+ Mode::NoAck => twai_mode_t_TWAI_MODE_NO_ACK,
+ Mode::ListenOnly => twai_mode_t_TWAI_MODE_LISTEN_ONLY,
+ }
+ }
+ }
+
+ #[derive(Debug, Clone)]
pub struct Config {
pub timing: Timing,
pub filter: Filter,
+ pub tx_queue_len: u32,
+ pub rx_queue_len: u32,
+ pub mode: Mode,
+ pub alerts: EnumSet,
+ pub intr_flags: EnumSet,
}
impl Config {
pub fn new() -> Self {
- Default::default()
+ Self {
+ timing: Default::default(),
+ filter: Default::default(),
+ tx_queue_len: 5,
+ rx_queue_len: 5,
+ mode: Default::default(),
+ alerts: Default::default(),
+ intr_flags: EnumSet::::empty(),
+ }
}
#[must_use]
@@ -257,113 +321,198 @@ pub mod config {
self.filter = filter;
self
}
+
+ #[must_use]
+ pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
+ self.tx_queue_len = tx_queue_len;
+ self
+ }
+
+ #[must_use]
+ pub fn rx_queue_len(mut self, rx_queue_len: u32) -> Self {
+ self.rx_queue_len = rx_queue_len;
+ self
+ }
+
+ #[must_use]
+ pub fn mode(mut self, mode: Mode) -> Self {
+ self.mode = mode;
+ self
+ }
+
+ #[must_use]
+ pub fn alerts(mut self, alerts: EnumSet) -> Self {
+ self.alerts = alerts;
+ self
+ }
+
+ #[must_use]
+ pub fn intr_flags(mut self, flags: EnumSet) -> Self {
+ self.intr_flags = flags;
+ self
+ }
+ }
+
+ impl Default for Config {
+ fn default() -> Self {
+ Self::new()
+ }
}
}
-/// CAN abstraction
-pub struct CanDriver<'d>(PeripheralRef<'d, CAN>);
+#[derive(Debug, EnumSetType, TryFromPrimitive)]
+#[enumset(repr = "u32")]
+#[repr(u32)]
+pub enum Alert {
+ TransmitIdle = 1,
+ Success = 2,
+ Received = 3,
+ BelowErrorWarning = 4,
+ ActiveError = 5,
+ RecoveryInProgress = 6,
+ BusRecovered = 7,
+ ArbLost = 8,
+ AboveErrorWarning = 9,
+ BusError = 10,
+ TransmitFailed = 11,
+ ReceiveQueueFull = 12,
+ ErrorPass = 13,
+ BusOffline = 14,
+ ReceiveFifoOverflow = 15,
+ TransmitRetried = 16,
+ PeripheralReset = 17,
+ AlertAndLog = 18,
+}
-unsafe impl<'d> Send for CanDriver<'d> {}
+/// CAN abstraction
+pub struct CanDriver<'d>(PeripheralRef<'d, CAN>, EnumSet, bool);
impl<'d> CanDriver<'d> {
pub fn new(
can: impl Peripheral + 'd,
tx: impl Peripheral
+ 'd,
- rx: impl Peripheral
+ 'd,
+ rx: impl Peripheral
+ 'd,
config: &config::Config,
) -> Result {
crate::into_ref!(can, tx, rx);
+ #[allow(clippy::needless_update)]
let general_config = twai_general_config_t {
- mode: twai_mode_t_TWAI_MODE_NORMAL,
+ mode: config.mode.into(),
tx_io: tx.pin(),
rx_io: rx.pin(),
clkout_io: -1,
bus_off_io: -1,
- tx_queue_len: 5,
- rx_queue_len: 5,
- alerts_enabled: TWAI_ALERT_NONE,
+ tx_queue_len: config.tx_queue_len,
+ rx_queue_len: config.rx_queue_len,
+ alerts_enabled: config.alerts.as_repr(),
clkout_divider: 0,
- intr_flags: ESP_INTR_FLAG_LEVEL1 as i32,
+ intr_flags: InterruptType::to_native(config.intr_flags) as _,
+ ..Default::default()
};
let timing_config = config.timing.into();
// modify filter and mask to be compatible with TWAI acceptance filter
- let (filter, mask) = match config.filter {
+ let (filter, mask, single_filter) = match config.filter {
config::Filter::Standard { filter, mask } => {
- ((filter as u32) << 21, !((mask as u32) << 21))
+ ((filter as u32) << 21, !((mask as u32) << 21), true)
}
- config::Filter::Extended { filter, mask } => (filter << 3, !(mask << 3)),
+ config::Filter::Extended { filter, mask } => (filter << 3, !(mask << 3), true),
+ config::Filter::Dual {
+ filter1,
+ mask1,
+ filter2,
+ mask2,
+ } => (
+ ((filter1 as u32) << 21) | ((filter2 as u32) << 5),
+ !(((mask1 as u32) << 21) | ((mask2 as u32) << 5)),
+ false,
+ ),
};
let filter_config = twai_filter_config_t {
acceptance_code: filter,
acceptance_mask: mask,
- single_filter: true,
+ single_filter,
};
esp!(unsafe { twai_driver_install(&general_config, &timing_config, &filter_config) })?;
- esp!(unsafe { twai_start() })?;
- Ok(Self(can))
+ Ok(Self(can, config.alerts, config.tx_queue_len > 0))
+ }
+
+ pub fn start(&mut self) -> Result<(), EspError> {
+ esp!(unsafe { twai_start() })
}
- pub fn transmit(&mut self, frame: &Frame, timeout: TickType_t) -> Result<(), EspError> {
+ pub fn stop(&mut self) -> Result<(), EspError> {
+ esp!(unsafe { twai_stop() })
+ }
+
+ pub fn transmit(&self, frame: &Frame, timeout: TickType_t) -> Result<(), EspError> {
esp!(unsafe { twai_transmit(&frame.0, timeout) })
}
- pub fn receive(&mut self, timeout: TickType_t) -> Result {
- let mut rx_msg = twai_message_t {
- ..Default::default()
- };
+ pub fn receive(&self, timeout: TickType_t) -> Result {
+ let mut rx_msg = Default::default();
match esp_result!(unsafe { twai_receive(&mut rx_msg, timeout) }, ()) {
Ok(_) => Ok(Frame(rx_msg)),
Err(err) => Err(err),
}
}
+
+ pub fn read_alerts(&self, timeout: TickType_t) -> Result, EspError> {
+ let mut alerts = 0;
+
+ esp!(unsafe { twai_read_alerts(&mut alerts, timeout) })?;
+
+ Ok(EnumSet::from_repr_truncated(alerts))
+ }
}
-impl<'d> Drop for CanDriver<'d> {
+impl Drop for CanDriver<'_> {
fn drop(&mut self) {
- esp!(unsafe { twai_stop() }).unwrap();
+ let _ = self.stop();
esp!(unsafe { twai_driver_uninstall() }).unwrap();
}
}
-impl<'d> embedded_hal_0_2::blocking::can::Can for CanDriver<'d> {
+unsafe impl Send for CanDriver<'_> {}
+
+impl embedded_hal_0_2::blocking::can::Can for CanDriver<'_> {
type Frame = Frame;
type Error = Can02Error;
fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error> {
- self.transmit(frame, BLOCK).map_err(Can02Error::other)
+ CanDriver::transmit(self, frame, BLOCK).map_err(Can02Error::other)
}
fn receive(&mut self) -> Result {
- self.receive(BLOCK).map_err(Can02Error::other)
+ CanDriver::receive(self, BLOCK).map_err(Can02Error::other)
}
}
-impl<'d> embedded_can::blocking::Can for CanDriver<'d> {
+impl embedded_can::blocking::Can for CanDriver<'_> {
type Frame = Frame;
type Error = CanError;
fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error> {
- self.transmit(frame, BLOCK).map_err(CanError::other)
+ CanDriver::transmit(self, frame, BLOCK).map_err(CanError::other)
}
fn receive(&mut self) -> Result {
- self.receive(BLOCK).map_err(CanError::other)
+ CanDriver::receive(self, BLOCK).map_err(CanError::other)
}
}
-impl<'d> embedded_hal_0_2::can::nb::Can for CanDriver<'d> {
+impl embedded_hal_0_2::can::nb::Can for CanDriver<'_> {
type Frame = Frame;
type Error = Can02Error;
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result