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:
ivmarkov 2022-09-10 09:09:25 +03:00 committed by Ivan Markov
parent 4dfe2f1bcd
commit 67f2484966
7 changed files with 501 additions and 121 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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