mirror of
https://github.com/esp-rs/esp-hal.git
synced 2026-01-21 03:35:37 +00:00
Document esp-rtos just a bit (#4208)
* Slight MG touchup * Document esp-rtos in migration guide * Document things * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Improve esp-rtos docs --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0e7c5f91e6
commit
d2d9151949
@ -442,7 +442,7 @@ impl EspHeapInner {
|
||||
used
|
||||
}
|
||||
|
||||
/// Return usage stats for the [Heap].
|
||||
/// Return usage stats for the [EspHeap].
|
||||
///
|
||||
/// Note:
|
||||
/// [HeapStats] directly implements [Display], so this function can be
|
||||
@ -603,7 +603,7 @@ impl EspHeap {
|
||||
self.inner.with(|heap| heap.used())
|
||||
}
|
||||
|
||||
/// Return usage stats for the [Heap].
|
||||
/// Return usage stats for the [EspHeap].
|
||||
///
|
||||
/// Note:
|
||||
/// [HeapStats] directly implements [Display], so this function can be
|
||||
|
||||
@ -22,8 +22,9 @@
|
||||
//! sections, such as SRAM or RTC RAM (slow or fast) with different initialization options. See
|
||||
//! its documentation for details.
|
||||
//!
|
||||
//! - [`embassy::main`](macro@embassy_main) - Creates a new `executor` instance and declares an
|
||||
//! application entry point spawning the corresponding function body as an async task.
|
||||
//! - [`esp_rtos::main`](macro@rtos_main) - Creates a new instance of `esp_rtos::embassy::Executor`
|
||||
//! and declares an application entry point spawning the corresponding function body as an async
|
||||
//! task.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
|
||||
@ -79,7 +79,9 @@ esp_hal::interrupt::bind_interrupt(
|
||||
);
|
||||
```
|
||||
|
||||
## RMT PulseCode changes
|
||||
## RMT changes
|
||||
|
||||
### RMT PulseCode changes
|
||||
|
||||
`PulseCode` used to be an extension trait implemented on `u32`. It is now a
|
||||
newtype struct, wrapping `u32`.
|
||||
@ -113,7 +115,7 @@ let _ = tx_channel.transmit(&tx_data).wait().unwrap();
|
||||
let _ = rx_channel.transmit(&mut rx_data).wait().unwrap();
|
||||
```
|
||||
|
||||
## RMT Channel Changes
|
||||
### RMT Channel Changes
|
||||
|
||||
`rmt::Channel` used to have a `Raw: RawChannelAccess` generic parameter,
|
||||
which could be either `ConstChannelAccess<Dir, const CHANNEL: u8>` or `DynChannelAccess<Dir>`.
|
||||
@ -149,7 +151,7 @@ API as well.
|
||||
+let rx_transaction: RxTransaction<'_, PulseCode> = rx.transmit(&data);
|
||||
```
|
||||
|
||||
## RMT method changes
|
||||
### RMT method changes
|
||||
|
||||
The `rmt::Channel::transmit_continuously` and
|
||||
`rmt::Channel::transmit_continuously_with_loopcount` methods have been merged:
|
||||
@ -268,9 +270,9 @@ Imports will need to be updated accordingly.
|
||||
|
||||
Additionally, enum variant naming violations have been resolved, so the `RtcFastClock` and `RtcSlowClock` prefixes will need to be removed from any variants from these enums.
|
||||
|
||||
## Direct vectoring changes
|
||||
## RISC-V interrupt direct vectoring changes
|
||||
|
||||
`enable_direct` now requires user to pass handler function to it.
|
||||
`enable_direct` now requires user to pass handler function to it.
|
||||
|
||||
```diff
|
||||
interrupt::enable_direct(
|
||||
@ -280,5 +282,86 @@ interrupt::enable_direct(
|
||||
+ interrupt_handler,
|
||||
)
|
||||
.unwrap();
|
||||
```
|
||||
|
||||
```
|
||||
## Async/embassy changes
|
||||
|
||||
> This section affects `esp-hal-embassy` users. Ariel-OS users are not affected by these changes.
|
||||
|
||||
The `esp-hal-embassy` has been discontinued. Embassy is continued to be supported as part of `esp-rtos`.
|
||||
|
||||
### Configuration
|
||||
|
||||
`esp-hal-embassy` configuration options have not been ported to `esp-rtos`. `esp-rtos` by default works with a single integrated timer queue.
|
||||
To keep using generic timer queues, use the configuration options provided by `embassy-time`. Multiple timer queues (i.e. the previous `multiple-integrated` option) are not supported.
|
||||
|
||||
The `low-power-wait` configuration can be substituted with a custom idle hook. You can specify an idle hook by calling `esp_rtos::start_with_idle_hook`, with a function that just `loop`s.
|
||||
|
||||
### Setup
|
||||
|
||||
The previous `esp_hal_embassy::main` macro has been replaced by `esp_rtos::main`. The `esp_hal_embassy::init` function has been replaced by `esp_rtos::start`, with a different signature; this function should be used for `esp_radio` as well.
|
||||
|
||||
`esp_rtos::start` has a different signature for the different CPU architectures. The function takes a single timer instead of a variable number of timers.
|
||||
|
||||
On Xtensa devices (ESP32/S2/S3):
|
||||
|
||||
```diff
|
||||
-#[esp_hal_embassy::main]
|
||||
+#[esp_rtos::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
// ... timer setup not shown here.
|
||||
- esp_hal_embassy::init([timer0, timer1]);
|
||||
+ esp_rtos::start(timer0);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
On RISC-V devices (ESP32-C2/C3/C6/H2) you'll need to also pass `SoftwareInterrupt<0>` to `esp_rtos::start`:
|
||||
|
||||
```diff
|
||||
+use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||
|
||||
-#[esp_hal_embassy::main]
|
||||
+#[esp_rtos::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
// ... timer setup not shown here.
|
||||
- esp_hal_embassy::init([timer0, timer1]);
|
||||
|
||||
+ let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
+ esp_rtos::start(timer0, software_interrupt.software_interrupt0);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-core support
|
||||
|
||||
`esp_rtos::embassy::Executor` expects to be run in an `esp_rtos` thread. This means it cannot be started on the second core, unless the second core is managed by `esp_rtos`:
|
||||
|
||||
```rust
|
||||
use esp_hal::system::Stack;
|
||||
use esp_rtos::embassy::Executor;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
static APP_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
||||
let app_core_stack = APP_CORE_STACK.init(Stack::new());
|
||||
|
||||
// AFTER esp_rtos::start
|
||||
|
||||
esp_rtos::start_second_core(
|
||||
peripherals.CPU_CTRL,
|
||||
sw_int.software_interrupt0,
|
||||
sw_int.software_interrupt1,
|
||||
app_core_stack,
|
||||
move || {
|
||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
||||
let executor = EXECUTOR.init(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
// Spawn tasks from here.
|
||||
});
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
### Interrupt executor changes
|
||||
|
||||
Interrupt executors are provided as `esp_rtos::embassy::InterruptExecutor` with no additional changes.
|
||||
|
||||
@ -11,7 +11,7 @@ repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.espressif]
|
||||
doc-config = { features = ["esp-hal/unstable"] }
|
||||
doc-config = { features = ["esp-hal/unstable", "embassy", "esp-radio"] }
|
||||
check-configs = [
|
||||
{ features = ["esp-hal/unstable"] },
|
||||
{ features = ["esp-hal/unstable", "esp-alloc"] },
|
||||
@ -25,7 +25,7 @@ clippy-configs = [
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "riscv32imac-unknown-none-elf"
|
||||
features = ["esp32c6"]
|
||||
features = ["esp32c6", "embassy", "esp-radio"]
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
@ -8,9 +8,3 @@ options:
|
||||
constraints:
|
||||
- type:
|
||||
validator: positive_integer
|
||||
|
||||
- name: stack-guard-value
|
||||
description: The stack overflow guard value. This must be the same value as esp_hal's stack guard value.
|
||||
default:
|
||||
- value: 3740121773
|
||||
display_hint: Hex
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//! OS-aware embassy executors.
|
||||
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::Ordering};
|
||||
|
||||
use embassy_executor::{SendSpawner, Spawner, raw};
|
||||
@ -34,6 +36,9 @@ pub trait Callbacks {
|
||||
fn on_idle(&mut self);
|
||||
}
|
||||
|
||||
/// Thread-mode executor.
|
||||
///
|
||||
/// This executor runs in an OS thread.
|
||||
pub struct Executor {
|
||||
/// Signals that work is available.
|
||||
semaphore: Semaphore,
|
||||
@ -144,6 +149,9 @@ impl Default for Executor {
|
||||
/// This executor runs tasks in interrupt mode. The interrupt handler is set up
|
||||
/// to poll tasks, and when a task is woken the interrupt is pended from
|
||||
/// software.
|
||||
///
|
||||
/// Interrupt executors have potentially lower latency than thread-mode executors, but only a
|
||||
/// limited number can be created.
|
||||
pub struct InterruptExecutor<const SWI: u8> {
|
||||
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
||||
interrupt: SoftwareInterrupt<'static, SWI>,
|
||||
@ -207,13 +215,11 @@ impl<const SWI: u8> InterruptExecutor<SWI> {
|
||||
/// The executor keeps running in the background through the interrupt.
|
||||
///
|
||||
/// This returns a [`SendSpawner`] you can use to spawn tasks on it. A
|
||||
/// [`SendSpawner`] is returned instead of a
|
||||
/// [`Spawner`](embassy_executor::Spawner) because the
|
||||
/// [`SendSpawner`] is returned instead of a [`Spawner`] because the
|
||||
/// executor effectively runs in a different "thread" (the interrupt),
|
||||
/// so spawning tasks on it is effectively sending them.
|
||||
///
|
||||
/// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor,
|
||||
/// use [`Spawner::for_current_executor`](embassy_executor::Spawner::for_current_executor)
|
||||
/// To obtain a [`Spawner`] for this executor, use [`Spawner::for_current_executor`]
|
||||
/// from a task running in it.
|
||||
pub fn start(&'static mut self, priority: Priority) -> SendSpawner {
|
||||
unsafe {
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
//! This crate allows using esp-radio on top of esp-hal, without any other OS.
|
||||
#![cfg_attr(
|
||||
all(docsrs, not(not_really_docsrs)),
|
||||
doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.espressif.com/projects/rust/'>browse the <code>esp-hal</code> documentation on the esp-rs website</a> instead.</p><p>The documentation here on <a href='https://docs.rs'>docs.rs</a> is built for a single chip only (ESP32-C6, in particular), while on the esp-rs website you can select your exact chip from the list of supported devices. Available peripherals and their APIs change depending on the chip.</p></div>\n\n<br/>\n\n"
|
||||
)]
|
||||
//! This crate provides RTOS functionality for `esp-radio`, and provides executors to enable
|
||||
//! running `async` code.
|
||||
//!
|
||||
//! This crate requires an esp-hal timer to operate.
|
||||
//! ## Setup
|
||||
//!
|
||||
//! This crate requires an esp-hal timer to operate, and needs to be started like so:
|
||||
//!
|
||||
//! ```rust, no_run
|
||||
//! use esp_hal::timer::timg::TimerGroup;
|
||||
@ -51,10 +58,17 @@ esp_rtos::start_second_core(
|
||||
//! // let esp_radio_controller = esp_radio::init().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! To write `async` code, enable the `embassy` feature, and mark the main function with `#[esp_rtos::main]`.
|
||||
//! Note that, to create async tasks, you will need the `task` macro from the `embassy-executor` crate. Do
|
||||
//! NOT enable any of the `arch-*` features on `embassy-executor`.
|
||||
//!
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![no_std]
|
||||
#![cfg_attr(xtensa, feature(asm_experimental_arch))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
@ -72,6 +86,7 @@ mod timer;
|
||||
mod wait_queue;
|
||||
|
||||
#[cfg(feature = "embassy")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "embassy")))]
|
||||
pub mod embassy;
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
@ -91,6 +106,7 @@ use esp_hal::{
|
||||
system::{CpuControl, Stack},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "embassy")))]
|
||||
pub use macros::rtos_main as main;
|
||||
pub(crate) use scheduler::SCHEDULER;
|
||||
pub use task::CurrentThreadHandle;
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
//! Semaphores and mutexes.
|
||||
//!
|
||||
//! This module provides the [`Semaphore`] type, which implements counting semaphores and mutexes.
|
||||
|
||||
use esp_hal::{
|
||||
system::Cpu,
|
||||
time::{Duration, Instant},
|
||||
@ -131,11 +135,13 @@ impl SemaphoreInner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Semaphore and mutex primitives.
|
||||
pub struct Semaphore {
|
||||
inner: NonReentrantMutex<SemaphoreInner>,
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
/// Create a new counting semaphore.
|
||||
pub const fn new_counting(initial: u32, max: u32) -> Self {
|
||||
Semaphore {
|
||||
inner: NonReentrantMutex::new(SemaphoreInner::Counting {
|
||||
@ -146,6 +152,9 @@ impl Semaphore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new mutex.
|
||||
///
|
||||
/// If `recursive` is true, the mutex can be locked multiple times by the same task.
|
||||
pub const fn new_mutex(recursive: bool) -> Self {
|
||||
Semaphore {
|
||||
inner: NonReentrantMutex::new(SemaphoreInner::Mutex {
|
||||
@ -158,10 +167,19 @@ impl Semaphore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to take the semaphore.
|
||||
///
|
||||
/// This is a non-blocking operation.
|
||||
pub fn try_take(&self) -> bool {
|
||||
self.inner.with(|sem| sem.try_take())
|
||||
}
|
||||
|
||||
/// Take the semaphore.
|
||||
///
|
||||
/// This is a blocking operation.
|
||||
///
|
||||
/// If the semaphore is already taken, the task will be blocked until the semaphore is released.
|
||||
/// Recursive mutexes can be locked multiple times by the mutex owner task.
|
||||
pub fn take(&self, timeout_us: Option<u32>) -> bool {
|
||||
let deadline = timeout_us.map(|us| Instant::now() + Duration::from_micros(us as u64));
|
||||
loop {
|
||||
@ -196,10 +214,12 @@ impl Semaphore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current count of the semaphore.
|
||||
pub fn current_count(&self) -> u32 {
|
||||
self.inner.with(|sem| sem.current_count())
|
||||
}
|
||||
|
||||
/// Unlock the semaphore.
|
||||
pub fn give(&self) -> bool {
|
||||
self.inner.with(|sem| {
|
||||
if sem.try_give() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user