Skip to content

Commit c4f869a

Browse files
Track/embedded (#28)
* added scheduler task * added openocd install disclaimer * initial Embassy track * added eeprom and i2c * fixed broken link * re-added VM mount info * added async subchapter --------- Co-authored-by: Irina Nita <[email protected]>
1 parent 04a311a commit c4f869a

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

docs/embedded/0.Tock/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ This workshop will not work on Windows systems.
1212
You can use the Ubuntu VM we provide [here](https://drive.google.com/file/d/1WSUo29d9Z8bmcjurvkDUmoAgq1TqaW4H/view?usp=sharing) (only works on VirtualBox).
1313
The username and password are both `ipwembedded`.
1414
The VM has the port 3033 forwarded for SSH connection.
15+
16+
If you want to have the VM home directory mounted in Windows, use the following commands (with Admin PowerShell):
17+
18+
```powershell
19+
winget install -e --id WinFsp.WinFsp
20+
winget install -e --id SSHFS-Win.SSHFS-Win
21+
net use X: \\sshfs\ipwembedded@localhost!3033
22+
```
1523
:::
1624

1725
If you did not attend the **Tock Workshop**, please follow the [Setup Tutorial](../../tock_workshop/index.md).

docs/embedded/1.Embassy/index.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ The communication is *half-duplex*. This means that data is transmitted only in
244244
:::
245245
246246
:::info I2C inner works
247-
The `SDA` and `SCL` wires are never actually driven (set to `LOW`/`HIGH`) by the controller/peripherals. The line is controlled by either pulling the line low or releasing the line high.
247+
The `SDA` and `SCL` wires are never actually driven (set to `LOW`/`HIGH`) by the controller/peripherals. The line is controlled by either pulling the line low or releasing the line high.
248248
249249
When the line is *pulled down*, this means that it is connected directly to `GND`. This electronically translates to `LOW`.
250250
@@ -266,12 +266,13 @@ Before the transmission, both the `SCL` and `SDA` lines are set to `HIGH`. First
266266
Depending on the command bit (R/W), either the controller or the target begins to send data over the `SDA` line. Data is sent one byte at a time, and then acknowledged by the receiver. One sequence of a data byte and `ack` is called a *frame*.
267267
268268
During the communication, data can be:
269+
269270
- written to the `SDA` line *only* when `SCL` is `LOW` or
270271
- read from the `SDA` line *only* when `SCL` is `HIGH`.
271272
272273
##### End
273274
274-
To end the transmission, the controller signals a `stop` condition. This is done by releasing the `SCL` line to `HIGH`, and then also releasing the `SDA` line. Since data can be written *only* when `SCL` is `LOW`, the target understands that this is a special event, that means that the communication has ended.
275+
To end the transmission, the controller signals a `stop` condition. This is done by releasing the `SCL` line to `HIGH`, and then also releasing the `SDA` line. Since data can be written *only* when `SCL` is `LOW`, the target understands that this is a special event, that means that the communication has ended.
275276
276277
![i2c_transmission](assets/i2c_7bit_address_transmission.svg)
277278
@@ -353,6 +354,7 @@ i2c.read(TARGET_ADDR, &mut rx_buf).await.unwrap();
353354
To write data to a target, we will be using the `write` **async** function of the I2C driver.
354355
355356
This function also takes 2 parameters:
357+
356358
- the address of the target we are attempting to transmit the data to
357359
- the *transmitting* buffer that contains the data we want to send to the target
358360
@@ -433,3 +435,44 @@ After each complete memory write transaction, the EEPROM an internally-timed wri
433435
#### `eeprom24x` crate
434436
435437
To simplify the interfacing with the non-volatile memory for your **project**, you can use the [`eeprom24x`](https://crates.io/crates/eeprom24x) crate. It is a is a platform agnostic Rust driver for the 24x series serial EEPROM, based on the [`embedded-hal`](https://docs.rs/embedded-hal/1.0.0/embedded_hal/) traits. This means that you will not be able to harness the power of the async executor, and you will need to use the conventional **blocking** API.
438+
439+
## Asynchronous basics
440+
441+
Until now you've only worked with simple (almost) serial programs. However, not all programs can be designed to run serially/sequentially. Handling multiple I/O events concurrently usually requires separate parallel tasks. For example, reading a button press while blinking an LED. A single loop would block the button reading event while waiting for the timer to finish.
442+
443+
To address this issue, we would need to spawn a new task in which we would wait for the button press, while blinking the LED in the `main` function.
444+
445+
The signature of the task that toggles the LED when the button is pressed would need to receive both an `Input` and `Output` as arguments.
446+
447+
```rust title="src/bin/button_task.rs"
448+
#[embassy_executor::task]
449+
async fn button_task(mut led: Output<'static>, mut btn: ExtiInput<'static>) {
450+
loop {
451+
// TODO:
452+
// * `await` button rising edge
453+
// * print message with `info!`
454+
// * toggle LED
455+
}
456+
}
457+
```
458+
459+
In the main function, tasks can be spawned using the `Spawner` provided as an argument.
460+
461+
```rust title="src/bin/button_task.rs"
462+
#[embassy_executor::main]
463+
async fn main(spawner: Spawner) {
464+
let p = embassy_stm32::init(Default::default());
465+
466+
// TODO: initialize LEDs and button
467+
468+
spawner.must_spawn(button_task(led, btn));
469+
470+
loop {
471+
// Handle other LED blink
472+
}
473+
}
474+
```
475+
476+
To periodically blink and LED, you will need to be able to introduce a delay. You can do so manually, by introducing a `for` loop with a number of steps that takes into account the frequency of the processor. The issue with this method is that it would do a *"busy loop"* where the processor spends both time and energy doing unproductive instructions.
477+
478+
This approach does not benefit from the underlying `async` that could schedule another task with available work to be executed. If you want to introduce delays the *`embassy`* way, you can do it using the `Timer` interface, specifically the `Timer::after()` function which takes a `Duration`, or the more direct `after_milis`, `after_secs`, etc.

0 commit comments

Comments
 (0)