|
|
|
### Multithreaded LED Blink
|
|
|
|
|
|
|
|
In this example two threads make two LEDs blink. The first one changes state two times every second while the second one is set "high" only when the on-board user button is pressed (the discovery's blue button).
|
|
|
|
|
|
|
|
Miosix provides two different interfaces for creating a thread: the `pthread` API and a navite one. Here we will consider only the native one.
|
|
|
|
|
|
|
|
In order to spawn a new thread, the `create()` function is needed and it takes the following parameters
|
|
|
|
* `startfunc`: the entry point function for the thread. It is the function that will be executed by the thread.
|
|
|
|
* `stacksize`: size of thread stack, its minimum is the constant `STACK_MIN`. The size of the stack must be divisible by 4, otherwise it will be rounded.
|
|
|
|
* `priority`: the thread's priority, between `0` (lower) and `PRIORITY_MAX-1` (higher).
|
|
|
|
* `argv`: a `void*` pointer that is passed as a parameter to the entry point function.
|
|
|
|
* `options`: thread options, such ad `Thread::JOINABLE`. By default native threads are not joinable, which means they will never end. The `join()` method allows to wait for a thread to end its execution.
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
#include "miosix.h"
|
|
|
|
|
|
|
|
using namespace miosix;
|
|
|
|
|
|
|
|
// red and blue on-board LEDs in STM32F407VG Discovery board
|
|
|
|
typedef Gpio<GPIOD_BASE, 14> led1;
|
|
|
|
typedef Gpio<GPIOD_BASE, 15> led2;
|
|
|
|
// On-board user button in STM32F407VG Discovery board (blue button)
|
|
|
|
typedef Gpio<GPIOA_BASE, 0> button;
|
|
|
|
|
|
|
|
void blink_led(void *arg) {
|
|
|
|
while(1) {
|
|
|
|
led1::high();
|
|
|
|
Thread::sleep(500);
|
|
|
|
led1::low();
|
|
|
|
Thread::sleep(500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void blink_with_button(void *arg) {
|
|
|
|
while(1) {
|
|
|
|
// check the button value and
|
|
|
|
// if it is pressed, turn the led on
|
|
|
|
if(button::value()==1)
|
|
|
|
led2::high();
|
|
|
|
else
|
|
|
|
led2::low();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
// configure the two LEDs to output mode
|
|
|
|
led1::mode(Mode::OUTPUT);
|
|
|
|
led2::mode(Mode::OUTPUT);
|
|
|
|
|
|
|
|
// the button is instead in input,
|
|
|
|
// in order to be able to read its value
|
|
|
|
button::mode(Mode::INPUT);
|
|
|
|
|
|
|
|
// create the two threads :
|
|
|
|
// each thread will execute a specific function
|
|
|
|
// that has to be passed them as a parameter
|
|
|
|
// (blink_led and blink_with_button)
|
|
|
|
Thread *t1 = Thread::create(blink_led, STACK_MIN,
|
|
|
|
MAIN_PRIORITY, nullptr);
|
|
|
|
Thread *t2 = Thread::create(blink_with_button, STACK_MIN,
|
|
|
|
MAIN_PRIORITY, nullptr);
|
|
|
|
|
|
|
|
for(;;);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Threads Synchronization
|
|
|
|
Miosix also provides primitives, in order to synchronize the execution of multiple threads, such as mutexes and condition variables.
|
|
|
|
|
|
|
|
A mutex can be created as a global variable. Note that `Mutex` is a C++ class, and thus it has a constructor, so it initializes itself. The Miosix API provides two mutex classes: `Mutex` and `FastMutex`. `Mutex` has support for priority inheritance, while `FastMutex` is optimized for fastest lock/unlock.
|
|
|
|
A mutex can also be part of a data structure. Note that even if this is a struct, the fact that it contains C++ class means it MUST be allocated with new and not with malloc(), because malloc() does not know about C++ constructors and would leave the mutex not initialized.
|
|
|
|
|
|
|
|
> :warning: **Try to always keep the critical section in which a mutex is locked as small as possible.**
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <miosix.h>
|
|
|
|
|
|
|
|
using namespace miosix;
|
|
|
|
|
|
|
|
FastMutex mutex1;
|
|
|
|
int foo = 50; // Synchronized by mutex1
|
|
|
|
int bar = 50; // Synchronized by mutex1
|
|
|
|
|
|
|
|
struct Data
|
|
|
|
{
|
|
|
|
Mutex mutex2;
|
|
|
|
int test; // Synchronized by mutex2
|
|
|
|
};
|
|
|
|
|
|
|
|
void *thread1(void *arg)
|
|
|
|
{
|
|
|
|
Data *data=reinterpret_cast<Data*>(arg);
|
|
|
|
for(int i=0; i < 1000000; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// A mutex is locked by creating an instance of the Lock
|
|
|
|
// class and passing the mutex to lock to its constructor.
|
|
|
|
// The mutex will stay locked until the lock variable goes
|
|
|
|
// out of scope, thus to limit the lock range you can use
|
|
|
|
// braces { }. Note that if you haven't used C++ before this
|
|
|
|
// is the first time you encounter the need to use a set of
|
|
|
|
// braces without an if, for, while, ...
|
|
|
|
Lock<FastMutex> l(mutex1);
|
|
|
|
foo++;
|
|
|
|
bar--;
|
|
|
|
} // Mutex automatically unlocked when
|
|
|
|
// the Lock gets out of scope
|
|
|
|
|
|
|
|
{
|
|
|
|
Lock<Mutex> l(data->mutex2);
|
|
|
|
data->test++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
Data *data = new Data;
|
|
|
|
data->test = 0;
|
|
|
|
|
|
|
|
Thread *t = Thread::create(blink_led, STACK_MIN, MAIN_PRIORITY,
|
|
|
|
nullptr, Thread::JOINABLE);
|
|
|
|
|
|
|
|
for(int i=0; i < 1000000; i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
{
|
|
|
|
Lock<FastMutex> l(mutex1);
|
|
|
|
foo++;
|
|
|
|
bar--;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Lock<Mutex> l(data->mutex2);
|
|
|
|
data->test++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t->join();
|
|
|
|
|
|
|
|
printf("foo=%d bar=%d test=%d\n", foo, bar, data->test);
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
If you want to know more about threads and synchronization primitives check out the [Miosix Wiki](https://miosix.org/wiki/index.php?title=Main_Page).
|
|
|
|
|
|
|
|
### References
|
|
|
|
- [Miosix Wiki - Threads example](https://miosix.org/wiki/index.php?title=Example:_Thread)
|
|
|
|
- [Miosix Wiki - Synchronization primitives](https://miosix.org/wiki/index.php?title=Synchronization_primitives) |
|
|
|
\ No newline at end of file |