use core::ffi::c_void;
use core::time::Duration;
use core::{fmt, ptr};
use alloc::boxed::Box;
use esp_idf_sys::*;
use crate::interrupt;
use crate::interrupt::asynch::HalIsrNotification;
use crate::units::{FromValueType, Hertz};
use config::*;
pub const ERR_EOVERFLOW: esp_err_t = 139;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct AlarmEventData {
pub count_value: u64,
pub alarm_value: u64,
}
impl From<gptimer_alarm_event_data_t> for AlarmEventData {
fn from(value: gptimer_alarm_event_data_t) -> Self {
Self {
count_value: value.count_value,
alarm_value: value.alarm_value,
}
}
}
pub mod config {
use esp_idf_sys::*;
use crate::units::{FromValueType, Hertz};
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub enum CountDirection {
#[default]
Up,
Down,
}
impl From<CountDirection> for gptimer_count_direction_t {
fn from(value: CountDirection) -> Self {
match value {
CountDirection::Up => gptimer_count_direction_t_GPTIMER_COUNT_UP,
CountDirection::Down => gptimer_count_direction_t_GPTIMER_COUNT_DOWN,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum ClockSource {
#[default]
Default,
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3))]
APB,
#[cfg(any(esp32c5, esp32c6, esp32c61, esp32h2, esp32p4))]
RcFast,
#[cfg(any(
esp32s2, esp32s3, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2, esp32p4
))]
XTAL,
#[cfg(esp32c2)]
PLLF40M,
#[cfg(esp32h2)]
PLLF48M,
#[cfg(any(esp32c5, esp32c6, esp32c61, esp32p4))]
PLLF80M,
}
impl From<ClockSource> for soc_periph_gptimer_clk_src_t {
fn from(clock: ClockSource) -> Self {
match clock {
ClockSource::Default => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_DEFAULT,
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3))]
ClockSource::APB => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_APB,
#[cfg(any(esp32c5, esp32c6, esp32c61, esp32h2, esp32p4))]
ClockSource::RcFast => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_RC_FAST,
#[cfg(any(
esp32s2, esp32s3, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2,
esp32p4
))]
ClockSource::XTAL => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_XTAL,
#[cfg(esp32c2)]
ClockSource::PLLF40M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F40M,
#[cfg(esp32h2)]
ClockSource::PLLF48M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F48M,
#[cfg(any(esp32c5, esp32c6, esp32c61, esp32p4))]
ClockSource::PLLF80M => soc_periph_gptimer_clk_src_t_GPTIMER_CLK_SRC_PLL_F80M,
}
}
}
#[derive(Debug, Clone)]
pub struct TimerConfig {
pub clock_source: ClockSource,
pub direction: CountDirection,
pub resolution: Hertz,
#[cfg(esp_idf_version_at_least_5_1_2)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_2)))]
pub intr_priority: i32,
pub intr_shared: bool,
#[cfg(esp_idf_version_at_least_5_4_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
pub allow_pd: bool,
#[doc(hidden)]
#[allow(dead_code)]
pub __internal: (),
}
impl Default for TimerConfig {
fn default() -> Self {
Self {
clock_source: Default::default(),
direction: Default::default(),
resolution: 1_000_000.Hz(), #[cfg(esp_idf_version_at_least_5_1_2)]
intr_priority: 0,
intr_shared: false,
#[cfg(esp_idf_version_at_least_5_4_0)]
allow_pd: false,
__internal: (),
}
}
}
#[derive(Debug, Clone)]
pub struct AlarmConfig {
pub reload_count: u64,
pub alarm_count: u64,
pub auto_reload_on_alarm: bool,
#[doc(hidden)]
#[allow(dead_code)]
pub __internal: (),
}
impl Default for AlarmConfig {
fn default() -> Self {
Self {
reload_count: 0,
alarm_count: 1_000_000,
auto_reload_on_alarm: false,
__internal: (),
}
}
}
impl From<&AlarmConfig> for gptimer_alarm_config_t {
fn from(config: &AlarmConfig) -> Self {
gptimer_alarm_config_t {
alarm_count: config.alarm_count,
reload_count: config.reload_count,
flags: gptimer_alarm_config_t__bindgen_ty_1 {
_bitfield_1: gptimer_alarm_config_t__bindgen_ty_1::new_bitfield_1(
config.auto_reload_on_alarm as _,
),
..Default::default()
},
}
}
}
}
struct AlarmUserData<'d> {
on_alarm: Box<dyn FnMut(AlarmEventData) + Send + 'd>,
notif: HalIsrNotification,
}
pub struct TimerDriver<'d> {
handle: gptimer_handle_t,
on_alarm: Option<Box<AlarmUserData<'d>>>,
}
impl<'d> TimerDriver<'d> {
pub fn new(config: &TimerConfig) -> Result<Self, EspError> {
let sys_config = gptimer_config_t {
clk_src: config.clock_source.into(),
direction: config.direction.clone().into(),
resolution_hz: config.resolution.into(),
#[cfg(esp_idf_version_at_least_5_1_2)]
intr_priority: config.intr_priority,
flags: gptimer_config_t__bindgen_ty_1 {
_bitfield_1: gptimer_config_t__bindgen_ty_1::new_bitfield_1(
config.intr_shared as _,
#[cfg(esp_idf_version_at_least_5_4_0)]
{
config.allow_pd as _
},
#[cfg(all(
esp_idf_version_at_least_5_3_0,
not(esp_idf_version_at_least_6_1_0)
))]
{
false as _
},
),
..Default::default()
},
};
let mut handle = ptr::null_mut();
esp!(unsafe { gptimer_new_timer(&sys_config, &raw mut handle) })?;
Ok(Self {
handle,
on_alarm: None,
})
}
pub fn handle(&self) -> gptimer_handle_t {
self.handle
}
#[cfg(esp_idf_version_at_least_5_1_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
pub fn duration_to_count(&self, duration: Duration) -> Result<u64, EspError> {
let ticks_per_second = self.get_resolution()?.0 as u64;
let duration_in_seconds = duration.as_secs();
let duration_rem_in_nanos = duration.subsec_nanos() as u64;
let Some(ticks) = ticks_per_second.checked_mul(duration_in_seconds) else {
return Err(EspError::from_infallible::<ERR_EOVERFLOW>());
};
ticks
.checked_add(
(ticks_per_second * duration_rem_in_nanos)
/ Duration::from_secs(1).as_nanos() as u64,
)
.ok_or(EspError::from_infallible::<ERR_EOVERFLOW>())
}
#[cfg(esp_idf_version_at_least_5_1_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
pub async fn delay(&self, duration: Duration) -> Result<(), EspError> {
let alarm_count = self.duration_to_count(duration)?;
self.set_raw_count(0)?;
self.set_alarm_action(Some(&AlarmConfig {
alarm_count,
..Default::default()
}))?;
let res = self.wait().await;
self.set_alarm_action(None)?;
res
}
fn notif(&self) -> Result<&HalIsrNotification, EspError> {
self.on_alarm
.as_ref()
.map(|data| &data.notif)
.ok_or(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())
}
pub async fn wait(&self) -> Result<(), EspError> {
self.notif()?.wait().await;
Ok(())
}
pub fn reset_wait(&self) {
if let Ok(notif) = self.notif() {
notif.reset();
}
}
pub fn set_raw_count(&self, value: u64) -> Result<(), EspError> {
esp!(unsafe { gptimer_set_raw_count(self.handle, value) })
}
pub fn get_raw_count(&self) -> Result<u64, EspError> {
let mut value: u64 = 0;
esp!(unsafe { gptimer_get_raw_count(self.handle, &raw mut value) })?;
Ok(value)
}
#[cfg(esp_idf_version_at_least_5_1_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
pub fn get_resolution(&self) -> Result<Hertz, EspError> {
let mut value: u32 = 0;
esp!(unsafe { gptimer_get_resolution(self.handle, &raw mut value) })?;
Ok(value.Hz())
}
#[cfg(esp_idf_version_at_least_5_1_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_1_0)))]
pub fn get_captured_count(&self) -> Result<u64, EspError> {
let mut value: u64 = 0;
esp!(unsafe { gptimer_get_captured_count(self.handle, &raw mut value) })?;
Ok(value)
}
pub fn subscribe(
&mut self,
on_alarm: impl FnMut(AlarmEventData) + Send + 'static,
) -> Result<(), EspError> {
unsafe { self.subscribe_nonstatic(on_alarm) }
}
pub unsafe fn subscribe_nonstatic(
&mut self,
on_alarm: impl FnMut(AlarmEventData) + Send + 'd,
) -> Result<(), EspError> {
let mut user_data = Box::new(AlarmUserData {
on_alarm: Box::new(on_alarm),
notif: HalIsrNotification::new(),
});
let cbs = gptimer_event_callbacks_t {
on_alarm: Some(Self::handle_isr),
};
esp!(unsafe {
gptimer_register_event_callbacks(self.handle, &cbs, (&raw mut *user_data) as *mut _)
})?;
self.on_alarm = Some(user_data);
Ok(())
}
pub fn subscribe_default(&mut self) -> Result<(), EspError> {
self.subscribe(|_| {})
}
pub fn unsubscribe(&mut self) -> Result<(), EspError> {
esp!(unsafe {
gptimer_register_event_callbacks(self.handle, ptr::null(), ptr::null_mut())
})?;
self.on_alarm = None;
Ok(())
}
pub fn set_alarm_action(&self, config: Option<&AlarmConfig>) -> Result<(), EspError> {
let sys_config = config.map(|c| c.into());
esp!(unsafe {
gptimer_set_alarm_action(self.handle, sys_config.as_ref().map_or(ptr::null(), |c| c))
})
}
pub fn enable(&self) -> Result<(), EspError> {
esp!(unsafe { gptimer_enable(self.handle) })
}
pub fn disable(&self) -> Result<(), EspError> {
esp!(unsafe { gptimer_disable(self.handle) })
}
pub fn start(&self) -> Result<(), EspError> {
esp!(unsafe { gptimer_start(self.handle) })
}
pub fn stop(&self) -> Result<(), EspError> {
esp!(unsafe { gptimer_stop(self.handle) })
}
unsafe extern "C" fn handle_isr(
_handle: gptimer_handle_t,
event_data: *const gptimer_alarm_event_data_t,
arg: *mut c_void,
) -> bool {
let user_data = &mut *(arg as *mut AlarmUserData);
let event = AlarmEventData::from(*event_data);
interrupt::with_isr_yield_signal(|| {
(user_data.on_alarm)(event);
user_data.notif.notify_lsb();
})
}
}
unsafe impl<'d> Send for TimerDriver<'d> {}
unsafe impl<'d> Sync for TimerDriver<'d> {}
impl<'d> Drop for TimerDriver<'d> {
fn drop(&mut self) {
let _ = self.stop();
let _ = self.disable();
unsafe {
gptimer_del_timer(self.handle);
}
}
}
impl<'d> fmt::Debug for TimerDriver<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TimerDriver")
.field("handle", &self.handle)
.finish()
}
}