From 8cc193aa35357e47fabf24c3558dc4c8ffae6c8e Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sun, 27 Aug 2023 10:47:18 +0300 Subject: [PATCH] TWAI driver async hack; examples cleanup (#292) * Hacky async TWAI driver * Async TWAI: Custom priority and pin to core * Async TWAI: Proper configuration of alerts * Do not refer to esp-idf-sys as it is not an explicit dependency anymore --- Cargo.toml | 1 + examples/blinky.rs | 2 +- examples/button.rs | 2 +- examples/i2c_master_slave.rs | 2 +- examples/i2c_ssd1306.rs | 2 +- examples/ledc_simple.rs | 2 +- examples/ledc_threads.rs | 2 +- examples/pcnt_i64_encoder.rs | 2 +- examples/rmt_morse_code.rs | 2 +- examples/rmt_musical_buzzer.rs | 2 +- examples/rmt_neopixel.rs | 2 +- examples/rmt_transceiver.rs | 2 - examples/spi_loopback.rs | 2 +- examples/spi_st7789.rs | 2 - examples/timer_notify.rs | 4 +- examples/uart_loopback.rs | 2 +- src/adc.rs | 4 +- src/can.rs | 436 ++++++++++++++++++--------------- src/i2s.rs | 6 +- src/interrupt.rs | 4 +- src/private.rs | 183 ++------------ src/task.rs | 46 ++++ 22 files changed, 329 insertions(+), 383 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7642406f3..42f9d9b03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ critical-section = { version = "1.1.1", optional = true } heapless = "0.7" embassy-sync = { version = "0.2" } edge-executor = { version = "0.3", optional = true, default-features = false } +num_enum = { version = "0.7", default-features = false } enumset = { version = "1", default-features = false } embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } atomic-waker = { version = "1.1.1", optional = true, default-features = false } diff --git a/examples/blinky.rs b/examples/blinky.rs index 31422f16d..ff5a0de5f 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -10,7 +10,7 @@ use esp_idf_hal::gpio::*; use esp_idf_hal::peripherals::Peripherals; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let mut led = PinDriver::output(peripherals.pins.gpio4)?; diff --git a/examples/button.rs b/examples/button.rs index 3619a6d9c..39866d4c6 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -12,7 +12,7 @@ use esp_idf_hal::gpio::*; use esp_idf_hal::peripherals::Peripherals; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let mut led = PinDriver::output(peripherals.pins.gpio4)?; diff --git a/examples/i2c_master_slave.rs b/examples/i2c_master_slave.rs index 31e04975b..4d60f5770 100644 --- a/examples/i2c_master_slave.rs +++ b/examples/i2c_master_slave.rs @@ -58,7 +58,7 @@ fn main() -> anyhow::Result<()> { #[cfg(esp32)] fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); println!("Starting I2C self test"); diff --git a/examples/i2c_ssd1306.rs b/examples/i2c_ssd1306.rs index 0de4e0e07..5d114761f 100644 --- a/examples/i2c_ssd1306.rs +++ b/examples/i2c_ssd1306.rs @@ -17,7 +17,7 @@ use esp_idf_hal::prelude::*; const SSD1306_ADDRESS: u8 = 0x3c; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let i2c = peripherals.i2c0; diff --git a/examples/ledc_simple.rs b/examples/ledc_simple.rs index cd53c06d7..545aca58c 100644 --- a/examples/ledc_simple.rs +++ b/examples/ledc_simple.rs @@ -4,7 +4,7 @@ use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::prelude::*; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); println!("Configuring output channel"); diff --git a/examples/ledc_threads.rs b/examples/ledc_threads.rs index 22a535720..c456c7343 100644 --- a/examples/ledc_threads.rs +++ b/examples/ledc_threads.rs @@ -9,7 +9,7 @@ use esp_idf_hal::prelude::*; const CYCLES: usize = 3; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); println!("Setting up PWM output channels"); diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 3c6b827d8..126b0a6f6 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -15,7 +15,7 @@ fn main() -> anyhow::Result<()> { // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once, // or else some patches to the runtime implemented by esp-idf-sys might not link properly. - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); println!("setup pins"); let peripherals = Peripherals::take().context("failed to take Peripherals")?; diff --git a/examples/rmt_morse_code.rs b/examples/rmt_morse_code.rs index 95ea01ddd..d91dca780 100644 --- a/examples/rmt_morse_code.rs +++ b/examples/rmt_morse_code.rs @@ -23,7 +23,7 @@ use esp_idf_hal::rmt::*; use esp_idf_hal::units::FromValueType; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let mut channel = peripherals.rmt.channel0; diff --git a/examples/rmt_musical_buzzer.rs b/examples/rmt_musical_buzzer.rs index dee24a392..f53687c43 100644 --- a/examples/rmt_musical_buzzer.rs +++ b/examples/rmt_musical_buzzer.rs @@ -14,7 +14,7 @@ use esp_idf_hal::rmt::*; use notes::*; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let led = peripherals.pins.gpio17; diff --git a/examples/rmt_neopixel.rs b/examples/rmt_neopixel.rs index df3a71c6a..d2c879f16 100644 --- a/examples/rmt_neopixel.rs +++ b/examples/rmt_neopixel.rs @@ -17,7 +17,7 @@ use esp_idf_hal::rmt::config::TransmitConfig; use esp_idf_hal::rmt::*; fn main() -> Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); // Onboard RGB LED pin diff --git a/examples/rmt_transceiver.rs b/examples/rmt_transceiver.rs index 396a4bdd2..2f58a8bf8 100644 --- a/examples/rmt_transceiver.rs +++ b/examples/rmt_transceiver.rs @@ -18,8 +18,6 @@ //! level0 = High dur0 = PulseTicks(210) level1 = Low dur1 = PulseTicks(0) //! Tx Loop -use esp_idf_sys::{self as _}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported - use esp_idf_hal::delay::FreeRtos; use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::rmt::{ diff --git a/examples/spi_loopback.rs b/examples/spi_loopback.rs index 4464dca7f..7fcb58ceb 100644 --- a/examples/spi_loopback.rs +++ b/examples/spi_loopback.rs @@ -19,7 +19,7 @@ use esp_idf_hal::prelude::*; use esp_idf_hal::spi::*; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let spi = peripherals.spi2; diff --git a/examples/spi_st7789.rs b/examples/spi_st7789.rs index 43d01bb27..76a3c0c75 100644 --- a/examples/spi_st7789.rs +++ b/examples/spi_st7789.rs @@ -12,8 +12,6 @@ //! For this example you need to hook up an ST7789 SPI display. //! The display will display an image on ferris the crab on a black background. -use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported - use std::thread; use std::time::Duration; diff --git a/examples/timer_notify.rs b/examples/timer_notify.rs index 9d242b57c..e7bff42db 100644 --- a/examples/timer_notify.rs +++ b/examples/timer_notify.rs @@ -1,9 +1,9 @@ -use esp_idf_sys::{self as _, EspError, TaskHandle_t}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported +use esp_idf_hal::sys::{self as _, EspError, TaskHandle_t}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported fn main() -> Result<(), EspError> { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let per = esp_idf_hal::peripherals::Peripherals::take().unwrap(); diff --git a/examples/uart_loopback.rs b/examples/uart_loopback.rs index c2d18bc8c..8a99f09cb 100644 --- a/examples/uart_loopback.rs +++ b/examples/uart_loopback.rs @@ -16,7 +16,7 @@ use esp_idf_hal::prelude::*; use esp_idf_hal::uart::*; fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let tx = peripherals.pins.gpio5; diff --git a/src/adc.rs b/src/adc.rs index 57366893c..a0dc9cb4c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -909,7 +909,9 @@ pub mod continuous { 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, + _ => { + NOTIFIER[self.adc as usize].wait().await; + } } } } diff --git a/src/can.rs b/src/can.rs index 0195d4849..29d6052e9 100644 --- a/src/can.rs +++ b/src/can.rs @@ -32,12 +32,22 @@ //! } //! ``` +use core::borrow::BorrowMut; +use core::ffi::CStr; +use core::marker::PhantomData; + +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::IntrFlags; use crate::peripheral::{Peripheral, PeripheralRef}; +use crate::private::notification::Notification; +use crate::{gpio::*, task}; crate::embedded_hal_error!(CanError, embedded_can::Error, embedded_can::ErrorKind); @@ -53,7 +63,9 @@ pub mod config { use enumset::EnumSet; use esp_idf_sys::*; - use crate::interrupt::IntrFlags; + use crate::interrupt::{InterruptType, IntrFlags}; + + use super::Alert; /// CAN timing #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -257,186 +269,6 @@ pub mod config { } } - #[derive(Debug, Copy, Clone)] - pub struct Alerts { - value: u32, - } - - impl Alerts { - const TWAI_ALERT_TX_SUCCESS: u32 = 0x00000002; - const TWAI_ALERT_TX_IDLE: u32 = 0x00000001; - const TWAI_ALERT_RX_DATA: u32 = 0x00000004; - const TWAI_ALERT_BELOW_ERR_WARN: u32 = 0x00000008; - const TWAI_ALERT_ERR_ACTIVE: u32 = 0x00000010; - const TWAI_ALERT_RECOVERY_IN_PROGRESS: u32 = 0x00000020; - const TWAI_ALERT_BUS_RECOVERED: u32 = 0x00000040; - const TWAI_ALERT_ARB_LOST: u32 = 0x00000080; - const TWAI_ALERT_ABOVE_ERR_WARN: u32 = 0x00000100; - const TWAI_ALERT_BUS_ERROR: u32 = 0x00000200; - const TWAI_ALERT_TX_FAILED: u32 = 0x00000400; - const TWAI_ALERT_RX_QUEUE_FULL: u32 = 0x00000800; - const TWAI_ALERT_ERR_PASS: u32 = 0x00001000; - const TWAI_ALERT_BUS_OFF: u32 = 0x00002000; - const TWAI_ALERT_RX_FIFO_OVERRUN: u32 = 0x00004000; - const TWAI_ALERT_TX_RETRIED: u32 = 0x00008000; - const TWAI_ALERT_PERIPH_RESET: u32 = 0x00010000; - const TWAI_ALERT_ALL: u32 = 0x0001FFFF; - const TWAI_ALERT_NONE: u32 = 0x00000000; - const TWAI_ALERT_AND_LOG: u32 = 0x00020000; - - /// Create new `Alerts` with no alerts set. - pub const fn new() -> Self { - Self { - value: Self::TWAI_ALERT_NONE, - } - } - - /// Enable the `TX_SUCCESS` alert: The previous transmission was successful. - pub fn tx_success(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_TX_SUCCESS, - } - } - - /// Enable the `TX_IDLE` alert: No more messages to transmit. - pub fn tx_idle(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_TX_IDLE, - } - } - - /// Enable the `RX_DATA` alert: A frame has been received and added to the RX queue. - pub fn rx_data(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_RX_DATA, - } - } - - /// Enable the `BELOW_ERR_WARN` alert: Both error counters have dropped below error warning limit. - pub fn below_err_warn(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_BELOW_ERR_WARN, - } - } - - /// Enable the `ERR_ACTIVE` alert: TWAI controller has become error active. - pub fn err_active(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_ERR_ACTIVE, - } - } - - /// Enable the `RECOVERY_IN_PROGRESS` alert: TWAI controller is undergoing bus recovery. - pub fn recovery_in_progress(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_RECOVERY_IN_PROGRESS, - } - } - - /// Enable the `BUS_RECOVERED` alert: TWAI controller has successfully completed bus recovery. - pub fn bus_recovered(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_BUS_RECOVERED, - } - } - - /// Enable the `ARB_LOST` alert: The previous transmission lost arbitration. - pub fn arb_lost(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_ARB_LOST, - } - } - - /// Enable the `ABOVE_ERR_WARN` alert: One of the error counters have exceeded the error warning limit. - pub fn above_err_warn(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_ABOVE_ERR_WARN, - } - } - - /// Enable the `BUS_ERROR` alert: A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus. - pub fn bus_error(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_BUS_ERROR, - } - } - - /// Enable the `TX_FAILED` alert: The previous transmission has failed (for single shot transmission). - pub fn tx_failed(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_TX_FAILED, - } - } - - /// Enable the `RX_QUEUE_FULL` alert: The RX queue is full causing a frame to be lost. - pub fn rx_queue_full(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_RX_QUEUE_FULL, - } - } - - /// Enable the `ERR_PASS` alert: TWAI controller has become error passive. - pub fn err_pass(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_ERR_PASS, - } - } - - /// Enable the `BUS_OFF` alert: Bus-off condition occurred. TWAI controller can no longer influence bus. - pub fn bus_off(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_BUS_OFF, - } - } - - /// Enable the `RX_FIFO_OVERRUN` alert: An RX FIFO overrun has occurred. - pub fn rx_fifo_overrun(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_RX_FIFO_OVERRUN, - } - } - - /// Enable the `TX_RETRIED` alert: An message transmission was cancelled and retried due to an errata workaround. - pub fn tx_retried(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_TX_RETRIED, - } - } - - /// Enable the `PERIPH_RESET` alert: The TWAI controller was reset. - pub fn periph_reset(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_PERIPH_RESET, - } - } - - /// Enable all alerts. - pub fn all(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_ALL, - } - } - - /// Enable alert logging when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled. - pub fn and_log(self) -> Self { - Self { - value: self.value | Self::TWAI_ALERT_AND_LOG, - } - } - } - - impl From for u32 { - fn from(val: Alerts) -> Self { - val.value - } - } - - impl Default for Alerts { - fn default() -> Self { - Self::new() - } - } - #[derive(Debug, Clone)] pub struct Config { pub timing: Timing, @@ -444,8 +276,8 @@ pub mod config { pub tx_queue_len: u32, pub rx_queue_len: u32, pub mode: Mode, - pub alerts: Alerts, - pub intr_flags: EnumSet, + pub alerts: EnumSet, + pub intr_flags: EnumSet, } impl Config { @@ -492,7 +324,7 @@ pub mod config { } #[must_use] - pub fn alerts(mut self, alerts: Alerts) -> Self { + pub fn alerts(mut self, alerts: EnumSet) -> Self { self.alerts = alerts; self } @@ -511,10 +343,32 @@ pub mod config { } } -/// 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); impl<'d> CanDriver<'d> { pub fn new( @@ -533,7 +387,7 @@ impl<'d> CanDriver<'d> { bus_off_io: -1, tx_queue_len: config.tx_queue_len, rx_queue_len: config.rx_queue_len, - alerts_enabled: config.alerts.into(), + alerts_enabled: config.alerts.as_repr(), clkout_divider: 0, intr_flags: IntrFlags::to_native(config.intr_flags) as _, }; @@ -555,16 +409,23 @@ impl<'d> CanDriver<'d> { }; esp!(unsafe { twai_driver_install(&general_config, &timing_config, &filter_config) })?; - esp!(unsafe { twai_start() })?; - Ok(Self(can)) + Ok(Self(can, config.alerts)) } - pub fn transmit(&mut self, frame: &Frame, timeout: TickType_t) -> Result<(), EspError> { + pub fn start(&mut self) -> Result<(), EspError> { + esp!(unsafe { twai_start() }) + } + + 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 { + pub fn receive(&self, timeout: TickType_t) -> Result { let mut rx_msg = Default::default(); match esp_result!(unsafe { twai_receive(&mut rx_msg, timeout) }, ()) { @@ -572,25 +433,35 @@ impl<'d> CanDriver<'d> { 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> { fn drop(&mut self) { - esp!(unsafe { twai_stop() }).unwrap(); + let _ = self.stop(); esp!(unsafe { twai_driver_uninstall() }).unwrap(); } } +unsafe impl<'d> Send for CanDriver<'d> {} + impl<'d> embedded_hal_0_2::blocking::can::Can for CanDriver<'d> { 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) } } @@ -599,11 +470,11 @@ impl<'d> embedded_can::blocking::Can for CanDriver<'d> { 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) } } @@ -612,7 +483,7 @@ impl<'d> embedded_hal_0_2::can::nb::Can for CanDriver<'d> { type Error = Can02Error; fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { - match self.transmit(frame, NON_BLOCK) { + match CanDriver::transmit(self, frame, NON_BLOCK) { Ok(_) => Ok(None), Err(e) if e.code() == ESP_FAIL => Err(nb::Error::WouldBlock), Err(e) if e.code() == ESP_ERR_TIMEOUT => Err(nb::Error::WouldBlock), @@ -621,7 +492,7 @@ impl<'d> embedded_hal_0_2::can::nb::Can for CanDriver<'d> { } fn receive(&mut self) -> nb::Result { - match self.receive(NON_BLOCK) { + match CanDriver::receive(self, NON_BLOCK) { Ok(frame) => Ok(frame), Err(e) if e.code() == ESP_ERR_TIMEOUT => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(Can02Error::other(e))), @@ -634,7 +505,7 @@ impl<'d> embedded_can::nb::Can for CanDriver<'d> { type Error = CanError; fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { - match self.transmit(frame, NON_BLOCK) { + match CanDriver::transmit(self, frame, NON_BLOCK) { Ok(_) => Ok(None), Err(e) if e.code() == ESP_FAIL => Err(nb::Error::WouldBlock), Err(e) if e.code() == ESP_ERR_TIMEOUT => Err(nb::Error::WouldBlock), @@ -643,7 +514,7 @@ impl<'d> embedded_can::nb::Can for CanDriver<'d> { } fn receive(&mut self) -> nb::Result { - match self.receive(NON_BLOCK) { + match CanDriver::receive(self, NON_BLOCK) { Ok(frame) => Ok(frame), Err(e) if e.code() == ESP_ERR_TIMEOUT => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(CanError::other(e))), @@ -651,6 +522,165 @@ impl<'d> embedded_can::nb::Can for CanDriver<'d> { } } +fn read_alerts() -> EnumSet { + Alert::Success | Alert::Received | Alert::ReceiveQueueFull +} + +fn write_alerts() -> EnumSet { + Alert::Success | Alert::TransmitIdle | Alert::TransmitFailed | Alert::TransmitRetried +} + +pub struct AsyncCanDriver<'d, T> +where + T: BorrowMut>, +{ + driver: T, + task: TaskHandle_t, + _data: PhantomData<&'d ()>, +} + +impl<'d> AsyncCanDriver<'d, CanDriver<'d>> { + pub fn new( + can: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + config: &config::Config, + ) -> Result { + Self::wrap(CanDriver::new(can, tx, rx, config)?) + } +} + +impl<'d, T> AsyncCanDriver<'d, T> +where + T: BorrowMut>, +{ + pub fn wrap(driver: T) -> Result { + Self::wrap_custom(driver, None, None) + } + + pub fn wrap_custom( + mut driver: T, + priority: Option, + pin_to_core: Option, + ) -> Result { + let _ = driver.borrow_mut().stop(); + + let mut alerts = 0; + esp!(unsafe { + twai_reconfigure_alerts( + driver + .borrow() + .1 + .union(read_alerts()) + .union(write_alerts()) + .as_repr(), + &mut alerts, + ) + })?; + + let task = unsafe { + task::create( + Self::process_alerts, + CStr::from_bytes_until_nul(b"CAN - Alerts task\0").unwrap(), + 2048, + core::ptr::null_mut(), + priority.unwrap_or(5), + pin_to_core, + )? + }; + + Ok(Self { + driver, + task, + _data: PhantomData, + }) + } + + pub fn start(&mut self) -> Result<(), EspError> { + self.driver.borrow_mut().start() + } + + pub fn stop(&mut self) -> Result<(), EspError> { + self.driver.borrow_mut().stop() + } + + pub async fn transmit(&self, frame: &Frame) -> Result<(), EspError> { + loop { + match self.driver.borrow().transmit(frame, delay::NON_BLOCK) { + Ok(()) => return Ok(()), + Err(e) if e.code() != ESP_ERR_TIMEOUT => return Err(e), + _ => (), + } + + WRITE_NOTIFICATION.wait().await; + } + } + + pub async fn receive(&self) -> Result { + loop { + match self.driver.borrow().receive(delay::NON_BLOCK) { + Ok(frame) => return Ok(frame), + Err(e) if e.code() != ESP_ERR_TIMEOUT => return Err(e), + _ => (), + } + + READ_NOTIFICATION.wait().await; + } + } + + pub async fn read_alerts(&self) -> Result, EspError> { + let alerts = loop { + let alerts = EnumSet::from_repr(ALERT_NOTIFICATION.wait().await) + .intersection(self.driver.borrow().1); + + if !alerts.is_empty() { + break alerts; + } + }; + + Ok(alerts) + } + + extern "C" fn process_alerts(_arg: *mut core::ffi::c_void) { + let mut alerts = 0; + + loop { + if unsafe { twai_read_alerts(&mut alerts, delay::BLOCK) } == 0 { + let ealerts: EnumSet = EnumSet::from_repr_truncated(alerts); + + if !ealerts.is_disjoint(read_alerts()) { + READ_NOTIFICATION.notify(); + } + + if !ealerts.is_disjoint(write_alerts()) { + WRITE_NOTIFICATION.notify(); + } + + ALERT_NOTIFICATION.signal(alerts); + } + } + } +} + +impl<'d, T> Drop for AsyncCanDriver<'d, T> +where + T: BorrowMut>, +{ + fn drop(&mut self) { + let _ = self.stop(); + + unsafe { task::destroy(self.task) }; + + let mut alerts = 0; + esp!(unsafe { twai_reconfigure_alerts(self.driver.borrow().1.as_repr(), &mut alerts) }) + .unwrap(); + } +} + +static READ_NOTIFICATION: Notification = Notification::new(); +static WRITE_NOTIFICATION: Notification = Notification::new(); +static ALERT_NOTIFICATION: Notification = Notification::new(); + pub struct Frame(twai_message_t); impl Frame { diff --git a/src/i2s.rs b/src/i2s.rs index 5df78bf26..11b76ec86 100644 --- a/src/i2s.rs +++ b/src/i2s.rs @@ -777,7 +777,7 @@ where loop { match self.read(buffer, crate::delay::NON_BLOCK) { Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => { - RECV_NOTIFIER[self.port as usize].wait().await + RECV_NOTIFIER[self.port as usize].wait().await; } other => break other, } @@ -857,7 +857,7 @@ where loop { match self.read_uninit(buffer, crate::delay::NON_BLOCK) { Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => { - RECV_NOTIFIER[self.port as usize].wait().await + RECV_NOTIFIER[self.port as usize].wait().await; } other => break other, } @@ -1052,7 +1052,7 @@ where loop { match self.write(data, crate::delay::NON_BLOCK) { Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => { - SEND_NOTIFIER[self.port as usize].wait().await + SEND_NOTIFIER[self.port as usize].wait().await; } other => break other, } diff --git a/src/interrupt.rs b/src/interrupt.rs index f5c7669ee..34453c577 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -3,10 +3,12 @@ use core::sync::atomic::{AtomicU64, Ordering}; use enumset::{EnumSet, EnumSetType}; use esp_idf_sys::*; +pub type IntrFlags = InterruptType; + /// Interrupt allocation flags. /// These flags can be used to specify which interrupt qualities the code calling esp_intr_alloc* needs. #[derive(Debug, EnumSetType)] -pub enum IntrFlags { +pub enum InterruptType { // Accept a Level 1 interrupt vector (lowest priority) Level1, // Accept a Level 2 interrupt vector. diff --git a/src/private.rs b/src/private.rs index 13892e586..d7a4cbbaf 100644 --- a/src/private.rs +++ b/src/private.rs @@ -1,199 +1,68 @@ pub mod notification { - // use core::cell::UnsafeCell; use core::future::Future; - use core::sync::atomic::{AtomicBool, Ordering}; + use core::sync::atomic::{AtomicU32, Ordering}; use core::task::{Context, Poll}; use atomic_waker::AtomicWaker; - // use crate::interrupt::IsrCriticalSection; - - // /// Utility struct to register and wake a waker. - // #[derive(Debug, Default)] - // pub struct WakerRegistration { - // waker: Option, - // } - - // impl WakerRegistration { - // /// Create a new `WakerRegistration`. - // pub const fn new() -> Self { - // Self { waker: None } - // } - - // /// Register a waker. Overwrites the previous waker, if any. - // pub fn register(&mut self, w: &Waker) { - // match self.waker { - // // Optimization: If both the old and new Wakers wake the same task, we can simply - // // keep the old waker, skipping the clone. (In most executor implementations, - // // cloning a waker is somewhat expensive, comparable to cloning an Arc). - // Some(ref w2) if (w2.will_wake(w)) => {} - // _ => { - // // clone the new waker and store it - // if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { - // // We had a waker registered for another task. Wake it, so the other task can - // // reregister itself if it's still interested. - // // - // // If two tasks are waiting on the same thing concurrently, this will cause them - // // to wake each other in a loop fighting over this WakerRegistration. This wastes - // // CPU but things will still work. - // // - // // If the user wants to have two tasks waiting on the same thing they should use - // // a more appropriate primitive that can store multiple wakers. - // old_waker.wake() - // } - // } - // } - // } - - // /// Wake the registered waker, if any. - // pub fn wake(&mut self) { - // if let Some(w) = self.waker.take() { - // w.wake() - // } - // } - - // /// Returns true if a waker is currently registered - // pub fn occupied(&self) -> bool { - // self.waker.is_some() - // } - // } - - // /// Utility struct to register and wake multiple wakers. - // pub struct MultiWakerRegistration { - // wakers: [WakerRegistration; N], - // } - - // impl MultiWakerRegistration { - // /// Create a new empty instance - // pub const fn new() -> Self { - // const WAKER: WakerRegistration = WakerRegistration::new(); - // Self { wakers: [WAKER; N] } - // } - - // /// Register a waker. If the buffer is full the function returns it in the error - // pub fn register<'a>(&mut self, w: &'a Waker) -> Result<(), &'a Waker> { - // if let Some(waker_slot) = self - // .wakers - // .iter_mut() - // .find(|waker_slot| !waker_slot.occupied()) - // { - // waker_slot.register(w); - // Ok(()) - // } else { - // Err(w) - // } - // } - - // /// Wake all registered wakers. This clears the buffer - // pub fn wake(&mut self) -> bool { - // let mut woken = false; - - // for waker_slot in self.wakers.iter_mut() { - // woken = waker_slot.occupied(); - // waker_slot.wake(); - // } - - // woken - // } - // } - pub struct Notification { waker: AtomicWaker, - notified: AtomicBool, + notified: AtomicU32, } impl Notification { pub const fn new() -> Self { + Self::new_with_value(0) + } + + pub const fn new_with_value(value: u32) -> Self { Self { waker: AtomicWaker::new(), - notified: AtomicBool::new(false), + notified: AtomicU32::new(value), } } pub fn notify(&self) -> bool { - self.notified.store(true, Ordering::SeqCst); + self.signal(1) + } - if let Some(waker) = self.waker.take() { - waker.wake(); + pub fn signal(&self, value: u32) -> bool { + if value != 0 { + self.notified.fetch_or(value, Ordering::SeqCst); - true + if let Some(waker) = self.waker.take() { + waker.wake(); + + true + } else { + false + } } else { false } } pub fn clear(&self) { - self.notified.store(false, Ordering::SeqCst); + self.notified.store(0, Ordering::SeqCst); } #[allow(unused)] - pub fn wait(&self) -> impl Future + '_ { + pub fn wait(&self) -> impl Future + '_ { core::future::poll_fn(move |cx| self.poll_wait(cx)) } - pub fn poll_wait(&self, cx: &Context<'_>) -> Poll<()> { + pub fn poll_wait(&self, cx: &Context<'_>) -> Poll { self.waker.register(cx.waker()); - if self.notified.swap(false, Ordering::SeqCst) { - Poll::Ready(()) + let value = self.notified.swap(0, Ordering::SeqCst); + + if value != 0 { + Poll::Ready(value) } else { Poll::Pending } } } - - // pub struct MultiNotificationInner { - // waker: MultiWakerRegistration, - // notified: bool, - // } - - // pub struct MultiNotification { - // cs: IsrCriticalSection, - // inner: UnsafeCell>, - // } - - // impl MultiNotification { - // pub const fn new() -> Self { - // Self { - // cs: IsrCriticalSection::new(), - // inner: UnsafeCell::new(MultiNotificationInner { - // waker: MultiWakerRegistration::new(), - // notified: false, - // }), - // } - // } - - // pub fn notify(&self) -> bool { - // let _cs = self.cs.enter(); - - // let inner = unsafe { self.inner.get().as_mut() }.unwrap(); - - // inner.notified = true; - - // inner.waker.wake() - // } - - // pub fn wait(&self) -> impl Future + '_ { - // core::future::poll_fn(move |cx| self.poll_wait(cx)) - // } - - // pub fn poll_wait(&self, cx: &Context<'_>) -> Poll<()> { - // let _cs = self.cs.enter(); - - // let inner = unsafe { self.inner.get().as_mut() }.unwrap(); - - // inner.waker.register(cx.waker()).unwrap(); - - // if inner.notified { - // Poll::Ready(()) - // } else { - // Poll::Pending - // } - // } - // } - - // unsafe impl Send for MultiNotification {} - // unsafe impl Sync for MultiNotification {} } pub mod completion { diff --git a/src/task.rs b/src/task.rs index a6248421d..c1707f673 100644 --- a/src/task.rs +++ b/src/task.rs @@ -5,9 +5,55 @@ use core::time::Duration; use esp_idf_sys::*; +use crate::cpu::Core; use crate::delay::TickType; use crate::interrupt; +/// Creates a FreeRTOS task. +/// +/// This API is to be used only for niche use cases like where the `std` feature is not enabled, or one absolutely +/// needs to create a raw FreeRTOS task. +/// +/// In all other cases, the standard, safe Rust `std::thread` API should be utilized, as it is anyway +/// a thin wrapper around the FreeRTOS task API. +pub unsafe fn create( + task_handler: extern "C" fn(*mut core::ffi::c_void), + task_name: &'static core::ffi::CStr, + stack_size: usize, + task_arg: *mut core::ffi::c_void, + priority: u8, + pin_to_core: Option, +) -> Result { + let mut task: TaskHandle_t = core::ptr::null_mut(); + + let created = xTaskCreatePinnedToCore( + Some(task_handler), + task_name.as_ptr(), + stack_size as _, + task_arg, + priority as _, + &mut task, + pin_to_core.map(Into::into).unwrap_or(tskNO_AFFINITY as _), + ); + + if created == 0 { + Err(EspError::from_infallible::()) // TODO + } else { + Ok(task) + } +} + +/// Deletes a FreeRTOS task. +/// +/// This API is to be used only for niche use cases like where the `std` feature is not enabled, or one absolutely +/// needs to create a raw FreeRTOS task. +/// +/// In all other cases, the standard, safe Rust `std::thread` API should be utilized, as it is anyway +/// a thin wrapper around the FreeRTOS task API. +pub unsafe fn destroy(task: TaskHandle_t) { + vTaskDelete(task) +} + #[inline(always)] #[link_section = ".iram1.interrupt_task_do_yield"] pub fn do_yield() {