No ACL setup yet! Denying access to everyone.
en:edu:zmitac:s2:aih:lab:nrf52threads2
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:edu:zmitac:s2:aih:lab:nrf52threads2 [2023/11/23 18:21] – ktokarz | en:edu:zmitac:s2:aih:lab:nrf52threads2 [2023/12/03 13:25] (current) – ktokarz | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Zephyr threads ====== | + | ====== Zephyr threads |
| In this part, we will show methods of exchanging data between threads. In the example from the previous part, threads were defined using K_THREAD_DEFINE()macro. | In this part, we will show methods of exchanging data between threads. In the example from the previous part, threads were defined using K_THREAD_DEFINE()macro. | ||
| < | < | ||
| Line 13: | Line 13: | ||
| * thread options (here 0) | * thread options (here 0) | ||
| * delay to start the thread (0 meaning no delay) | * delay to start the thread (0 meaning no delay) | ||
| - | As we can see every thread has its own stack, which means that if there is a need to exchange data between threads we have to use some shared variable visible for all threads interested in using it. In multitasking environments, | + | As we can see every thread has its own stack, which means that if there is a need to exchange data between threads we have to use some shared variable visible for all threads interested in using it. In multitasking environments, |
| + | * FIFO queue | ||
| + | * Semaphores | ||
| + | * Mutexes | ||
| + | ===== FIFO queue ===== | ||
| + | In the example available in the Zephyr framework the FIFO queue is used to pass data from two threads (blink0, blink1) which are used to blink two LEDs to the uart_out thread which displays received data in the terminal. | ||
| + | |||
| + | The FIFO queue can be defined with the macro K_FIFO_DEFINE. In our example, it defines the FIFO queue and gives it the name " | ||
| + | < | ||
| + | K_FIFO_DEFINE(printk_fifo); | ||
| + | </ | ||
| + | |||
| + | The Blink0 thread as well as Blink1 calls the same function blink() passing three parameters: | ||
| + | - address of the structure that represents LED (&led0 or &led1 respectively) | ||
| + | - number of milliseconds the function goes to sleep between iterations | ||
| + | - id of LED (0 or 1) | ||
| + | Notice that blink() is called only once from thread Blink0 and once from thread Blink1. After the initialisation of LED, it jumps into infinite while(1) loop. Every iteration toggles the LED using the least significant bit of the cnt variable | ||
| + | < | ||
| + | gpio_pin_set(spec-> | ||
| + | </ | ||
| + | and inserts the content of the object tx_data into the printk_fifo using the dynamically allocated memory region of the size equal to printk_data_t size. | ||
| + | < | ||
| + | k_fifo_put(& | ||
| + | </ | ||
| + | The uart_out thread gets the element from the thread with the function k_fifo_get | ||
| + | < | ||
| + | struct printk_data_t *rx_data = k_fifo_get(& | ||
| + | </ | ||
| + | After successful reading, prints the LED number and counter value (cnt) to the console, and releases the memory allocated by blink. | ||
| + | |||
| + | The full code looks as follows. If you face problems with compilation add the following line to the prj.conf file | ||
| + | < | ||
| + | CONFIG_HEAP_MEM_POOL_SIZE=1024 | ||
| + | </ | ||
| < | < | ||
| /* | /* | ||
| Line 133: | Line 166: | ||
| PRIORITY, 0, 0); | PRIORITY, 0, 0); | ||
| </ | </ | ||
| + | |||
| + | Note that there is no main() function in this code, everything happens in thread functions. | ||
| + | ===== Semaphores ===== | ||
| + | Semaphore is the variable that presents the state of the shared resource. This variable must be available for all the threads interested in using the resource. The common way to implement the semaphore is the global variable. Still, Zephyr OS has the semaphore mechanism built in which has some additional features like controlling the limit value of the semaphore, preventing blocking the ISR handlers. | ||
| + | At initialization, | ||
| + | Function “Give” will increment the semaphore value up to the maximum limit, in which case the semaphore will not be modified. “Give” can be called from any thread or ISR because it does not stop program execution in any case. | ||
| + | Function “Take” will decrement the semaphore count down to zero (the semaphore is unavailable). “Take” can be called only from threads but not from ISR, because if a thread is trying to " | ||
| + | To create the semaphore with the name " | ||
| + | < | ||
| + | K_SEM_DEFINE(my_semaphore, | ||
| + | </ | ||
| + | Or create the k_sem structure and call the initialization function. | ||
| + | < | ||
| + | k_sem_init(& | ||
| + | </ | ||
| + | Giving semaphore can be done with the function: | ||
| + | < | ||
| + | k_sem_give(& | ||
| + | </ | ||
| + | and taking the semaphore with the code as follows: | ||
| + | < | ||
| + | if (k_sem_take(& | ||
| + | | ||
| + | } else { | ||
| + | /* semaphore availabe, execute the code here */ | ||
| + | } | ||
| + | </ | ||
| + | As you can see the k_sem_take() function has a timeout that can prevent the thread from hanging. This argument can also be K_NO_WAIT (returns immediately) and K_FOREVER (blocks the thread).\\ | ||
| + | ===== Mutexes ===== | ||
| + | Mutexes are similar to the semaphores but present two values only: locked and unlocked. Despite this, they are implemented also as the counter in which zero value means that the resource is unlocked, and any other value means that the resource is locked. If the mutex is locked three times it also must be unlocked three times to make it unlocked. Another feature that differs between mutexes and semaphores is that the mutex can be unlocked only by the same thread which locks it. Mutexes are mainly used to synchronize the critical shared code that can be executed from many threads. | ||
| + | To create the mutex you can use the macro | ||
| + | < | ||
| + | K_MUTEX_DEFINE(my_mutex); | ||
| + | </ | ||
| + | or the k_mutex structure definition and initialization as for the semaphores: | ||
| + | < | ||
| + | struct k_mutex my_mutex; | ||
| + | k_mutex_init(& | ||
| + | </ | ||
| + | Locking the mutex is done with the function: | ||
| + | < | ||
| + | if (k_mutex_lock(& | ||
| + | /* mutex successfully locked */ | ||
| + | } else { | ||
| + | printk(" | ||
| + | } | ||
| + | </ | ||
| + | Here the timeout can be also K_NO_WAIT and K_FOREVER.\\ | ||
| + | Unlocking the mutex can be done with the function: | ||
| + | < | ||
| + | k_mutex_unlock(& | ||
| + | </ | ||
| + | ===== Tasks for the laboratory ===== | ||
| + | - Write the program that blinks the LED as many times as it is set in the semaphore (decrementing semaphore with every blink). The semaphore can be given (incremented) by pressing the button. Thus if you press the button three times the LED will blink three times. | ||
en/edu/zmitac/s2/aih/lab/nrf52threads2.1700760080.txt.gz · Last modified: by ktokarz
