diff options
Diffstat (limited to 'docs/modules/ROOT')
18 files changed, 331 insertions, 91 deletions
diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index 8f6e05fae..2c282145d 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml | |||
| @@ -6,8 +6,8 @@ version = "0.1.0" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-executor = { version = "0.4.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.5.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.2.0", path = "../../../../../embassy-time", features = ["defmt"] } | 10 | embassy-time = { version = "0.3.0", path = "../../../../../embassy-time", features = ["defmt"] } |
| 11 | embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 11 | embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
diff --git a/docs/modules/ROOT/examples/basic/src/main.rs b/docs/modules/ROOT/examples/basic/src/main.rs index 04170db55..4412712c8 100644 --- a/docs/modules/ROOT/examples/basic/src/main.rs +++ b/docs/modules/ROOT/examples/basic/src/main.rs | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | 3 | ||
| 5 | use defmt::*; | 4 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; |
| 8 | use embassy_nrf::peripherals::P0_13; | ||
| 9 | use embassy_time::{Duration, Timer}; | 7 | use embassy_time::{Duration, Timer}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; // global logger | 8 | use {defmt_rtt as _, panic_probe as _}; // global logger |
| 11 | 9 | ||
| 12 | #[embassy_executor::task] | 10 | #[embassy_executor::task] |
| 13 | async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) { | 11 | async fn blinker(mut led: Output<'static>, interval: Duration) { |
| 14 | loop { | 12 | loop { |
| 15 | led.set_high(); | 13 | led.set_high(); |
| 16 | Timer::after(interval).await; | 14 | Timer::after(interval).await; |
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index dcdb71e7b..64f7e8403 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | |||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | |||
| 8 | cortex-m = "0.7" | 8 | cortex-m = "0.7" |
| 9 | cortex-m-rt = "0.7" | 9 | cortex-m-rt = "0.7" |
| 10 | embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } | 10 | embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } |
| 11 | embassy-executor = { version = "0.4.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } | 11 | embassy-executor = { version = "0.5.0", features = ["arch-cortex-m", "executor-thread"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3.0" | 13 | defmt = "0.3.0" |
| 14 | defmt-rtt = "0.3.0" | 14 | defmt-rtt = "0.3.0" |
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs index 8df632240..004602816 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs | |||
| @@ -1,17 +1,16 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | 3 | ||
| 5 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::exti::ExtiInput; | 5 | use embassy_stm32::exti::ExtiInput; |
| 7 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 8 | ||
| 10 | #[embassy_executor::main] | 9 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 10 | async fn main(_spawner: Spawner) { |
| 12 | let p = embassy_stm32::init(Default::default()); | 11 | let p = embassy_stm32::init(Default::default()); |
| 13 | let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); | 12 | let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); |
| 14 | let mut button = ExtiInput::new(Input::new(p.PC13, Pull::Up), p.EXTI13); | 13 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); |
| 15 | 14 | ||
| 16 | loop { | 15 | loop { |
| 17 | button.wait_for_any_edge().await; | 16 | button.wait_for_any_edge().await; |
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs index aecba0755..743c9d99b 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs | |||
| @@ -6,13 +6,12 @@ use core::cell::RefCell; | |||
| 6 | use cortex_m::interrupt::Mutex; | 6 | use cortex_m::interrupt::Mutex; |
| 7 | use cortex_m::peripheral::NVIC; | 7 | use cortex_m::peripheral::NVIC; |
| 8 | use cortex_m_rt::entry; | 8 | use cortex_m_rt::entry; |
| 9 | use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | 9 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| 10 | use embassy_stm32::peripherals::{PB14, PC13}; | ||
| 11 | use embassy_stm32::{interrupt, pac}; | 10 | use embassy_stm32::{interrupt, pac}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | static BUTTON: Mutex<RefCell<Option<Input<'static, PC13>>>> = Mutex::new(RefCell::new(None)); | 13 | static BUTTON: Mutex<RefCell<Option<Input<'static>>>> = Mutex::new(RefCell::new(None)); |
| 15 | static LED: Mutex<RefCell<Option<Output<'static, PB14>>>> = Mutex::new(RefCell::new(None)); | 14 | static LED: Mutex<RefCell<Option<Output<'static>>>> = Mutex::new(RefCell::new(None)); |
| 16 | 15 | ||
| 17 | #[entry] | 16 | #[entry] |
| 18 | fn main() -> ! { | 17 | fn main() -> ! { |
| @@ -62,14 +61,14 @@ fn EXTI15_10() { | |||
| 62 | 61 | ||
| 63 | const PORT: u8 = 2; | 62 | const PORT: u8 = 2; |
| 64 | const PIN: usize = 13; | 63 | const PIN: usize = 13; |
| 65 | fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool { | 64 | fn check_interrupt(_pin: &mut Input<'static>) -> bool { |
| 66 | let exti = pac::EXTI; | 65 | let exti = pac::EXTI; |
| 67 | let pin = PIN; | 66 | let pin = PIN; |
| 68 | let lines = exti.pr(0).read(); | 67 | let lines = exti.pr(0).read(); |
| 69 | lines.line(pin) | 68 | lines.line(pin) |
| 70 | } | 69 | } |
| 71 | 70 | ||
| 72 | fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) { | 71 | fn clear_interrupt(_pin: &mut Input<'static>) { |
| 73 | let exti = pac::EXTI; | 72 | let exti = pac::EXTI; |
| 74 | let pin = PIN; | 73 | let pin = PIN; |
| 75 | let mut lines = exti.pr(0).read(); | 74 | let mut lines = exti.pr(0).read(); |
| @@ -77,7 +76,7 @@ fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) { | |||
| 77 | exti.pr(0).write_value(lines); | 76 | exti.pr(0).write_value(lines); |
| 78 | } | 77 | } |
| 79 | 78 | ||
| 80 | fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) { | 79 | fn enable_interrupt(_pin: &mut Input<'static>) { |
| 81 | cortex_m::interrupt::free(|_| { | 80 | cortex_m::interrupt::free(|_| { |
| 82 | let rcc = pac::RCC; | 81 | let rcc = pac::RCC; |
| 83 | rcc.apb2enr().modify(|w| w.set_syscfgen(true)); | 82 | rcc.apb2enr().modify(|w| w.set_syscfgen(true)); |
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index fabb80e31..44b0eddb9 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc | |||
| @@ -3,10 +3,11 @@ | |||
| 3 | ** xref:project_structure.adoc[Project Structure] | 3 | ** xref:project_structure.adoc[Project Structure] |
| 4 | ** xref:new_project.adoc[Starting a new Embassy project] | 4 | ** xref:new_project.adoc[Starting a new Embassy project] |
| 5 | ** xref:best_practices.adoc[Best Practices] | 5 | ** xref:best_practices.adoc[Best Practices] |
| 6 | * xref:layer_by_layer.adoc[Bare metal to async] | ||
| 7 | * xref:runtime.adoc[Executor] | 6 | * xref:runtime.adoc[Executor] |
| 8 | * xref:delaying_a_task.adoc[Delaying a Task] | 7 | * xref::time_keeping.adoc[Time-keeping] |
| 8 | * xref:sharing_peripherals.adoc[Sharing peripherals] | ||
| 9 | * xref:hal.adoc[HAL] | 9 | * xref:hal.adoc[HAL] |
| 10 | ** xref:layer_by_layer.adoc[Anatomy of an async HAL] | ||
| 10 | ** xref:nrf.adoc[nRF] | 11 | ** xref:nrf.adoc[nRF] |
| 11 | ** xref:stm32.adoc[STM32] | 12 | ** xref:stm32.adoc[STM32] |
| 12 | * xref:bootloader.adoc[Bootloader] | 13 | * xref:bootloader.adoc[Bootloader] |
diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index 95792d5a0..d5aad806d 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc | |||
| @@ -17,22 +17,13 @@ The first thing you’ll notice are two attributes at the top of the file. These | |||
| 17 | include::example$basic/src/main.rs[lines="1..2"] | 17 | include::example$basic/src/main.rs[lines="1..2"] |
| 18 | ---- | 18 | ---- |
| 19 | 19 | ||
| 20 | === Rust Nightly | ||
| 21 | |||
| 22 | The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly: | ||
| 23 | |||
| 24 | [source,rust] | ||
| 25 | ---- | ||
| 26 | include::example$basic/src/main.rs[lines="3"] | ||
| 27 | ---- | ||
| 28 | |||
| 29 | === Dealing with errors | 20 | === Dealing with errors |
| 30 | 21 | ||
| 31 | Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal: | 22 | Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal: |
| 32 | 23 | ||
| 33 | [source,rust] | 24 | [source,rust] |
| 34 | ---- | 25 | ---- |
| 35 | include::example$basic/src/main.rs[lines="10"] | 26 | include::example$basic/src/main.rs[lines="8"] |
| 36 | ---- | 27 | ---- |
| 37 | 28 | ||
| 38 | === Task declaration | 29 | === Task declaration |
| @@ -41,7 +32,7 @@ After a bit of import declaration, the tasks run by the application should be de | |||
| 41 | 32 | ||
| 42 | [source,rust] | 33 | [source,rust] |
| 43 | ---- | 34 | ---- |
| 44 | include::example$basic/src/main.rs[lines="12..20"] | 35 | include::example$basic/src/main.rs[lines="10..18"] |
| 45 | ---- | 36 | ---- |
| 46 | 37 | ||
| 47 | An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. | 38 | An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. |
| @@ -56,7 +47,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals` | |||
| 56 | 47 | ||
| 57 | [source,rust] | 48 | [source,rust] |
| 58 | ---- | 49 | ---- |
| 59 | include::example$basic/src/main.rs[lines="22..-1"] | 50 | include::example$basic/src/main.rs[lines="20..-1"] |
| 60 | ---- | 51 | ---- |
| 61 | 52 | ||
| 62 | What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following: | 53 | What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following: |
diff --git a/docs/modules/ROOT/pages/best_practices.adoc b/docs/modules/ROOT/pages/best_practices.adoc index 1e02f9ba9..bfcedec06 100644 --- a/docs/modules/ROOT/pages/best_practices.adoc +++ b/docs/modules/ROOT/pages/best_practices.adoc | |||
| @@ -3,8 +3,10 @@ | |||
| 3 | Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework. | 3 | Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework. |
| 4 | 4 | ||
| 5 | == Passing Buffers by Reference | 5 | == Passing Buffers by Reference |
| 6 | It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack. | 6 | It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], |
| 7 | This, however, can easily blow up your stack if you are not careful. | 7 | to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't |
| 8 | want to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up | ||
| 9 | your stack if you are not careful. | ||
| 8 | 10 | ||
| 9 | Consider the following example: | 11 | Consider the following example: |
| 10 | [,rust] | 12 | [,rust] |
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index b7215e52a..3b0cdb182 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc | |||
| @@ -45,6 +45,8 @@ The BOOTLOADER_STATE partition must be big enough to store one word per page in | |||
| 45 | 45 | ||
| 46 | The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. | 46 | The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. |
| 47 | 47 | ||
| 48 | NOTE: The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application. | ||
| 49 | |||
| 48 | === FirmwareUpdater | 50 | === FirmwareUpdater |
| 49 | 51 | ||
| 50 | The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call. | 52 | The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call. |
| @@ -91,4 +93,4 @@ cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed | |||
| 91 | tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed | 93 | tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed |
| 92 | ---- | 94 | ---- |
| 93 | 95 | ||
| 94 | Remember, guard the `$SECRETS_DIR/key.sec` key as compromising it means that another party can sign your firmware. \ No newline at end of file | 96 | Remember, guard the `$SECRETS_DIR/key.sec` key as compromising it means that another party can sign your firmware. |
diff --git a/docs/modules/ROOT/pages/delaying_a_task.adoc b/docs/modules/ROOT/pages/delaying_a_task.adoc deleted file mode 100644 index 3171e3515..000000000 --- a/docs/modules/ROOT/pages/delaying_a_task.adoc +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | = Delaying a Task | ||
| 2 | |||
| 3 | In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure | ||
| 4 | that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides an abstraction | ||
| 5 | to delay the current task for a specified interval of time. | ||
| 6 | |||
| 7 | Timing is serviced by the `embassy::time::Timer` struct, which provides two timing methods. | ||
| 8 | |||
| 9 | `Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time. | ||
| 10 | `Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created. | ||
| 11 | |||
| 12 | An example of a delay is provided as follows: | ||
| 13 | |||
| 14 | [,rust] | ||
| 15 | ---- | ||
| 16 | use embassy::executor::{task, Executor}; | ||
| 17 | use embassy::time::{Duration, Timer}; | ||
| 18 | |||
| 19 | #[task] | ||
| 20 | /// Task that ticks periodically | ||
| 21 | async fn tick_periodic() -> ! { | ||
| 22 | loop { | ||
| 23 | rprintln!("tick!"); | ||
| 24 | // async sleep primitive, suspends the task for 500ms. | ||
| 25 | Timer::after(Duration::from_millis(500)).await; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | ---- \ No newline at end of file | ||
diff --git a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc index a1c31bfc7..85ad7f4a2 100644 --- a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc +++ b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc | |||
| @@ -7,3 +7,5 @@ Here are known examples of real-world projects which make use of Embassy. Feel f | |||
| 7 | * link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device | 7 | * link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device |
| 8 | ** Targets the ESP32-S3 or ESP32-C6 MCU | 8 | ** Targets the ESP32-S3 or ESP32-C6 MCU |
| 9 | * The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL | 9 | * The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL |
| 10 | ** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system] | ||
| 11 | *** Targets nRF52 and uses nrf-softdevice | ||
diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index d1a012978..6b5e6d009 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc | |||
| @@ -29,13 +29,12 @@ If you see an error like this: | |||
| 29 | 29 | ||
| 30 | You are likely missing some features of the `embassy-executor` crate. | 30 | You are likely missing some features of the `embassy-executor` crate. |
| 31 | 31 | ||
| 32 | For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate: | 32 | For Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate: |
| 33 | 33 | ||
| 34 | * `arch-cortex-m` | 34 | * `arch-cortex-m` |
| 35 | * `executor-thread` | 35 | * `executor-thread` |
| 36 | * `nightly` | ||
| 37 | 36 | ||
| 38 | For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate]. | 37 | For ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate]. |
| 39 | 38 | ||
| 40 | == Why is my binary so big? | 39 | == Why is my binary so big? |
| 41 | 40 | ||
| @@ -44,11 +43,12 @@ The first step to managing your binary size is to set up your link:https://doc.r | |||
| 44 | [source,toml] | 43 | [source,toml] |
| 45 | ---- | 44 | ---- |
| 46 | [profile.release] | 45 | [profile.release] |
| 47 | debug = false | ||
| 48 | lto = true | 46 | lto = true |
| 49 | opt-level = "s" | 47 | opt-level = "s" |
| 50 | incremental = false | 48 | incremental = false |
| 51 | codegen-units = 1 | 49 | codegen-units = 1 |
| 50 | # note: debug = true is okay - debuginfo isn't flashed to the device! | ||
| 51 | debug = true | ||
| 52 | ---- | 52 | ---- |
| 53 | 53 | ||
| 54 | All of these flags are elaborated on in the Rust Book page linked above. | 54 | All of these flags are elaborated on in the Rust Book page linked above. |
| @@ -118,21 +118,31 @@ features = [ | |||
| 118 | ] | 118 | ] |
| 119 | ---- | 119 | ---- |
| 120 | 120 | ||
| 121 | If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source: | ||
| 122 | |||
| 123 | [source,rust] | ||
| 124 | ---- | ||
| 125 | use embassy_stm32 as _; | ||
| 126 | ---- | ||
| 127 | |||
| 121 | == Error: `Only one package in the dependency graph may specify the same links value.` | 128 | == Error: `Only one package in the dependency graph may specify the same links value.` |
| 122 | 129 | ||
| 123 | You have multiple versions of the same crate in your dependency tree. This means that some of your | 130 | You have multiple versions of the same crate in your dependency tree. This means that some of your |
| 124 | embassy crates are coming from crates.io, and some from git, each of them pulling in a different set | 131 | embassy crates are coming from crates.io, and some from git, each of them pulling in a different set |
| 125 | of dependencies. | 132 | of dependencies. |
| 126 | 133 | ||
| 127 | To resolve this issue, make sure to only use a single source for all your embassy crates! To do this, | 134 | To resolve this issue, make sure to only use a single source for all your embassy crates! |
| 128 | you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`. | 135 | To do this, you should patch your dependencies to use git sources using `[patch.crates.io]` |
| 136 | and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`. | ||
| 129 | 137 | ||
| 130 | Example: | 138 | Example: |
| 131 | 139 | ||
| 132 | [source,toml] | 140 | [source,toml] |
| 133 | ---- | 141 | ---- |
| 134 | [patch.crates-io] | 142 | [patch.crates-io] |
| 135 | embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } | 143 | embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } |
| 144 | embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } | ||
| 145 | # embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } | ||
| 136 | ---- | 146 | ---- |
| 137 | 147 | ||
| 138 | Note that the git revision should match any other embassy patches or git dependencies that you are using! | 148 | Note that the git revision should match any other embassy patches or git dependencies that you are using! |
| @@ -152,4 +162,49 @@ Note that the git revision should match any other embassy patches or git depende | |||
| 152 | * When using `InterruptExecutor`: | 162 | * When using `InterruptExecutor`: |
| 153 | ** disable `executor-thread` | 163 | ** disable `executor-thread` |
| 154 | ** make `main`` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }` | 164 | ** make `main`` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }` |
| 155 | ** *Note:* If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor. \ No newline at end of file | 165 | ** *Note:* If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor. |
| 166 | |||
| 167 | == How do I set up the task arenas on stable? | ||
| 168 | |||
| 169 | When you aren't using the `nightly` feature of `embassy-executor`, the executor uses a bump allocator, which may require configuration. | ||
| 170 | |||
| 171 | Something like this error will occur at **compile time** if the task arena is *too large* for the target's RAM: | ||
| 172 | |||
| 173 | [source,plain] | ||
| 174 | ---- | ||
| 175 | rust-lld: error: section '.bss' will not fit in region 'RAM': overflowed by _ bytes | ||
| 176 | rust-lld: error: section '.uninit' will not fit in region 'RAM': overflowed by _ bytes | ||
| 177 | ---- | ||
| 178 | |||
| 179 | And this message will appear at **runtime** if the task arena is *too small* for the tasks running: | ||
| 180 | |||
| 181 | [source,plain] | ||
| 182 | ---- | ||
| 183 | ERROR panicked at 'embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/' | ||
| 184 | ---- | ||
| 185 | |||
| 186 | NOTE: If all tasks are spawned at startup, this panic will occur immediately. | ||
| 187 | |||
| 188 | Check out link:https://docs.embassy.dev/embassy-executor/git/cortex-m/index.html#task-arena[Task Arena Documentation] for more details. | ||
| 189 | |||
| 190 | == Can I use manual ISRs alongside Embassy? | ||
| 191 | |||
| 192 | Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. Simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project]. | ||
| 193 | |||
| 194 | == How can I measure resource usage (CPU, RAM, etc.)? | ||
| 195 | |||
| 196 | === For CPU Usage: | ||
| 197 | |||
| 198 | There are a couple techniques that have been documented, generally you want to measure how long you are spending in the idle or low priority loop. | ||
| 199 | |||
| 200 | We need to document specifically how to do this in embassy, but link:https://blog.japaric.io/cpu-monitor/[this older post] describes the general process. | ||
| 201 | |||
| 202 | If you end up doing this, please update this section with more specific examples! | ||
| 203 | |||
| 204 | === For Static Memory Usage | ||
| 205 | |||
| 206 | Tools like `cargo size` and `cargo nm` can tell you the size of any globals or other static usage. Specifically you will want to see the size of the `.data` and `.bss` sections, which together make up the total global/static memory usage. | ||
| 207 | |||
| 208 | === For Max Stack Usage | ||
| 209 | |||
| 210 | Check out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details. | ||
diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index ab819ac2a..73cb5530d 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | So you want to try Embassy, great! To get started, there are a few tools you need to install: | 3 | So you want to try Embassy, great! To get started, there are a few tools you need to install: |
| 4 | 4 | ||
| 5 | * link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code. | 5 | * link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code. |
| 6 | * link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well. | 6 | * link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well. |
| 7 | 7 | ||
| 8 | If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples. | 8 | If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples. |
| 9 | 9 | ||
| @@ -82,19 +82,19 @@ If everything worked correctly, you should see a blinking LED on your board, and | |||
| 82 | └─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27 | 82 | └─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27 |
| 83 | ---- | 83 | ---- |
| 84 | 84 | ||
| 85 | NOTE: How does the `cargo run` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips. | 85 | NOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips. |
| 86 | 86 | ||
| 87 | === It didn’t work! | 87 | === It didn’t work! |
| 88 | 88 | ||
| 89 | If you hare having issues when running `cargo run --release`, please check the following: | 89 | If you hare having issues when running `+cargo run --release+`, please check the following: |
| 90 | 90 | ||
| 91 | * You are specifying the correct `--chip on the command line``, OR | 91 | * You are specifying the correct `+--chip+` on the command line, OR |
| 92 | * You have set `.cargo/config.toml`'s run line to the correct chip, AND | 92 | * You have set `+.cargo/config.toml+`’s run line to the correct chip, AND |
| 93 | * You have changed `examples/Cargo.toml`'s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature) | 93 | * You have changed `+examples/Cargo.toml+`’s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature) |
| 94 | 94 | ||
| 95 | At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin. | 95 | At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin. |
| 96 | 96 | ||
| 97 | If you are trying to run an example with `cargo run --release` and you see the following output: | 97 | If you are trying to run an example with `+cargo run --release+` and you see the following output: |
| 98 | [source] | 98 | [source] |
| 99 | ---- | 99 | ---- |
| 100 | 0.000000 INFO Hello World! | 100 | 0.000000 INFO Hello World! |
| @@ -115,6 +115,22 @@ To get rid of the frame-index error add the following to your `Cargo.toml`: | |||
| 115 | debug = 2 | 115 | debug = 2 |
| 116 | ---- | 116 | ---- |
| 117 | 117 | ||
| 118 | If you’re getting an extremely long error message containing something like the following: | ||
| 119 | |||
| 120 | [source] | ||
| 121 | ---- | ||
| 122 | error[E0463]: can't find crate for `std` | ||
| 123 | | | ||
| 124 | = note: the `thumbv6m-none-eabi` target may not support the standard library | ||
| 125 | = note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]` | ||
| 126 | ---- | ||
| 127 | |||
| 128 | Make sure that you didn’t accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs]. | ||
| 129 | |||
| 130 | If you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked. | ||
| 131 | |||
| 132 | If you’re using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state]. | ||
| 133 | |||
| 118 | If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. | 134 | If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. |
| 119 | 135 | ||
| 120 | == What's next? | 136 | == What's next? |
| @@ -124,3 +140,4 @@ Congratulations, you have your first Embassy application running! Here are some | |||
| 124 | * Read more about the xref:runtime.adoc[executor]. | 140 | * Read more about the xref:runtime.adoc[executor]. |
| 125 | * Read more about the xref:hal.adoc[HAL]. | 141 | * Read more about the xref:hal.adoc[HAL]. |
| 126 | * Start xref:basic_application.adoc[writing your application]. | 142 | * Start xref:basic_application.adoc[writing your application]. |
| 143 | * Learn how to xref:new_project.adoc[start a new embassy project by adapting an example]. | ||
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 6fba80eda..e17adbbd7 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc | |||
| @@ -41,7 +41,7 @@ The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implem | |||
| 41 | The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. | 41 | The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. |
| 42 | 42 | ||
| 43 | === LoRa | 43 | === LoRa |
| 44 | link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation. | 44 | link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others. |
| 45 | 45 | ||
| 46 | === USB | 46 | === USB |
| 47 | link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. | 47 | link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. |
diff --git a/docs/modules/ROOT/pages/new_project.adoc b/docs/modules/ROOT/pages/new_project.adoc index ce139ed8d..320966bb6 100644 --- a/docs/modules/ROOT/pages/new_project.adoc +++ b/docs/modules/ROOT/pages/new_project.adoc | |||
| @@ -1,6 +1,17 @@ | |||
| 1 | = Starting a new Embassy project | 1 | = Starting a new Embassy project |
| 2 | 2 | ||
| 3 | Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one you’re targeting. | 3 | Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. |
| 4 | |||
| 5 | There are some tools for generating Embassy projects: (WIP) | ||
| 6 | |||
| 7 | ==== CLI | ||
| 8 | - link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF) | ||
| 9 | |||
| 10 | ==== cargo-generate | ||
| 11 | - link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) | ||
| 12 | - link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) | ||
| 13 | |||
| 14 | But if you want to start from scratch: | ||
| 4 | 15 | ||
| 5 | As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes. | 16 | As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes. |
| 6 | 17 | ||
| @@ -166,13 +177,13 @@ should result in a blinking LED (if there’s one attached to the pin in `src/ma | |||
| 166 | Erasing sectors âś” [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s ) | 177 | Erasing sectors âś” [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s ) |
| 167 | Programming pages âś” [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s | 178 | Programming pages âś” [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s |
| 168 | 0.000000 TRACE BDCR configured: 00008200 | 179 | 0.000000 TRACE BDCR configured: 00008200 |
| 169 | └─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117 | 180 | └─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117 |
| 170 | 0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) } | 181 | 0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) } |
| 171 | └─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130 | 182 | └─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130 |
| 172 | 0.000000 INFO Hello World! | 183 | 0.000000 INFO Hello World! |
| 173 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14 | 184 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14 |
| 174 | 0.000091 INFO high | 185 | 0.000091 INFO high |
| 175 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19 | 186 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19 |
| 176 | 0.300201 INFO low | 187 | 0.300201 INFO low |
| 177 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23 | 188 | └─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23 |
| 178 | ---- \ No newline at end of file | 189 | ---- |
diff --git a/docs/modules/ROOT/pages/project_structure.adoc b/docs/modules/ROOT/pages/project_structure.adoc index 3e6008ec4..2adfcc1df 100644 --- a/docs/modules/ROOT/pages/project_structure.adoc +++ b/docs/modules/ROOT/pages/project_structure.adoc | |||
| @@ -18,7 +18,7 @@ my-project | |||
| 18 | |- rust-toolchain.toml | 18 | |- rust-toolchain.toml |
| 19 | ---- | 19 | ---- |
| 20 | 20 | ||
| 21 | === .cargo/config.toml | 21 | == .cargo/config.toml |
| 22 | 22 | ||
| 23 | This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device. | 23 | This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device. |
| 24 | 24 | ||
| @@ -36,17 +36,22 @@ target = "thumbv6m-none-eabi" # <-change for your platform | |||
| 36 | DEFMT_LOG = "trace" # <- can change to info, warn, or error | 36 | DEFMT_LOG = "trace" # <- can change to info, warn, or error |
| 37 | ---- | 37 | ---- |
| 38 | 38 | ||
| 39 | === build.rs | 39 | == build.rs |
| 40 | 40 | ||
| 41 | This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. | 41 | This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. |
| 42 | 42 | ||
| 43 | === Cargo.toml | 43 | == Cargo.toml |
| 44 | 44 | ||
| 45 | This is your manifest file, where you can configure all of the embassy components to use the features you need. | 45 | This is your manifest file, where you can configure all of the embassy components to use the features you need. |
| 46 | 46 | ||
| 47 | TODO: someone should exhaustively describe every feature for every component! | 47 | ==== Features |
| 48 | ===== Time | ||
| 49 | - tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes. | ||
| 50 | - defmt-timestamp-uptime: defmt log entries will display the uptime in seconds. | ||
| 48 | 51 | ||
| 49 | === memory.x | 52 | ...more to come |
| 53 | |||
| 54 | == memory.x | ||
| 50 | 55 | ||
| 51 | This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x. | 56 | This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x. |
| 52 | 57 | ||
| @@ -63,7 +68,7 @@ MEMORY | |||
| 63 | } | 68 | } |
| 64 | ---- | 69 | ---- |
| 65 | 70 | ||
| 66 | === rust-toolchain.toml | 71 | == rust-toolchain.toml |
| 67 | 72 | ||
| 68 | This file configures the rust version and configuration to use. | 73 | This file configures the rust version and configuration to use. |
| 69 | 74 | ||
diff --git a/docs/modules/ROOT/pages/sharing_peripherals.adoc b/docs/modules/ROOT/pages/sharing_peripherals.adoc new file mode 100644 index 000000000..fcba0e27b --- /dev/null +++ b/docs/modules/ROOT/pages/sharing_peripherals.adoc | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | = Sharing peripherals between tasks | ||
| 2 | |||
| 3 | Often times, more than one task needs access to the same resource (pin, communication interface, etc.). Embassy provides many different synchronization primitives in the link:https://crates.io/crates/embassy-sync[embassy-sync] crate. | ||
| 4 | |||
| 5 | The following examples shows different ways to use the on-board LED on a Raspberry Pi Pico board by two tasks simultaneously. | ||
| 6 | |||
| 7 | == Sharing using a Mutex | ||
| 8 | |||
| 9 | Using mutual exclusion is the simplest way to share a peripheral. | ||
| 10 | |||
| 11 | [,rust] | ||
| 12 | ---- | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_rp::gpio; | ||
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 17 | use embassy_sync::mutex::Mutex; | ||
| 18 | use embassy_time::{Duration, Ticker}; | ||
| 19 | use gpio::{AnyPin, Level, Output}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>>; | ||
| 23 | static LED: LedType = Mutex::new(None); | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(spawner: Spawner) { | ||
| 27 | let p = embassy_rp::init(Default::default()); | ||
| 28 | // set the content of the global LED reference to the real LED pin | ||
| 29 | let led = Output::new(AnyPin::from(p.PIN_25), Level::High); | ||
| 30 | // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the | ||
| 31 | // Mutex is released | ||
| 32 | { | ||
| 33 | *(LED.lock().await) = Some(led); | ||
| 34 | } | ||
| 35 | let dt = 100 * 1_000_000; | ||
| 36 | let k = 1.003; | ||
| 37 | |||
| 38 | unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt)))); | ||
| 39 | unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64)))); | ||
| 40 | } | ||
| 41 | |||
| 42 | // A pool size of 2 means you can spawn two instances of this task. | ||
| 43 | #[embassy_executor::task(pool_size = 2)] | ||
| 44 | async fn toggle_led(led: &'static LedType, delay: Duration) { | ||
| 45 | let mut ticker = Ticker::every(delay); | ||
| 46 | loop { | ||
| 47 | { | ||
| 48 | let mut led_unlocked = led.lock().await; | ||
| 49 | if let Some(pin_ref) = led_unlocked.as_mut() { | ||
| 50 | pin_ref.toggle(); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | ticker.next().await; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | ---- | ||
| 57 | |||
| 58 | The structure facilitating access to the resource is the defined `LedType`. | ||
| 59 | |||
| 60 | === Why so complicated | ||
| 61 | |||
| 62 | Unwrapping the layers gives insight into why each one is needed. | ||
| 63 | |||
| 64 | ==== `Mutex<RawMutexType, T>` | ||
| 65 | |||
| 66 | The mutex is there so if one task gets the resource first and begins modifying it, all other tasks wanting to write will have to wait (the `led.lock().await` will return immediately if no task has locked the mutex, and will block if it is accessed somewhere else). | ||
| 67 | |||
| 68 | ==== `Option<T>` | ||
| 69 | |||
| 70 | The `LED` variable needs to be defined outside the main task as references accepted by tasks need to be `'static`. However, if it is outside the main task, it cannot be initialised to point to any pin, as the pins themselves are not initialised. Thus, it is set to `None`. | ||
| 71 | |||
| 72 | ==== `Output<AnyPin>` | ||
| 73 | |||
| 74 | To indicate that the pin will be set to an Output. The `AnyPin` could have been `embassy_rp::peripherals::PIN_25`, however this option lets the `toggle_led` function be more generic. | ||
| 75 | |||
| 76 | == Sharing using a Channel | ||
| 77 | |||
| 78 | A channel is another way to ensure exclusive access to a resource. Using a channel is great in the cases where the access can happen at a later point in time, allowing you to enqueue operations and do other things. | ||
| 79 | |||
| 80 | [,rust] | ||
| 81 | ---- | ||
| 82 | use defmt::*; | ||
| 83 | use embassy_executor::Spawner; | ||
| 84 | use embassy_rp::gpio; | ||
| 85 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 86 | use embassy_sync::channel::{Channel, Sender}; | ||
| 87 | use embassy_time::{Duration, Ticker}; | ||
| 88 | use gpio::{AnyPin, Level, Output}; | ||
| 89 | use {defmt_rtt as _, panic_probe as _}; | ||
| 90 | |||
| 91 | enum LedState { | ||
| 92 | Toggle, | ||
| 93 | } | ||
| 94 | static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new(); | ||
| 95 | |||
| 96 | #[embassy_executor::main] | ||
| 97 | async fn main(spawner: Spawner) { | ||
| 98 | let p = embassy_rp::init(Default::default()); | ||
| 99 | let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High); | ||
| 100 | |||
| 101 | let dt = 100 * 1_000_000; | ||
| 102 | let k = 1.003; | ||
| 103 | |||
| 104 | unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt)))); | ||
| 105 | unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos((dt as f64 * k) as u64)))); | ||
| 106 | |||
| 107 | loop { | ||
| 108 | match CHANNEL.receive().await { | ||
| 109 | LedState::Toggle => led.toggle(), | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | // A pool size of 2 means you can spawn two instances of this task. | ||
| 115 | #[embassy_executor::task(pool_size = 2)] | ||
| 116 | async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) { | ||
| 117 | let mut ticker = Ticker::every(delay); | ||
| 118 | loop { | ||
| 119 | control.send(LedState::Toggle).await; | ||
| 120 | ticker.next().await; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | ---- | ||
| 124 | |||
| 125 | This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure | ||
| 126 | that the operation is ecompleted before continuing to do other work in your task. | ||
diff --git a/docs/modules/ROOT/pages/time_keeping.adoc b/docs/modules/ROOT/pages/time_keeping.adoc new file mode 100644 index 000000000..5068216ed --- /dev/null +++ b/docs/modules/ROOT/pages/time_keeping.adoc | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | = Time-keeping | ||
| 2 | |||
| 3 | In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure | ||
| 4 | that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides abstractions | ||
| 5 | to delay the current task for a specified interval of time. | ||
| 6 | |||
| 7 | The interface for time-keeping in Embassy is handled by the link:https://crates.io/crates/embassy-time[embassy-time] crate. The types can be used with the internal | ||
| 8 | timer queue in link:https://crates.io/crates/embassy-executor[embassy-executor] or a custom timer queue implementation. | ||
| 9 | |||
| 10 | == Timer | ||
| 11 | |||
| 12 | The `embassy::time::Timer` type provides two timing methods. | ||
| 13 | |||
| 14 | `Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time. | ||
| 15 | `Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created. | ||
| 16 | |||
| 17 | An example of a delay is provided as follows: | ||
| 18 | |||
| 19 | [,rust] | ||
| 20 | ---- | ||
| 21 | use embassy::executor::{task, Executor}; | ||
| 22 | use embassy::time::{Duration, Timer}; | ||
| 23 | |||
| 24 | #[task] | ||
| 25 | /// Task that ticks periodically | ||
| 26 | async fn tick_periodic() -> ! { | ||
| 27 | loop { | ||
| 28 | rprintln!("tick!"); | ||
| 29 | // async sleep primitive, suspends the task for 500ms. | ||
| 30 | Timer::after(Duration::from_millis(500)).await; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | ---- | ||
| 34 | |||
| 35 | == Delay | ||
| 36 | |||
| 37 | The `embassy::time::Delay` type provides an implementation of the link:https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html[embedded-hal] and | ||
| 38 | link:https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/index.html[embedded-hal-async] traits. This can be used for drivers | ||
| 39 | that expect a generic delay implementation to be provided. | ||
| 40 | |||
| 41 | An example of how this can be used: | ||
| 42 | |||
| 43 | [,rust] | ||
| 44 | ---- | ||
| 45 | use embassy::executor::{task, Executor}; | ||
| 46 | |||
| 47 | #[task] | ||
| 48 | /// Task that ticks periodically | ||
| 49 | async fn tick_periodic() -> ! { | ||
| 50 | loop { | ||
| 51 | rprintln!("tick!"); | ||
| 52 | // async sleep primitive, suspends the task for 500ms. | ||
| 53 | generic_delay(embassy::time::Delay).await | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | async fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) { | ||
| 58 | delay.delay_ms(500).await; | ||
| 59 | } | ||
| 60 | ---- | ||
