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
This commit is contained in:
ivmarkov 2023-08-27 10:47:18 +03:00 committed by GitHub
parent b92764159a
commit 8cc193aa35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 329 additions and 383 deletions

View File

@ -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 }

View File

@ -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)?;

View File

@ -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)?;

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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")?;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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::{

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;
}
}
}
}

View File

@ -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<Alerts> 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<IntrFlags>,
pub alerts: EnumSet<Alert>,
pub intr_flags: EnumSet<InterruptType>,
}
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<Alert>) -> 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<Alert>);
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<Frame, EspError> {
pub fn receive(&self, timeout: TickType_t) -> Result<Frame, EspError> {
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<EnumSet<Alert>, 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::Frame, Self::Error> {
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::Frame, Self::Error> {
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<Option<Self::Frame>, 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<Self::Frame, Self::Error> {
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<Option<Self::Frame>, 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<Self::Frame, Self::Error> {
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> {
Alert::Success | Alert::Received | Alert::ReceiveQueueFull
}
fn write_alerts() -> EnumSet<Alert> {
Alert::Success | Alert::TransmitIdle | Alert::TransmitFailed | Alert::TransmitRetried
}
pub struct AsyncCanDriver<'d, T>
where
T: BorrowMut<CanDriver<'d>>,
{
driver: T,
task: TaskHandle_t,
_data: PhantomData<&'d ()>,
}
impl<'d> AsyncCanDriver<'d, CanDriver<'d>> {
pub fn new(
can: impl Peripheral<P = CAN> + 'd,
tx: impl Peripheral<P = impl OutputPin> + 'd,
rx: impl Peripheral<P = impl OutputPin> + 'd,
config: &config::Config,
) -> Result<Self, EspError> {
Self::wrap(CanDriver::new(can, tx, rx, config)?)
}
}
impl<'d, T> AsyncCanDriver<'d, T>
where
T: BorrowMut<CanDriver<'d>>,
{
pub fn wrap(driver: T) -> Result<Self, EspError> {
Self::wrap_custom(driver, None, None)
}
pub fn wrap_custom(
mut driver: T,
priority: Option<u8>,
pin_to_core: Option<Core>,
) -> Result<Self, EspError> {
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<Frame, EspError> {
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<EnumSet<Alert>, 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<Alert> = 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<CanDriver<'d>>,
{
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 {

View File

@ -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,
}

View File

@ -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.

View File

@ -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<Waker>,
// }
// 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<const N: usize> {
// wakers: [WakerRegistration; N],
// }
// impl<const N: usize> MultiWakerRegistration<N> {
// /// 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<Output = ()> + '_ {
pub fn wait(&self) -> impl Future<Output = u32> + '_ {
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<u32> {
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<const N: usize> {
// waker: MultiWakerRegistration<N>,
// notified: bool,
// }
// pub struct MultiNotification<const N: usize> {
// cs: IsrCriticalSection,
// inner: UnsafeCell<MultiNotificationInner<N>>,
// }
// impl<const N: usize> MultiNotification<N> {
// 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<Output = ()> + '_ {
// 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<const N: usize> Send for MultiNotification<N> {}
// unsafe impl<const N: usize> Sync for MultiNotification<N> {}
}
pub mod completion {

View File

@ -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<Core>,
) -> Result<TaskHandle_t, EspError> {
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::<ESP_ERR_INVALID_ARG>()) // 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() {