You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: website/lab/04/index.md
+63-9Lines changed: 63 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,9 @@ slug: /lab/04
7
7
8
8
This lab will teach you the principles of asynchronous programming, and its application in Embassy.
9
9
10
+
import Tabs from '@theme/Tabs';
11
+
import TabItem from '@theme/TabItem';
12
+
10
13
11
14
## Resources
12
15
@@ -57,10 +60,50 @@ sequenceDiagram
57
60
58
61
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.
59
62
60
-
When thinking of how exactly this works, you would probably think that the task is running on a separate *thread* than the `main` function. Usually this would be the case when developing a normal computer application. Multithreading is possible, but requires a preemptive operating system. Without one, only one thread can independently run per processor core and that means that, since we are using only one core of the RP2350 (which actually has only 2), we would only be able to run **one thread at a time**. So how exactly does the task wait for the button press in parallel with the LED blinking?
63
+
When thinking of how exactly this works, you would probably think that the task is running on a separate *thread* than the `main` function. Usually this would be the case when developing a normal computer application. Multithreading is possible, but requires a preemptive operating system. Without one, only one thread can independently run per processor core and that means that, since we are using only one core of the STM32U545RE (which has only 1) or the RP2350\RP2040 (which actually has only 2), we would only be able to run **one thread at a time**. So how exactly does the task wait for the button press in parallel with the LED blinking?
61
64
Short answer is: it doesn't. In reality, both functions run asynchronously.
62
65
63
66
A task in Embassy is represented by an *asynchronous function*. Asynchronous functions are different from normal functions, in the sense that they allow asynchronous code execution. Let's take an example from the previous lab:
67
+
68
+
:::note
69
+
In the examples below you’ll see `Output<'static>` and `Input<'static>`.
70
+
The `'static` lifetime here means that once the peripherals are initialized, those pins remain valid for the entire lifetime of the firmware. Their role as input or output will not change while the program is running, and Embassy’s async tasks can safely hold on to them without risk of dangling references.
In this example, we notice that both the `button_pressed` and `main` functions are declared as `async`, telling the compiler to treat them as asynchronous functions. Inside the `main` function (which is also a task, actually), we blink the LED:
92
142
93
143
```rust
@@ -101,7 +151,7 @@ loop {
101
151
## `await` keyword
102
152
103
153
After setting the timer, our `main` function would need to wait until the alarm fires after 200 ms. Instead of just waiting and blocking the current and *only* thread of execution, it could allow the thread to do another action in the meantime. This is where the `.await` keyword comes into play.
104
-
When using `.await` inside of an asynchronous function, we are telling a third party (called the **executor**, detailed later) that this action might take more time to finish, so *do something else* until it's ready. Basically, the execution flow of the asynchronous function function is halted exactly where `.await` is used, and the executor starts running another task. In our case, it would halt the main function while waiting for the alarm to go off and it could start running the code inside the `button_pressed` task.
154
+
When using `.await` inside of an asynchronous function, we are telling a third party (called the **executor**, detailed later) that this action might take more time to finish, so *do something else* until it's ready. Basically, the execution flow of the asynchronous function is halted exactly where `.await` is used, and the executor starts running another task. In our case, it would halt the main function while waiting for the alarm to go off and it could start running the code inside the `button_pressed` task.
105
155
```rust
106
156
loop {
107
157
info!("waiting for button press");
@@ -358,14 +408,18 @@ A buzzer is a hardware device that emits sound. There are two types of buzzers:
358
408

359
409
360
410
:::tip
361
-
To control the buzzer, all you need to do is to set the `top` value of the PWM config to match the frequency you want!
411
+
To control the buzzer, you just need to set the PWM so that its period matches the sound frequency you want.
412
+
- On RP2s, this is done by adjusting the `top` value.
413
+
- On STM32U545RE, you achieve the same thing by adjusting the timer’s `SimplePwm` frequency using the [`set_frequency`](https://docs.embassy.dev/embassy-stm32/git/stm32u545re/timer/simple_pwm/struct.SimplePwm.html#method.set_frequency) function.
414
+
415
+
In both cases, the duty cycle controls how strong or “loud” the buzzer sounds.
362
416
:::
363
417
364
418
#### How to wire an RGB LED
365
419
366
420
The buzzer on the development board is connected to a pin in the J9 block.
367
421
368
-

422
+

369
423
370
424
## Exercises
371
425
@@ -378,7 +432,7 @@ while start_time.elapsed().as_millis() < time_interval {}
378
432
You should notice that one of the tasks is not running. Why? (**1p**)
379
433
:::tip
380
434
Use a different task instance for each LED. You can spawn multiple instances of the same task, however you need to specify the pool size with `#[embassy_executor::task(pool_size = 2)]`. Take a look at [task-arena](https://docs.embassy.dev/embassy-executor/git/std/index.html#task-arena) for more info.
381
-
Use [`AnyPin`](https://docs.embassy.dev/embassy-rp/git/rp2040/gpio/struct.AnyPin.html) and blinking frequency parameters for the task.
435
+
Use [`AnyPin`](https://docs.embassy.dev/embassy-stm32/git/stm32u545re/gpio/struct.AnyPin.html) and blinking frequency parameters for the task.
382
436
:::
383
437
384
438
2. Fix the usage of busy waiting from exercise 1 and make the 4 LEDs (YELLOW, RED, GREEN, BLUE) blink at different frequencies. (**1p**)
@@ -396,9 +450,9 @@ Blink:
396
450
1 Hz means once per second.
397
451
:::
398
452
399
-
3. Write a firmware that changes the RED LED's intensity, using switch **SW_4** and switch **SW_5**. Switch **SW_4** will increase the intensity, and switch **SW_5** will decrease it. You will implement this in three ways: (**3p**)
453
+
3. Write a firmware that changes the RED LED's intensity, using switch **S1** and switch **S2**. Switch **S1** will increase the intensity, and switch **S2** will decrease it. You will implement this in three ways: (**3p**)
400
454
401
-
1. Use three tasks : one task will be the `main` to control the LED and another two tasks for each button (one for switch **SW_4**, one for switch **SW_5**). Use a [`Channel`](#channel) to send commands from each button task to the main task.
455
+
1. Use three tasks : one task will be the `main` to control the LED and another two tasks for each button (one for switch **S1**, one for switch **S2**). Use a [`Channel`](#channel) to send commands from each button task to the main task.
402
456
:::tip
403
457
Use an `enum` to define the LED Intensity change command for point i.
404
458
:::
@@ -411,7 +465,7 @@ Blink:
411
465
412
466
413
467
4. Simulates a traffic light using the GREEN, YELLOW and RED LEDs on the board. Normally the traffic light goes from one state based on the time elapsed (Green -> 5s , Yellow Blink (4 times) -> 1s , Red -> 2s ).
414
-
However if the switch **SW4** is pressed the state of traffic light changes immediately as shown in the diagram bellow.(**2p**)
468
+
However if the switch **S1** is pressed the state of traffic light changes immediately as shown in the diagram bellow.(**2p**)
415
469
416
470
```mermaid
417
471
flowchart LR
@@ -439,7 +493,7 @@ However if the switch **SW4** is pressed the state of traffic light changes imme
439
493
For this exercise you only need one task. Define an `enum` to save the traffic light state (`Green`, `Yellow`,`Red`). Use `match` to check the current state of the traffic light. Then you need to wait for two futures, since the traffic light changes its color either because some time has elapsed or because the button was pressed. Use `select` to check which future completes first (`Timer` or button press).
440
494
:::
441
495
442
-
5. Continue exercise 4: this time, instead of using only the switch **SW4** to change the state of traffic light, we will use the switches **SW4** and **SW7** pressed consecutively to trigger a state change of the traffic light. Use `join` to check that both switches were pressed. (**1p**)
496
+
5. Continue exercise 4: this time, instead of using only the switch **S1** to change the state of traffic light, we will use the switches **S1** and **S3** pressed consecutively to trigger a state change of the traffic light. Use `join` to check that both switches were pressed. (**1p**)
443
497
:::note
444
498
The switches don't need to be pressed at the same time, but one after the other. The order does not matter.
0 commit comments