mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-27 04:10:30 +00:00
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:
parent
b92764159a
commit
8cc193aa35
@ -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 }
|
||||
|
@ -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)?;
|
||||
|
@ -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)?;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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")?;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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::{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
436
src/can.rs
436
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<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 {
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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.
|
||||
|
183
src/private.rs
183
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<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 {
|
||||
|
46
src/task.rs
46
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<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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user