forked from smartuni/exercises
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "thread-example" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["staticlib"] | ||
|
||
[profile.release] | ||
# Setting the panic mode has little effect on the built code (as Rust on RIOT | ||
# supports no unwinding), but setting it allows builds on native without using | ||
# the nightly-only lang_items feature. | ||
panic = "abort" | ||
|
||
[dependencies] | ||
riot-sys = "0.7.12" | ||
riot-wrappers = { version = "0.8", features = ["set_panic_handler", "panic_handler_format"] } | ||
|
||
rust_riotmodules = { path = "../RIOT/sys/rust_riotmodules/" } | ||
static_cell = "2.1.0" | ||
switch-hal = "0.4.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# name of your application | ||
APPLICATION = thread_example | ||
|
||
# If no BOARD is found in the environment, use this default: | ||
BOARD ?= feather-nrf52840-sense | ||
|
||
# This has to be the absolute path to the RIOT base directory: | ||
RIOTBASE ?= $(CURDIR)/../RIOT | ||
|
||
USEMODULE += ztimer | ||
USEMODULE += ztimer_msec | ||
USEMODULE += ztimer_sec | ||
|
||
# Comment this out to disable code in RIOT that does safety checking | ||
# which is not needed in a production environment but helps in the | ||
# development process: | ||
DEVELHELP ?= 1 | ||
|
||
# Some workarounds are needed in order to get the tutorial running on | ||
# some computers. | ||
-include ../lab_workarounds.mk | ||
|
||
# Change this to 0 show compiler invocation lines by default: | ||
QUIET ?= 1 | ||
|
||
# Tell the build system to use the Rust crate here | ||
FEATURES_REQUIRED += rust_target | ||
APPLICATION_RUST_MODULE = thread_example | ||
BASELIBS += $(APPLICATION_RUST_MODULE).module | ||
|
||
include $(RIOTBASE)/Makefile.include |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# Threads | ||
|
||
Threads are functions designed to be scheduled and executed by the CPU | ||
independently. An example of a thread is the `main()` function. | ||
In single-core MCUs only one thread can have control of the CPU at a given time. | ||
The scheduler is in charge of deciding which thread takes control, depending on | ||
its state and priority. Usually threads are waiting for some event to occur, | ||
and are not 'runnable' (e.g., waiting for a certain time to pass, a packet to | ||
arrive, an external interrupt or an event to be posted on a queue). | ||
|
||
To create threads, we first define a function with the following signature: | ||
|
||
```rust | ||
fn thread_handler() | ||
``` | ||
|
||
The function content is what will be executed whenever our thread is running. | ||
The function gets called from the beginning only once: at the moment of creation. | ||
That is why, commonly, thread handlers contain a loop which would run forever. | ||
|
||
In addition to a handler function, threads require memory: a stack. There local | ||
variables and other information will be stored when performing function calls. | ||
|
||
To create a new thread, we provide stack memory, a function, and spawn a thread: | ||
|
||
```C | ||
fn thread_handler() { | ||
// Some task for our thread | ||
} | ||
|
||
let my_thread = riot_wrappers::thread::spawn( | ||
// Memory stack | ||
thread_stack, | ||
// The thread handler function | ||
&mut thread_handler, | ||
// Human readable name of the thread | ||
c"my_thread", | ||
// Priority of the thread. The lower value, the higher priority | ||
(riot_sys::THREAD_PRIORITY_MAIN - 1) as _, | ||
// Thread flags. By default, enable stack memory usage measurements. | ||
(riot_sys::THREAD_CREATE_STACKTEST) as _, | ||
); | ||
``` | ||
|
||
RIOT scheduler executes a thread until one of the following conditions occurs: | ||
- The thread finishes | ||
- The thread waits for an incoming event (e.g event queue) | ||
- An interrupt source is triggered (button, GPIO pin, timer, etc). In this case, | ||
the scheduler resumes execution in the highest priority thread that does not have | ||
pending events. | ||
|
||
For more information visit the | ||
[thread documentation page](https://doc.riot-os.org/group__core__thread.html). | ||
|
||
To change to this directory from a different exercise, use the following command in the terminal. | ||
|
||
```sh | ||
$ cd ../riot06-threads | ||
``` | ||
|
||
## A note on interrupts | ||
|
||
We usually don't want to perform lengthy tasks from interrupt contexts, as they | ||
will block the execution of all threads. What is commonly done instead, is to | ||
use some signalization mechanism to offload a task to a thread. A good choice is | ||
to use events, as they are allocated by the sender and we are certain that they | ||
will not get lost (as opposed to messages in RIOT). We will see how to utilize | ||
events on the next exercise. | ||
|
||
## Task 1 | ||
|
||
Create a "blinky" thread that toggles the LED1 every 250 milliseconds. | ||
|
||
**1. Create a handler function for your thread.** | ||
**As we want the task to be repeated indefinitely, we include an infinite loop:** | ||
|
||
|
||
```rust | ||
fn blinky_handler() { | ||
let mut led1 = riot_wrappers::led::LED::<1>::new(); | ||
|
||
loop { | ||
println!( | ||
"Thread {}\n", | ||
riot_wrappers::thread::get_pid() | ||
.get_name() | ||
.unwrap_or("unnamed") | ||
); | ||
|
||
led1.on().unwrap(); | ||
Clock::msec().sleep(Duration::from_millis(20)); | ||
led1.off().unwrap(); | ||
Clock::msec().sleep(Duration::from_millis(180)); | ||
} | ||
} | ||
``` | ||
|
||
**2. Define a stack for your thread:** | ||
```rust | ||
use riot_sys::THREAD_STACKSIZE_DEFAULT; | ||
use static_cell::StaticCell; | ||
static THREAD_STACK: StaticCell<[u8; THREAD_STACKSIZE_DEFAULT as usize]> = StaticCell::new(); | ||
``` | ||
|
||
**3. Take possession of the stack, and spawn your thread.** | ||
```rust | ||
let thread_stack = THREAD_STACK.init_with(|| [0; THREAD_STACKSIZE_DEFAULT as usize]); | ||
// Due to a bug in the spawn function | ||
// <https://github.com/RIOT-OS/rust-riot-wrappers/issues/99>, we must provide the pointer | ||
// inside a &'static mut | ||
static STATIC_POINTER: StaticCell<fn()> = StaticCell::new(); | ||
let static_pointer = STATIC_POINTER.init(blinky_handler); | ||
let my_thread = riot_wrappers::thread::spawn( | ||
thread_stack, | ||
static_pointer, | ||
c"blinky", | ||
(riot_sys::THREAD_PRIORITY_MAIN - 1) as _, | ||
(riot_sys::THREAD_CREATE_STACKTEST) as _, | ||
); | ||
``` | ||
|
||
**4. Build and flash your application. Open a serial communication:** | ||
```sh | ||
$ make all flash term | ||
``` | ||
|
||
You should see messages from both threads and the LEDs blinking at different rates. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// SPDX-FileCopyrightText: Christian Amsüss <[email protected]> | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
#![no_std] | ||
|
||
use core::time::Duration; | ||
use riot_wrappers::println; | ||
use riot_wrappers::riot_main; | ||
use riot_wrappers::ztimer::Clock; | ||
|
||
// We are using the LED through a generic "on"/"off" interface. | ||
use switch_hal::OutputSwitch; | ||
|
||
extern crate rust_riotmodules; | ||
|
||
riot_main!(main); | ||
|
||
fn main() { | ||
// Startup delay to ensure the terminal is connected | ||
Clock::sec().sleep(Duration::from_secs(5)); | ||
|
||
println!("Thread example."); | ||
|
||
/* [TASK 1: create the thread here] */ | ||
|
||
let mut led0 = riot_wrappers::led::LED::<0>::new(); | ||
|
||
loop { | ||
println!( | ||
"Thread {}\n", | ||
riot_wrappers::thread::get_pid() | ||
.get_name() | ||
.unwrap_or("unnamed") | ||
); | ||
|
||
led0.on().unwrap(); | ||
Clock::msec().sleep(Duration::from_millis(100)); | ||
led0.off().unwrap(); | ||
Clock::msec().sleep(Duration::from_millis(900)); | ||
} | ||
} |