So far the example implements a blinking LED in one thread. Let us now extend the application such that the delay time can be adjusted using a button on the board. The value will be adjusted in an interrupt service routing (ISR) and shared with the blinky thread. The thread and the ISR run in different contexts pseudoparallel. Thus, access to the shared delay must be synchronized to prevent inconsistent data.


let mut led = board.led;
let mut button = board.button;
PROC.init(move |c| {
    let delay = Arc::new(Mutex::new(100_u32));1

    let delay_reader = delay.clone();
        .stack(Stack::try_new_in(c, 1024).unwrap())
        .spawn(move || {
            let mut local_delay = 100;
            loop {
                    .map(|s| local_delay = *s).ok();

        .connect_interrupt(stm32f4xx_hal::interrupt::EXTI15_10 as u16)4
        .handler(move |_c| {
                .map(|mut s| {
                    *s = if *s < 1000 { *s + 100 } else { 100 };

Listing: Process initialization with interrupt handler and synchronization.

We use a mutex to synchronize access to the shared delay value. The mutex is allocated on the process heap at 1 in the listing. The delay value is initially set to 100 and encapsulated in the mutex. The blinky loop in the thread tries locking the mutex 2. If that succeeds, the local delay values are updated.

Setting up an interrupt handler is similar to a thread 3. The interrupt number provided by the hardware manufacturer connects the handler to the interrupt 4. When the button is pressed to the handler is called, which clears the remaining interrupt flags. Then, the shared delay value is locked 5 and incremented. In contrast to a thread an interrupt handler does not contain a loop and is kept as short as possible.

This example illustrates the basic usage of the Bern RTOS kernel. The next chapter explains the design and usage of all kernel components.