Crate minimult_cortex_m

Source
Expand description

This crate for Rust provides a minimal multitask library Minimult for Cortex-M microcontrollers.

§Target

Single-core systems of

  • Cortex-M0 / M0+ / M1 (thumbv6m-none-eabi)
  • Cortex-M3 (thumbv7m-none-eabi)
  • Cortex-M4 / M7 (thumbv7em-none-eabi) with FPU (thumbv7em-none-eabihf)
  • Cortex-M23 (thumbv8m.base-none-eabi)
  • Cortex-M33 / M35P (thumbv8m.main-none-eabi) with FPU (thumbv8m.main-none-eabihf)

§Features

  • Task like that of a typical RTOS
    • Minimult can take closures and register them as tasks.
    • Minimult runs into a loop to start dispatching those tasks.
      • Not supported: dynamically creating and spawning.
  • Synchronization
    • idle and kick
      • A task goes into an idle state and other tasks/interrupts wake it up by kicking.
    • MTMsgSender and MTMsgReceiver
      • Task-to-task communication by message passing.
    • MTSharedCh
      • Shared variable among tasks.
  • Priority-based dispatching
    • A higher priority task preempts lower priority tasks.
    • Round-robin dispatching within the same priority tasks.
    • dispatch can be directly requested so that timer-based preemption is also possible.
  • Static memory allocation
    • Minimult doesn’t require a global allocator but reserves a bunch of memory block in advance.

§Examples

§Usage

// Runnable on QEMU ARM

#![no_main]
#![no_std]

use cortex_m::Peripherals;
use cortex_m_rt::entry;
use cortex_m_rt::exception;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::hprintln;
use panic_semihosting as _;

use minimult_cortex_m::*;

#[entry]
fn main() -> !
{
    let mut mem = Minimult::mem::<[u8; 4096]>();
    let mut mt = Minimult::new(&mut mem, 3);

    let mut q = mt.msgq::<u32>(4);
    let (snd, rcv) = q.ch();

    let sh = mt.share::<u32>(0);
    let shch1 = sh.ch();
    let shch2 = sh.ch();

    mt.register(0/*tid*/, 1, 256, || task0(snd));
    mt.register(1/*tid*/, 1, 256, || task1(rcv, shch1));
    mt.register(2/*tid*/, 1, 256, || task2(shch2));

    // SysTick settings
    let cmperi = Peripherals::take().unwrap();
    let mut syst = cmperi.SYST;
    syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
    syst.set_reload(1_000_000);
    syst.clear_current();
    syst.enable_counter();
    syst.enable_interrupt();

    // must be error in terms of lifetime and ownership
    //drop(mem);
    //drop(q);
    //drop(snd);
    //drop(rcv);
    //drop(sh);
    //drop(shch1);
    //drop(shch2);
    
    hprintln!("Minimult run").unwrap();
    mt.run()
}

#[exception]
fn SysTick()
{
    Minimult::kick(0/*tid*/);
}

fn task0(mut snd: MTMsgSender<u32>)
{
    for vsnd in 0.. {
        Minimult::idle();

        hprintln!("task0 send {}", vsnd).unwrap();
        snd.send(vsnd);
    }
}

fn task1(mut rcv: MTMsgReceiver<u32>, shch: MTSharedCh<u32>)
{
    for i in 0.. {
        let vrcv = rcv.receive();

        assert_eq!(i, vrcv);
        hprintln!("task1 touch {}", vrcv).unwrap();
        let mut vtouch = shch.touch();
        *vtouch = vrcv;
    }
}

fn task2(shch: MTSharedCh<u32>)
{
    let mut j = 0;

    while j < 50 {
        let vlook = shch.look();

        assert!(j <= *vlook);
        //hprintln!("task2 look {}", *vlook).unwrap(); // many lines printed
        j = *vlook;
    }

    hprintln!("task2 exit").unwrap();
    debug::exit(debug::EXIT_SUCCESS);
}

§Other Examples

You can find a specific board’s example here. Currently there are very few examples, however.

Structs§

MTMemBlk
Memory block used by Minimult
MTMsgQueue
Message queue for task-to-task communication
MTMsgReceiver
Message receiving channel
MTMsgSender
Message sending channel
MTShared
Shared variable among tasks
MTSharedCh
Shared variable access channel
MTSharedLook
Shared variable’s immutable access scope wrapper
MTSharedTouch
Shared variable’s mutable access scope wrapper
Minimult
Multitasking API

Type Aliases§

MTTaskId
Task identifier
MTTaskPri
Task priority