mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-29 05:12:09 +00:00
Hardware timers
Use only ISR terminology Remove unused imports Simplify subscription code timer subscription callbacks No need for e-hal timer error More meaningful timer periph names for esp32c3 embassy-time driver
This commit is contained in:
parent
4dfe2f1bcd
commit
67f2484966
@ -14,12 +14,16 @@ documentation = "https://esp-rs.github.io/esp-idf-hal/"
|
||||
rust-version = "1.61"
|
||||
|
||||
[features]
|
||||
default = ["std", "alloc", "esp-idf-sys"]
|
||||
default = ["std", "alloc", "esp-idf-sys", "embassy-time-timer00"]
|
||||
std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"]
|
||||
alloc = []
|
||||
critical-section-interrupt = ["critical-section"]
|
||||
critical-section-mutex = ["critical-section"]
|
||||
riscv-ulp-hal = []
|
||||
embassy-time-timer00 = ["embassy-time"]
|
||||
embassy-time-timer01 = ["embassy-time"]
|
||||
embassy-time-timer10 = ["embassy-time"]
|
||||
embassy-time-timer11 = ["embassy-time"]
|
||||
|
||||
[dependencies]
|
||||
nb = "0.1.2"
|
||||
@ -28,6 +32,7 @@ embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["u
|
||||
embedded-svc = { version = "0.22", optional = true, default-features = false }
|
||||
esp-idf-sys = { version = "0.31.9", optional = true, default-features = false, features = ["native"] }
|
||||
critical-section = { version = "1.1", optional = true }
|
||||
embassy-time = { version = "0.1", optional = true, features = ["tick-hz-1_000_000"], git = "https://github.com/embassy-rs/embassy" }
|
||||
edge-executor = { version = "0.2", optional = true, default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
|
151
src/gpio.rs
151
src/gpio.rs
@ -990,16 +990,28 @@ impl<'d, T: Pin, MODE> PinDriver<'d, T, MODE> {
|
||||
/// Care should be taken not to call STD, libc or FreeRTOS APIs (except for a few allowed ones)
|
||||
/// in the callback passed to this function, as it is executed in an ISR context.
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
pub unsafe fn subscribe(
|
||||
&mut self,
|
||||
callback: impl FnMut() + Send + 'static,
|
||||
) -> Result<(), EspError>
|
||||
pub unsafe fn subscribe(&mut self, callback: impl FnMut() + 'static) -> Result<(), EspError>
|
||||
where
|
||||
MODE: InputMode,
|
||||
{
|
||||
let callback = PinNotifySubscription::subscribe(self.pin.pin(), callback)?;
|
||||
enable_isr_service()?;
|
||||
|
||||
register_irq_handler(self.pin.pin() as usize, callback);
|
||||
self.unsubscribe()?;
|
||||
|
||||
let callback: Box<dyn FnMut() + 'static> = Box::new(callback);
|
||||
|
||||
chip::ISR_HANDLERS[self.pin.pin() as usize] = Some(Box::new(callback));
|
||||
|
||||
esp!(gpio_isr_handler_add(
|
||||
self.pin.pin(),
|
||||
Some(Self::handle_isr),
|
||||
UnsafeCallback::from(
|
||||
chip::ISR_HANDLERS[self.pin.pin() as usize]
|
||||
.as_mut()
|
||||
.unwrap(),
|
||||
)
|
||||
.as_ptr(),
|
||||
))?;
|
||||
|
||||
self.enable_interrupt()?;
|
||||
|
||||
@ -1011,9 +1023,9 @@ impl<'d, T: Pin, MODE> PinDriver<'d, T, MODE> {
|
||||
where
|
||||
MODE: InputMode,
|
||||
{
|
||||
esp!(unsafe { gpio_intr_disable(self.pin.pin()) })?;
|
||||
esp!(unsafe { gpio_set_intr_type(self.pin.pin(), gpio_int_type_t_GPIO_INTR_DISABLE) })?;
|
||||
unsafe { unregister_irq_handler(self.pin.pin() as usize) };
|
||||
unsafe {
|
||||
unsubscribe_pin(self.pin.pin())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1023,7 +1035,9 @@ impl<'d, T: Pin, MODE> PinDriver<'d, T, MODE> {
|
||||
where
|
||||
MODE: InputMode,
|
||||
{
|
||||
esp_result!(unsafe { gpio_intr_enable(self.pin.pin()) }, ())
|
||||
esp!(unsafe { gpio_intr_enable(self.pin.pin()) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
@ -1041,43 +1055,44 @@ impl<'d, T: Pin, MODE> PinDriver<'d, T, MODE> {
|
||||
where
|
||||
MODE: InputMode,
|
||||
{
|
||||
esp_result!(
|
||||
unsafe { gpio_set_intr_type(self.pin.pin(), interrupt_type.into()) },
|
||||
()
|
||||
)
|
||||
esp!(unsafe { gpio_set_intr_type(self.pin.pin(), interrupt_type.into()) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
unsafe extern "C" fn handle_isr(unsafe_callback: *mut c_types::c_void) {
|
||||
let mut unsafe_callback = UnsafeCallback::from_ptr(unsafe_callback);
|
||||
unsafe_callback.call();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin, MODE> Drop for PinDriver<'d, T, MODE> {
|
||||
fn drop(&mut self) {
|
||||
reset_pin(self.pin.pin(), gpio_mode_t_GPIO_MODE_DISABLE).unwrap();
|
||||
unsafe { reset_pin(self.pin.pin(), gpio_mode_t_GPIO_MODE_DISABLE) }.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Pin, MODE> Send for PinDriver<'d, T, MODE> {}
|
||||
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub(crate) fn rtc_reset_pin(pin: i32) -> Result<(), EspError> {
|
||||
pub(crate) unsafe fn rtc_reset_pin(pin: i32) -> Result<(), EspError> {
|
||||
reset_pin(pin, gpio_mode_t_GPIO_MODE_DISABLE)?;
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), not(esp32c3)))]
|
||||
esp!(unsafe { rtc_gpio_init(pin) })?;
|
||||
esp!(rtc_gpio_init(pin))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_pin(pin: i32, mode: gpio_mode_t) -> Result<(), EspError> {
|
||||
unsafe fn reset_pin(pin: i32, mode: gpio_mode_t) -> Result<(), EspError> {
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
let res = {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
esp!(unsafe { gpio_intr_disable(pin) })?;
|
||||
esp!(unsafe { gpio_set_intr_type(pin, gpio_int_type_t_GPIO_INTR_DISABLE) })?;
|
||||
unsafe { unregister_irq_handler(pin as usize) };
|
||||
}
|
||||
unsubscribe_pin(pin)?;
|
||||
|
||||
esp!(unsafe { gpio_reset_pin(pin) })?;
|
||||
esp!(unsafe { gpio_set_direction(pin, mode) })?;
|
||||
esp!(gpio_reset_pin(pin))?;
|
||||
esp!(gpio_set_direction(pin, mode))?;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
@ -1088,6 +1103,21 @@ fn reset_pin(pin: i32, mode: gpio_mode_t) -> Result<(), EspError> {
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
unsafe fn unsubscribe_pin(pin: i32) -> Result<(), EspError> {
|
||||
let subscribed = chip::ISR_HANDLERS[pin as usize].is_some();
|
||||
|
||||
if subscribed {
|
||||
esp!(gpio_intr_disable(pin))?;
|
||||
esp!(gpio_set_intr_type(pin, gpio_int_type_t_GPIO_INTR_DISABLE))?;
|
||||
esp!(gpio_isr_handler_remove(pin))?;
|
||||
|
||||
chip::ISR_HANDLERS[pin as usize] = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'d, T: Pin, MODE> embedded_hal_0_2::digital::v2::InputPin for PinDriver<'d, T, MODE>
|
||||
where
|
||||
MODE: InputMode,
|
||||
@ -1227,12 +1257,6 @@ static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool =
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
static ISR_SERVICE_ENABLED_CS: crate::cs::CriticalSection = crate::cs::CriticalSection::new();
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
unsafe extern "C" fn irq_handler(unsafe_callback: *mut esp_idf_sys::c_types::c_void) {
|
||||
let mut unsafe_callback = UnsafeCallback::from_ptr(unsafe_callback);
|
||||
unsafe_callback.call();
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
fn enable_isr_service() -> Result<(), EspError> {
|
||||
use core::sync::atomic::Ordering;
|
||||
@ -1241,7 +1265,7 @@ fn enable_isr_service() -> Result<(), EspError> {
|
||||
let _ = ISR_SERVICE_ENABLED_CS.enter();
|
||||
|
||||
if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) {
|
||||
esp!(unsafe { esp_idf_sys::gpio_install_isr_service(0) })?;
|
||||
esp!(unsafe { gpio_install_isr_service(0) })?;
|
||||
|
||||
ISR_SERVICE_ENABLED.store(true, Ordering::SeqCst);
|
||||
}
|
||||
@ -1250,63 +1274,6 @@ fn enable_isr_service() -> Result<(), EspError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
type ClosureBox = Box<Box<dyn FnMut()>>;
|
||||
|
||||
/// The PinNotifySubscription represents the association between an InputPin and
|
||||
/// a registered isr handler.
|
||||
/// When the PinNotifySubscription is dropped, the isr handler is unregistered.
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
pub(crate) struct PinNotifySubscription(i32, ClosureBox);
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
impl PinNotifySubscription {
|
||||
fn subscribe(pin: i32, callback: impl FnMut() + 'static) -> Result<Self, EspError> {
|
||||
enable_isr_service()?;
|
||||
|
||||
let callback: Box<dyn FnMut() + 'static> = Box::new(callback);
|
||||
let mut callback = Box::new(callback);
|
||||
|
||||
let unsafe_callback = UnsafeCallback::from(&mut callback);
|
||||
|
||||
esp!(unsafe {
|
||||
esp_idf_sys::gpio_isr_handler_add(pin, Some(irq_handler), unsafe_callback.as_ptr())
|
||||
})?;
|
||||
|
||||
Ok(Self(pin, callback))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
impl Drop for PinNotifySubscription {
|
||||
fn drop(self: &mut PinNotifySubscription) {
|
||||
esp!(unsafe { esp_idf_sys::gpio_isr_handler_remove(self.0) }).expect("Error unsubscribing");
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// - Access to `IRQ_HANDLERS` is not guarded because we only call register
|
||||
/// from the context of Pin which is owned and in the rights state
|
||||
///
|
||||
// Clippy in the CI seems to wrongfully catch a only_used_in_recursion
|
||||
// lint error in this function. We'll ignore it until it's fixed.
|
||||
#[allow(clippy::only_used_in_recursion)]
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
unsafe fn register_irq_handler(pin_number: usize, p: PinNotifySubscription) {
|
||||
chip::IRQ_HANDLERS[pin_number] = Some(p);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// - Access to `IRQ_HANDLERS` is not guarded because we only call register
|
||||
/// from the context of Pin which is owned and in the rights state
|
||||
///
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
unsafe fn unregister_irq_handler(pin_number: usize) {
|
||||
chip::IRQ_HANDLERS[pin_number].take();
|
||||
}
|
||||
|
||||
macro_rules! impl_input {
|
||||
($pxi:ident: $pin:expr) => {
|
||||
crate::impl_peripheral!($pxi);
|
||||
@ -1450,7 +1417,7 @@ mod chip {
|
||||
use super::*;
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
pub(crate) static mut IRQ_HANDLERS: [Option<PinNotifySubscription>; 40] = [
|
||||
pub(crate) static mut ISR_HANDLERS: [Option<Box<Box<dyn FnMut()>>>; 40] = [
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None,
|
||||
@ -1634,7 +1601,7 @@ mod chip {
|
||||
use super::*;
|
||||
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))]
|
||||
pub(crate) static mut IRQ_HANDLERS: [Option<PinNotifySubscription>; 49] = [
|
||||
pub(crate) static mut ISR_HANDLERS: [Option<Box<Box<dyn FnMut()>>>; 49] = [
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
@ -1882,7 +1849,7 @@ mod chip {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub(crate) static mut IRQ_HANDLERS: [Option<PinNotifySubscription>; 22] = [
|
||||
pub(crate) static mut ISR_HANDLERS: [Option<Box<Box<dyn FnMut()>>>; 22] = [
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None,
|
||||
];
|
||||
|
@ -1,5 +1,4 @@
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use esp_idf_sys::*;
|
||||
|
||||
@ -10,17 +9,42 @@ pub fn active() -> bool {
|
||||
unsafe { xPortInIsrContext() != 0 }
|
||||
}
|
||||
|
||||
static ISR_YIELDER: AtomicPtr<c_types::c_void> = AtomicPtr::new(ptr::null_mut());
|
||||
pub fn with_isr_yield_signal(cb: impl FnOnce()) -> bool {
|
||||
if !active() {
|
||||
panic!("with_isr_yield_signal() can only be called from an ISR context");
|
||||
}
|
||||
|
||||
let mut signaled = false;
|
||||
|
||||
let prev_yielder =
|
||||
unsafe { set_isr_yielder(Some((do_yield_signal, &mut signaled as *mut _ as _))) };
|
||||
|
||||
cb();
|
||||
|
||||
unsafe { set_isr_yielder(prev_yielder) };
|
||||
|
||||
signaled
|
||||
}
|
||||
|
||||
unsafe fn do_yield_signal(arg: *mut ()) {
|
||||
let signaled = (arg as *mut bool).as_mut().unwrap();
|
||||
|
||||
*signaled = true
|
||||
}
|
||||
|
||||
static ISR_YIELDER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[inline(always)]
|
||||
#[link_section = ".iram1.interrupt_get_isr_yielder"]
|
||||
pub(crate) unsafe fn get_isr_yielder() -> Option<unsafe fn()> {
|
||||
pub(crate) unsafe fn get_isr_yielder() -> Option<(unsafe fn(*mut ()), *mut ())> {
|
||||
if active() {
|
||||
let ptr = ISR_YIELDER.load(Ordering::SeqCst);
|
||||
if ptr.is_null() {
|
||||
let value = ISR_YIELDER.load(Ordering::SeqCst);
|
||||
if value == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(core::mem::transmute(ptr))
|
||||
let func = core::mem::transmute((value >> 32) as u32);
|
||||
let arg = core::mem::transmute((value & 0xffffffff) as u32);
|
||||
Some((func, arg))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -40,21 +64,23 @@ pub(crate) unsafe fn get_isr_yielder() -> Option<unsafe fn()> {
|
||||
/// ISR handler was invoked.
|
||||
#[inline(always)]
|
||||
#[link_section = ".iram1.interrupt_set_isr_yielder"]
|
||||
pub unsafe fn set_isr_yielder(yielder: Option<unsafe fn()>) -> Option<unsafe fn()> {
|
||||
pub unsafe fn set_isr_yielder(
|
||||
yielder: Option<(unsafe fn(*mut ()), *mut ())>,
|
||||
) -> Option<(unsafe fn(*mut ()), *mut ())> {
|
||||
if active() {
|
||||
let ptr = if let Some(yielder) = yielder {
|
||||
#[allow(clippy::transmutes_expressible_as_ptr_casts)]
|
||||
core::mem::transmute(yielder)
|
||||
let value = if let Some((func, arg)) = yielder {
|
||||
((func as u32 as u64) << 32) | (arg as u32 as u64)
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
0
|
||||
};
|
||||
|
||||
let ptr = ISR_YIELDER.swap(ptr, Ordering::SeqCst);
|
||||
|
||||
if ptr.is_null() {
|
||||
let value = ISR_YIELDER.swap(value, Ordering::SeqCst);
|
||||
if value == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(core::mem::transmute(ptr))
|
||||
let func = core::mem::transmute((value >> 32) as u32);
|
||||
let arg = core::mem::transmute((value & 0xffffffff) as u32);
|
||||
Some((func, arg))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -56,6 +56,8 @@ pub mod spi;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod task;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod timer;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub mod uart;
|
||||
#[cfg(all(any(esp32, esp32s2, esp32s3), not(feature = "riscv-ulp-hal")))]
|
||||
pub mod ulp;
|
||||
|
@ -18,6 +18,8 @@ use crate::rmt;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::spi;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::timer;
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
use crate::uart;
|
||||
#[cfg(all(
|
||||
any(esp32, esp32s2, esp32s3),
|
||||
@ -67,6 +69,22 @@ pub struct Peripherals {
|
||||
pub mac: mac::MAC,
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
pub modem: modem::Modem,
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "embassy-time-timer-00")))]
|
||||
pub timer00: timer::TIMER00,
|
||||
#[cfg(all(
|
||||
not(esp32c3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
not(feature = "embassy-time-timer-01")
|
||||
))]
|
||||
pub timer01: timer::TIMER01,
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "embassy-time-timer-10")))]
|
||||
pub timer10: timer::TIMER10,
|
||||
#[cfg(all(
|
||||
not(esp32c3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
not(feature = "embassy-time-timer-11")
|
||||
))]
|
||||
pub timer11: timer::TIMER11,
|
||||
}
|
||||
|
||||
#[cfg(feature = "riscv-ulp-hal")]
|
||||
@ -153,6 +171,22 @@ impl Peripherals {
|
||||
mac: mac::MAC::new(),
|
||||
#[cfg(not(feature = "riscv-ulp-hal"))]
|
||||
modem: modem::Modem::new(),
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "embassy-time-timer-00")))]
|
||||
timer00: timer::TIMER00::new(),
|
||||
#[cfg(all(
|
||||
not(esp32c3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
not(feature = "embassy-time-timer-01")
|
||||
))]
|
||||
timer01: timer::TIMER01::new(),
|
||||
#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "embassy-time-timer-10")))]
|
||||
timer10: timer::TIMER10::new(),
|
||||
#[cfg(all(
|
||||
not(esp32c3),
|
||||
not(feature = "riscv-ulp-hal"),
|
||||
not(feature = "embassy-time-timer-11")
|
||||
))]
|
||||
timer11: timer::TIMER11::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/task.rs
17
src/task.rs
@ -10,24 +10,17 @@ use crate::interrupt;
|
||||
#[link_section = ".iram1.interrupt_task_do_yield"]
|
||||
pub fn do_yield() {
|
||||
if interrupt::active() {
|
||||
#[cfg(esp32c3)]
|
||||
unsafe {
|
||||
if let Some(yielder) = interrupt::get_isr_yielder() {
|
||||
yielder();
|
||||
if let Some((yielder, arg)) = interrupt::get_isr_yielder() {
|
||||
yielder(arg);
|
||||
} else {
|
||||
#[cfg(esp32c3)]
|
||||
vPortYieldFromISR();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(esp32c3))]
|
||||
unsafe {
|
||||
if let Some(yielder) = interrupt::get_isr_yielder() {
|
||||
yielder();
|
||||
} else {
|
||||
#[cfg(esp_idf_version_major = "4")]
|
||||
#[cfg(all(not(esp32c3), esp_idf_version_major = "4"))]
|
||||
vPortEvaluateYieldFromISR(0);
|
||||
|
||||
#[cfg(esp_idf_version_major = "5")]
|
||||
#[cfg(all(not(esp32c3), esp_idf_version_major = "5"))]
|
||||
_frxt_setup_switch();
|
||||
}
|
||||
}
|
||||
|
353
src/timer.rs
Normal file
353
src/timer.rs
Normal file
@ -0,0 +1,353 @@
|
||||
use esp_idf_sys::*;
|
||||
|
||||
use crate::peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
pub type TimerConfig = config::Config;
|
||||
|
||||
/// Timer configuration
|
||||
pub mod config {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
pub divider: u32,
|
||||
#[cfg(any(esp32s2, esp32s3, esp32c3))]
|
||||
pub xtal: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn divider(mut self, divider: u32) -> Self {
|
||||
self.divider = divider;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[cfg(any(esp32s2, esp32s3, esp32c3))]
|
||||
pub fn xtal(mut self, xtal: bool) -> Self {
|
||||
self.xtal = xtal;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
divider: 80,
|
||||
#[cfg(any(esp32s2, esp32s3, esp32c3))]
|
||||
xtal: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Timer: Send {
|
||||
fn group() -> timer_group_t;
|
||||
fn index() -> timer_idx_t;
|
||||
}
|
||||
|
||||
pub struct TimerDriver<'d, TIMER>
|
||||
where
|
||||
TIMER: Timer,
|
||||
{
|
||||
_timer: PeripheralRef<'d, TIMER>,
|
||||
}
|
||||
|
||||
impl<'d, TIMER> TimerDriver<'d, TIMER>
|
||||
where
|
||||
TIMER: Timer,
|
||||
{
|
||||
pub fn new(
|
||||
timer: impl Peripheral<P = TIMER> + 'd,
|
||||
config: &config::Config,
|
||||
) -> Result<TimerDriver<'d, TIMER>, EspError> {
|
||||
crate::into_ref!(timer);
|
||||
|
||||
esp!(unsafe {
|
||||
timer_init(
|
||||
TIMER::group(),
|
||||
TIMER::index(),
|
||||
&timer_config_t {
|
||||
alarm_en: timer_alarm_t_TIMER_ALARM_DIS,
|
||||
counter_en: timer_start_t_TIMER_PAUSE,
|
||||
counter_dir: timer_count_dir_t_TIMER_COUNT_UP,
|
||||
auto_reload: timer_autoreload_t_TIMER_AUTORELOAD_DIS,
|
||||
intr_type: timer_intr_mode_t_TIMER_INTR_LEVEL,
|
||||
divider: config.divider,
|
||||
#[cfg(any(esp32s2, esp32s3, esp32c3))]
|
||||
clk_src: if config.xtal {
|
||||
timer_src_clk_t_TIMER_SRC_CLK_XTAL
|
||||
} else {
|
||||
timer_src_clk_t_TIMER_SRC_CLK_ARB
|
||||
},
|
||||
},
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(TimerDriver { _timer: timer })
|
||||
}
|
||||
|
||||
pub fn enable(&mut self, enable: bool) -> Result<(), EspError> {
|
||||
if enable {
|
||||
esp!(unsafe { timer_start(TIMER::group(), TIMER::index()) })?;
|
||||
} else {
|
||||
esp!(unsafe { timer_pause(TIMER::group(), TIMER::index()) })?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn counter(&self) -> Result<u64, EspError> {
|
||||
let mut value = 0_u64;
|
||||
|
||||
esp!(unsafe {
|
||||
timer_get_counter_value(TIMER::group(), TIMER::index(), &mut value as *mut _)
|
||||
})?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn set_counter(&mut self, value: u64) -> Result<(), EspError> {
|
||||
esp!(unsafe { timer_set_counter_value(TIMER::group(), TIMER::index(), value) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_alarm(&mut self, enable: bool) -> Result<(), EspError> {
|
||||
esp!(unsafe {
|
||||
timer_set_alarm(
|
||||
TIMER::group(),
|
||||
TIMER::index(),
|
||||
if enable {
|
||||
timer_alarm_t_TIMER_ALARM_EN
|
||||
} else {
|
||||
timer_alarm_t_TIMER_ALARM_DIS
|
||||
},
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn alarm(&self) -> Result<u64, EspError> {
|
||||
let mut value = 0_u64;
|
||||
|
||||
esp!(unsafe { timer_get_alarm_value(TIMER::group(), TIMER::index(), &mut value) })?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn set_alarm(&mut self, value: u64) -> Result<(), EspError> {
|
||||
esp!(unsafe { timer_set_alarm_value(TIMER::group(), TIMER::index(), value) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_interrupt(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { timer_enable_intr(TIMER::group(), TIMER::index()) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable_interrupt(&mut self) -> Result<(), EspError> {
|
||||
esp!(unsafe { timer_disable_intr(TIMER::group(), TIMER::index()) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn subscribe(&mut self, callback: impl FnMut() + 'static) -> Result<(), EspError> {
|
||||
self.unsubscribe()?;
|
||||
|
||||
let callback: Box<dyn FnMut() + 'static> = Box::new(callback);
|
||||
|
||||
ISR_HANDLERS[(TIMER::group() * timer_group_t_TIMER_GROUP_MAX + TIMER::index()) as usize] =
|
||||
Some(Box::new(callback));
|
||||
|
||||
esp!(timer_isr_callback_add(
|
||||
TIMER::group(),
|
||||
TIMER::index(),
|
||||
Some(Self::handle_isr),
|
||||
UnsafeCallback::from(
|
||||
ISR_HANDLERS
|
||||
[(TIMER::group() * timer_group_t_TIMER_GROUP_MAX + TIMER::index()) as usize]
|
||||
.as_mut()
|
||||
.unwrap(),
|
||||
)
|
||||
.as_ptr(),
|
||||
0
|
||||
))?;
|
||||
|
||||
self.enable_interrupt()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unsubscribe(&mut self) -> Result<(), EspError> {
|
||||
unsafe {
|
||||
unsubscribe_timer(TIMER::group(), TIMER::index())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C" fn handle_isr(unsafe_callback: *mut c_types::c_void) -> bool {
|
||||
crate::interrupt::with_isr_yield_signal(move || {
|
||||
UnsafeCallback::from_ptr(unsafe_callback).call();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, TIMER: Timer> Drop for TimerDriver<'d, TIMER> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { timer_deinit(TIMER::group(), TIMER::index()) }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, TIMER: Timer> Send for TimerDriver<'d, TIMER> {}
|
||||
|
||||
unsafe fn unsubscribe_timer(group: timer_group_t, index: timer_idx_t) -> Result<(), EspError> {
|
||||
let subscribed =
|
||||
ISR_HANDLERS[(group * timer_group_t_TIMER_GROUP_MAX + index) as usize].is_some();
|
||||
|
||||
if subscribed {
|
||||
esp!(timer_disable_intr(group, index))?;
|
||||
esp!(timer_isr_callback_remove(group, index))?;
|
||||
|
||||
ISR_HANDLERS[(group * timer_group_t_TIMER_GROUP_MAX + index) as usize] = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct UnsafeCallback(*mut Box<dyn FnMut() + 'static>);
|
||||
|
||||
impl UnsafeCallback {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn from(boxed: &mut Box<Box<dyn FnMut() + 'static>>) -> Self {
|
||||
Self(boxed.as_mut())
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr(ptr: *mut c_types::c_void) -> Self {
|
||||
Self(ptr as *mut _)
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut c_types::c_void {
|
||||
self.0 as *mut _
|
||||
}
|
||||
|
||||
pub unsafe fn call(&mut self) {
|
||||
let reference = self.0.as_mut().unwrap();
|
||||
|
||||
(reference)();
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_timer {
|
||||
($timer:ident: $group:expr, $index:expr) => {
|
||||
crate::impl_peripheral!($timer);
|
||||
|
||||
impl Timer for $timer {
|
||||
#[inline(always)]
|
||||
fn group() -> timer_group_t {
|
||||
$group
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn index() -> timer_idx_t {
|
||||
$index
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
static mut ISR_HANDLERS: [Option<Box<Box<dyn FnMut()>>>; 2] = [None, None];
|
||||
|
||||
#[cfg(not(esp32c3))]
|
||||
static mut ISR_HANDLERS: [Option<Box<Box<dyn FnMut()>>>; 4] = [None, None, None, None];
|
||||
|
||||
impl_timer!(TIMER00: timer_group_t_TIMER_GROUP_0, timer_idx_t_TIMER_0);
|
||||
#[cfg(not(esp32c3))]
|
||||
impl_timer!(TIMER01: timer_group_t_TIMER_GROUP_0, timer_idx_t_TIMER_1);
|
||||
impl_timer!(TIMER10: timer_group_t_TIMER_GROUP_1, timer_idx_t_TIMER_0);
|
||||
#[cfg(not(esp32c3))]
|
||||
impl_timer!(TIMER11: timer_group_t_TIMER_GROUP_1, timer_idx_t_TIMER_0);
|
||||
|
||||
#[cfg(any(
|
||||
feature = "embassy-time-timer00",
|
||||
feature = "embassy-time-timer01",
|
||||
feature = "embassy-time-timer10",
|
||||
feature = "embassy-time-timer11"
|
||||
))]
|
||||
mod embassy_time {
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use esp_idf_sys::esp_timer_get_time;
|
||||
|
||||
#[cfg(any(feature = "embassy-time-timer01", feature = "embassy-time-timer11"))]
|
||||
compile_error!("Features `embassy-time-timer01` and `embassy-time-timer11`");
|
||||
|
||||
#[cfg(feature = "embassy-time-timer00")]
|
||||
type TIMER = super::TIMER00;
|
||||
#[cfg(feature = "embassy-time-timer01")]
|
||||
type TIMER = super::TIMER01;
|
||||
#[cfg(feature = "embassy-time-timer10")]
|
||||
type TIMER = super::TIMER10;
|
||||
#[cfg(feature = "embassy-time-timer11")]
|
||||
type TIMER = super::TIMER11;
|
||||
|
||||
use ::embassy_time::driver::{AlarmHandle, Driver};
|
||||
|
||||
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
struct EspDriver(UnsafeCell<Option<super::TimerDriver<'static, TIMER>>>);
|
||||
|
||||
impl EspDriver {
|
||||
pub const fn new() -> Self {
|
||||
Self(UnsafeCell::new(None))
|
||||
}
|
||||
|
||||
unsafe fn driver(&self) -> &mut super::TimerDriver<'static, TIMER> {
|
||||
self.0.get().as_mut().unwrap().as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for EspDriver {}
|
||||
unsafe impl Sync for EspDriver {}
|
||||
|
||||
impl Driver for EspDriver {
|
||||
fn now(&self) -> u64 {
|
||||
unsafe { esp_timer_get_time() as _ }
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
if TAKEN.swap(true, Ordering::SeqCst) {
|
||||
None
|
||||
} else {
|
||||
let mut timer =
|
||||
super::TimerDriver::new(TIMER::new(), &super::TimerConfig::new()).unwrap();
|
||||
|
||||
timer.set_counter(0).unwrap();
|
||||
|
||||
*self.0.get().as_mut().unwrap() = Some(timer);
|
||||
|
||||
Some(AlarmHandle::new(0))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
unsafe {
|
||||
self.driver().subscribe(move || callback(ctx)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) {
|
||||
unsafe { self.driver() }.set_alarm(timestamp).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
embassy_time::time_driver_impl!(static DRIVER: EspDriver = EspDriver::new());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user