esp-hal/esp-hal-embassy/src/lib.rs
Dániel Buga 1e6820d1a7
Systimer improvements (#2451)
* Do not read to set update bit

* Deduplicate

* Try to bind interrupts to the correct core

* Inline poll_count into read_count

* Clean up

* Make sure only a single update is done at a time

* Changelog

* Fix docs

* Correct the channel count

* Assign enough timers for HIL test

* Use a lock to prevent re-update

* Remove locking, use esp-idf implementation

* Document timer count requirement
2024-11-04 11:36:34 +00:00

170 lines
4.4 KiB
Rust

//! Embassy support for [esp-hal].
//!
//! [Embassy] is a modern asynchronous framework intended for use with embedded
//! systems. This package provides support for building applications using
//! Embassy with [esp-hal].
//!
//! [esp-hal]: https://github.com/esp-rs/esp-hal
//! [embassy]: https://github.com/embassy-rs/embassy
//!
//! ## Executors
//!
//! Two types of executors are provided:
//!
//! - [Executor]: A thread-mode executor
//! - [InterruptExecutor]: An interrupt-mode executor
//!
//! [InterruptExecutor] can be used to achieve preemptive multitasking in
//! asynchronous applications, which is typically something reserved for more
//! traditional RTOS. More information can be found in the [Embassy
//! documentation].
//!
//! [embassy documentation]: https://embassy.dev/book/
//!
//! ## Initialization
//!
//! Embassy **must** be initialized by calling the [init] function. This
//! initialization must be performed *prior* to spawning any tasks.
//!
//! ## Feature Flags
#![doc = document_features::document_features!()]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
#![deny(missing_docs)]
#![cfg_attr(xtensa, feature(asm_experimental_arch))]
#![no_std]
// MUST be the first module
mod fmt;
#[cfg(not(feature = "esp32"))]
use esp_hal::timer::systimer::Alarm;
use esp_hal::timer::{timg::Timer as TimgTimer, AnyTimer};
pub use macros::main;
#[cfg(feature = "executors")]
pub use self::executor::{Executor, InterruptExecutor};
use self::time_driver::{EmbassyTimer, Timer};
#[cfg(feature = "executors")]
mod executor;
mod time_driver;
macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
x
}};
}
/// A trait to allow better UX for initializing the timers.
///
/// This trait is meant to be used only for the `init` function.
/// Calling `timers()` multiple times may panic.
pub trait TimerCollection {
/// Returns the timers as a slice.
fn timers(self) -> &'static mut [Timer];
}
/// Helper trait to reduce boilerplate.
///
/// We can't blanket-implement for `Into<AnyTimer>` because of possible
/// conflicting implementations.
trait IntoAnyTimer: Into<AnyTimer> {}
impl IntoAnyTimer for AnyTimer {}
impl<T, DM> IntoAnyTimer for TimgTimer<T, DM>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
#[cfg(not(feature = "esp32"))]
impl<T, DM, COMP, UNIT> IntoAnyTimer for Alarm<'_, T, DM, COMP, UNIT>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
impl<T> TimerCollection for T
where
T: IntoAnyTimer,
{
fn timers(self) -> &'static mut [Timer] {
Timer::new(self.into()).timers()
}
}
impl TimerCollection for Timer {
fn timers(self) -> &'static mut [Timer] {
let timers = mk_static!([Timer; 1], [self]);
timers.timers()
}
}
impl TimerCollection for &'static mut [Timer] {
fn timers(self) -> &'static mut [Timer] {
self
}
}
impl<const N: usize> TimerCollection for &'static mut [Timer; N] {
fn timers(self) -> &'static mut [Timer] {
self.as_mut()
}
}
macro_rules! impl_array {
($n:literal) => {
impl<T> TimerCollection for [T; $n]
where
T: IntoAnyTimer,
{
fn timers(self) -> &'static mut [Timer] {
mk_static!([Timer; $n], self.map(|t| Timer::new(t.into())))
}
}
};
}
impl_array!(2);
impl_array!(3);
impl_array!(4);
/// Initialize embassy.
///
/// Call this as soon as possible, before the first timer-related operation.
///
/// The time driver can be one of a number of different options:
///
/// - A timg `Timer` instance
/// - A systimer `Alarm` instance
/// - An `AnyTimer` instance
/// - A `OneShotTimer` instance
/// - A mutable static slice of `OneShotTimer` instances
/// - A mutable static array of `OneShotTimer` instances
/// - A 2, 3, 4 element array of `AnyTimer` instances
///
/// Note that if you use the `integrated-timers` feature,
/// you need to pass as many timers as you start executors.
///
/// # Examples
///
/// ```rust, no_run
#[doc = esp_hal::before_snippet!()]
/// use esp_hal::timg::TimerGroup;
///
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
/// esp_hal_embassy::init(timg0.timer0);
///
/// // ... now you can spawn embassy tasks or use `Timer::after` etc.
/// # }
/// ```
pub fn init(time_driver: impl TimerCollection) {
EmbassyTimer::init(time_driver.timers())
}