I am trying to turn on and off a led using FreeRTOS on STM32 F401RE MCU in IAR Workbench IDE.
The led belongs the STM32 nucleo board. There are two task one turn on the Led, the other task turn off the same Led.
Here is the code:
The main Code:
SemaphoreHandle_t xMutex;
int main()
{
if ( xMutex == NULL )
{
xMutex = xSemaphoreCreateMutex();
if ( ( xMutex ) != NULL )
xSemaphoreGive( ( xMutex ) );
}
xTaskCreate(LedOn, "Led On", 100, NULL, 1, NULL);
xTaskCreate(LedOff, "Led Off", 100, NULL, 1, NULL);
vTaskStartScheduler();
while(1){}
}
The tasks:
void LedOn(void *argument)
{
for(;;)
{
xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
vTaskDelay(5000);
xSemaphoreGive(xMutex);
}
}
void LedOff(void *argument)
{
for(;;)
{
xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
vTaskDelay(5000);
xSemaphoreGive(xMutex);
}
}
My intension here is:
Led on task is responsible to turn on Led for 5s
Led off task is responsible to turn of Led for 5s
So this will continue till power off
My problem here is:
In the initial case, the Led stays 5s on after that led stays 5s off it works for only two context switching after two switching the led remains on.
When i debug after two switch the break point doesnt hit the tasks
After a little bit try I guess I found the answer:
Each task should have its delay time so we need to add a delay time in order to task proceed its operation, but i thougt i was add the delay time between xTakeSemaphore and xGiveSemaphore methods, are mutexes delay time which states how the resource should be locked not task delay time.
The Solution is:
void LedOn(void *argument)
{
for(;;)
{
if(xSemaphoreTake(xMutex, portMAX_DELAY)== pdTRUE)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
vTaskDelay(pdMS_TO_TICKS(5000));
xSemaphoreGive(xMutex);
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
}
void LedOff(void *argument)
{
for(;;)
{
if( xSemaphoreTake( xMutex, portMAX_DELAY)== pdTRUE)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
vTaskDelay(pdMS_TO_TICKS(5000));
xSemaphoreGive(xMutex);
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
}
Consider that "task A" is LedOn and "task B" is LedOff. Or, the reverse as it doesn't matter for the problem.
Assume task B has acquired the mutex.
Your problem is that task A's xSemaphoreTake is [probably] timing out, without acquiring the mutex.
You should check the return code.
The reason is that it has a timeout value of 5000 ticks. But, task B does a vTaskDelay(5000). And, while it is doing this, it has the mutex locked
So, most probably, task A's xSemaphoreTake will timeout before task B releases the mutex.
And, then, you flip the LED value, and do a delay. But, then you're doing an xSemaphoreGive on a mutex that task A does not have locked.
In other words, you have a race condition.
Either set an infinite timeout in the take calls or at least set a larger value than the value you give to the delay function.
Try a take value of (e.g.) 10000
just wait forever for the mutex
xSemaphoreTake( xMutex, portMAX_DELAY);
in both tasks.
The usual premise of a priority scheduled RTOS is that the highest priority task/thread that is ready to run pre-empts the one that is running with a lower priority.
Both LedOff, and LedOn tasks are created with the same priority, and thus a context switch doesn't occur immediately when the semaphore is signalled.
A context switch potentially happens when it goes round the loop and attempts to take the semaphore again. Two tasks are now contending on it.
Who wins is essentially an implementation detail of FreeRTOS - and particularly whether semaphore-take operations on the semaphore happen in strict FIFO order or not - ISTR that VxWorks (which FreeRTOS seems heavily modelled on) would optionally do this.
The alternative approach which is seen with POSIX-threads (which FreeRTOS also supports) is to wake up the waiting thread, and the semaphore to be taken by the one gets scheduled first - which is certain to the be one already running.
As a more general point, you have hugely over-complicated a finite state machine with two states. A highly reliable way to achieve it would be:
void LedFlasher(void *argument)
{
for(;;)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
vTaskDelay(5000);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
vTaskDelay(5000);
}
}
A bit late .. ;) but I think there is a bug in your program.
The initial release of the mutex (xSemaphoreGive in main) breaks it.
A mutex is created ready to be taken i.e. in released state.
Related
i have to write a function that, by calling it only a single time, have to:
turn on an output pin
the pin stays high for 200mS
at the end of the timer the pin need to be low again.
the pin stays low for 200mS
at the end of the timer the function can be called again.
to turn on and off an output pin I already have wrote and tested the funcions:
outOn(pin_id);
outOff(pin_id);
now, i am trying to write the function that does the above mentioned actions and this is what l've come out with so far:
void outOnT02(enum e_outs ou){
outOn(ou);
gu_RegTim.BTime[BTIM_FUNCT].Timer = O_SEC01*2;
if(gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer) {
outOff(ou);
}
}
the function is named outOnT02 because:
it is an output;
after calling it, the pin became high;
T02 because the pin stays high for 0.2 Seconds.
outOn(ou); makes the pin go high,
outOff(ou); makes the pin go low,
gu_RegTim.BTime[BTIM_FUNCT].Timer = O_SEC01*2;
starts a 200mS timer,
and gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer is true when the timer has run out.
it works but, as you can tell, I have to put it in a cycle otherwise gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer will never be true and so,the pin will stay high forever.
this is where i am stuck. i can't use a SLEEP(200); because i can't interrupt the execution of the code.
the language is C, the ide is MPLAB X IDE v6.00, the compiler is XC8 v2.31 and the cpu is a PIC16F15355.
This post is a little old but it is worth to answer since it is both a good question and a common problem. Now this problem is very common in embedded world when we develop applications that has to run on only one CPU. Hence there is no real parallelism in the workflow. Also since the application will not run on top of any OS, there will be no scheduler, no timers, no threads etc. Especially in small scaled microcontrollers there is no way to run many of the true RTOSs.
But this shouldn't be an obstacle for developing applications that runs tasks concurrently. We can develop an application using some tricks so that it runs the tasks concurrently and behave as a small OS. Running concurrently means that no task blocks the CPU using busy waiting checks or something alike but we block a task that needs to wait some event to occur.
When we block a task, the specific data and the next execution point on that task must be preserved so that it can continue from where it should in the next execution. Knowing what we need to preserve helps us to create a thread-like structures that executes until it has to wait some event to occur (eg. time delay). When it has to wait (means that it will be blocked) the next state of it must be preserved and it exits to give the control to the CPU so that it executes other tasks.
When we need to deal with periodic tasks as in the question, it is relatively easier to implement without blocking the CPU execution and meanwhile handle other tasks. Moreover no interrupt usage needed for this type of tasks unless the tasks are extremely time sensitive.
Well, enough with the story part, let's get into it. I will base the examples on the OP's output flashing problem. However the same techniques can be applied for other situations like I/O events, hardware events etc.
Let's sum up the requirement briefly, we have a task that runs atomically. That is, when it is called it must run to completion so that it can be called again (this is what I understand from the OP's requirement):
Turns on an output pin for 200ms
Then turns off the pin for 200ms
Once turned off and 200ms time has elapsed it can be executed again.
Note Some functions in this example are not implemented since they can be application or microcontroller specific.
Task-like Functions
Let's assume we want to schedule the following two task-like functions each of which keeps track of its execution continuation points.
The static cp variables are declared in each function so that they remember where to continue whenever they are called. The content of cp variable will not be destroyed by the compiler when the function returns since we declare it as static. The cp needs to be updated upon the expected events occur in order to proceed to the next step whenever it is called.
Note that in outputTask, the call source must be known to control its atomic behaviour. Since the requirement for this task is that once it triggered or called, it must run to completion. So we have to know where the task is called from, in order it to decide what to do on each call. If it has been triggered from another task, it can't be triggered anymore until it completes its flashing prosess. If it is called from the scheduler (main loop) it knows it is a periodic call and will keep track of the time. This control is achieved using a parameter called periodic. When it is called from the scheduler this parameter must be set to 1, and 0 for the calls other than the scheduler.
/*
* This task-like function performs what the OP wants to achieve
*/
void outputTask(unsigned char periodic) {
static unsigned char cp = 0; // Continuation Point holder
static unsigned char currentMillis;
/*
* Check whether it is a periodic call or a new output signal call.
* If it is a periodic call and signalling has been initialized,
* proceed for time keeping.
* If it is a new signalling call and the task hasn't completed yet,
* simply ignore and return.
*/
if(!periodic && cp != 0) {
return;
}
switch(cp) {
case 0:
outOn(pin_id); // Turn on the output
cp = 1; // Next execution point
currentMillis = 200; // Load the 200ms counter for time keeping
break;
case 1:
currentMillis--;
if(currentMillis == 0) {
// 200ms time for output high has elapsed, proceed to next step
outOff(pin_id); // Turn off the output
currentMillis = 200; // Reload the counter value
cp = 2; // Proceed to the next step
}
break;
case 2:
currentMillis--;
if(currentMillis == 0) {
// 200ms time for output low has elapsed, proceed to next step
cp = 0; // Last step is done, reset the state for new calls
}
break;
default:
// For anything else, reset the task state to the initials
cp = 0 // Reset the task state to zero so that it accepts new calls
}
}
/*
* Let's say this task will wait for a button press event and will
* trigger the outputTask upon the event occurs
*/
void outputTriggerTask() {
static unsigned char cp = 0;
static unsigned char currentMillis;
switch(cp) {
case 0:
if(isButtonPressed()) { // Platform specific function
// A button press has been detected, debounce first
currentMillis = 50;
cp = 1; // Next step, check for the elapsed time
}
else {
break;
}
case 1:
currentMillis--;
if(currentMillis == 0) {
// Check whether the button press is consistent
if(isButtonPressed()) {
// Yes still consistent, handle the button press by triggering the output task
outputTask(0); // Not a periodic call
cp = 2; // Next step is to check whether button is released
}
else {
cp = 0; // Reset the task state
}
}
break;
case 2:
if(isButtonReleased()) { // Platform specific function
currentMillis = 50; // Reload the time counter
cp = 3;
}
else {
break;
}
case 3:
currentMillis--;
if(currentMillis == 0) {
// Check whether the button release is consistent
if(isButtonReleased()) {
// Yes still consistent, handle the button release if needed
cp = 0; // Reset the task to its initial state
}
}
break;
default:
cp = 0; // Reset to initials
}
}
Scheduling Approches
The following approches are for non RTOS small embedded systems. They are suitable for wide range of 8-bit microcontrollers.
Approach 1 - Create Delay Based Timebase to Schedule Tasks
Scheduling using CPU blocking delay is suitable for hobby and educational purposes while it is not suitable for real projects. This example uses a platform specific delay_ms function (or can be a macro) to create a 1ms heartbeat for the application so that the tasks can keep track of time.
void main(void) {
systemInit(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// The first thing we do is to create a 1ms timebase using delay.
// This is the heartbeat for the application
delay_ms(1000); // Platform specific function
// 1ms has elapsed check the tasks
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
Approach 2 - Create Hardware Timer Based Timebase
void main(void) {
systemInit(); // Platform specific function
// Setup a hardware timer for 1ms overflow without interrupt
initTimerForOneMs(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Wait for the timer to overflow
while(!isTimerOverflow()) // Platform specific function
;
// Timer has overflowed, reload and check tasks
reloadTimer(); // Platform specific function
// 1ms has elapsed check the tasks
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
Approach 3 Put the Processor to Sleep for 1ms Timebase
void main(void) {
systemInit(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Put the Processor to sleep along with a watchdog timer to wake it up
clearWatchdogTimer(); // Platform specific function
sleep(); // Platform specific function
// CPU slept for 1ms and woke up, handle the periodic tasks
outputTriggerTask(); // Check whether any button press event has occured
clearWatchdogTimer(); // Platform specific function
outputTask(1); // It is a periodic call for the output task
clearWatchdogTimer(); // Platform specific function
// Maybe more tasks go here...
}
}
And Last But not Least Time Checking Approach
In this approach the tasks will be keeping the time by checking better say comparing the elapsed time to the desired time to delay tasks without blocking the CPU. For this, we will need to use a free running timer. This will be like the millis function of the Arduino API.
Rewriting the Tasks for the Time Checking Approach
/*
* This task-like function performs what the OP wants to achieve
*/
void outputTask(unsigned char periodic) {
static unsigned char cp = 0; // Continuation Point holder
static unsigned short currentMillis; // 16 bit millisecond holder
/*
* Check whether it is a periodic call or a new output signal call.
* If it is a periodic call and signalling has been initialized,
* proceed for time keeping.
* If it is a new signalling call and the task hasn't completed yet,
* simply ignore and return.
*/
if(!periodic && cp != 0) {
return;
}
switch(cp) {
case 0:
outOn(pin_id); // Turn on the output
cp = 1; // Next execution point
currentMillis = getCurrentMillis(); // Platform specific function
break;
case 1:
if(getCurrentMillis() - currentMillis >= 200) {
// 200ms time for output high has elapsed, proceed to next step
outOff(pin_id); // Turn off the output
currentMillis = getCurrentMillis(); // Reload the counter value
cp = 2; // Proceed to the next step
}
break;
case 2:
if(getCurrentMillis() - currentMillis >= 200) {
// 200ms time for output low has elapsed, proceed to next step
cp = 0; // Last step is done, reset the state for new calls
}
break;
default:
// For anything else, reset the task state to the initials
cp = 0 // Reset the task state to zero so that it accepts new calls
}
}
/*
* Let's say this task will wait for a button press event and will
* trigger the outputTask upon the event occurs
*/
void outputTriggerTask() {
static unsigned char cp = 0;
static unsigned short currentMillis;
switch(cp) {
case 0:
if(isButtonPressed()) { // Platform specific function
// A button press has been detected, debounce first
currentMillis = getCurrentMillis(); // Platform specific function
cp = 1; // Next step, check for the elapsed time
}
else {
break;
}
case 1:
if(getCurrentMillis() - currentMillis >= 50) {
// Check whether the button press is consistent
if(isButtonPressed()) {
// Yes still consistent, handle the button press by triggering the output task
outputTask(0); // Not a periodic call
cp = 2; // Next step is to check whether button is released
}
else {
cp = 0; // Reset the task state
}
}
break;
case 2:
if(isButtonReleased()) { // Platform specific function
currentMillis = getCurrentMillis();
cp = 3;
}
else {
break;
}
case 3:
if(getCurrentMillis() - currentMillis >= 50) {
// Check whether the button release is consistent
if(isButtonReleased()) {
// Yes still consistent, handle the button release if needed
cp = 0; // Reset the task to its initial state
}
}
break;
default:
cp = 0; // Reset to initials
}
}
Scheduler for Time Checking Approach
void main(void) {
systemInit(); // Platform specific function
initMillisTimerWithInterrupt(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Now that we use a free running millis timer no need to block the CPU to create a timebase
// Just call tasks sequentially. Each task will know what to do individually
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
I am working on a 2-axis CNC machine using an STM32F103C8T6 and using the HAL libraries. I've setup a seperate timer for each axis of the machine and have those configured in PWM mode.
I have a function called step_x(numberSteps, Direction) which takes in two parameters which are the number of steps to make and the direction to move in. The function sets the number of steps as the target number of steps in a global variable, sets the direction via GPIO write and then starts the PWM in interrupt mode using the HAL library:
void step_x(uint32_t numberSteps, uint16_t direction){
RELEASE_X=0;
steps_x_target = numberSteps;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, direction_x);
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1);
}
This then starts the PWM and I am counting the number of pulses via the timer update callback function:
void TIM1_UP_IRQHandler(void)
{
if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE))
{
if (__HAL_TIM_GET_IT_SOURCE(&htim1, TIM_IT_UPDATE))
{
step_update('X');
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
__HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_CC1 );
}
}
}
Inside this callback, step_update() is called taking as a parameter which axis I am working with.
The step_update function adds +1 to a count for the number of steps that have occured and stops the PWM when the number of steps is equal to the target number of steps that was set. It also calls a function which keeps track of the position in mm rather than steps. (Y and Z axis cases removed for the sake of brevity)
void step_update(char axis){
switch(axis){
case 'X':
steps_x++;
updatePosition(axis);
if(steps_x==(2*steps_x_target)){ //check if 2* because the update event happens twice every pulse
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
steps_x_target=0;
steps_x=0;
RELEASE_X=1;
if(limitSwitchX_Trigger==1){
positionX=0;
limitSwitchX_Trigger=0;
NVIC_EnableIRQ(EXTI0_IRQn);
}
}
break;
case 'Y':
break;
case 'Z':
break;
}
}
I'm sure this is not an entirely efficient way to do this however it works well enough except for the fact that I have no way to call the step_x() function and wait for it to finish before making the call to the next one. I added the volatile variables RELEASE_X which is set to 0 when step_x() is called and set to 1 when the step_update() function has stopped the PWM when the target steps are reached. I thought using this that I may be able to do something like this which I would expect to step 800 pulses in one direction and then step 800 pulses in another direction:
step_x(800,0);
while(RELEASE_X!=1);
step_x(800,1);
However what happens is that the while loop ends up blocking the Timer1_Update callback from occuring and the pulses don't get counted. I expected that because the pulse counting is done in an ISR callback that the MCU would just jump to the ISR from this while loop and update the steps until RELEASE_X is set to true and then advance to the next call of step_x()? Why is this not so?
Could someone suggest a way that I can write code which will allow me to call a function which steps a certain amount of steps while waiting for them to finish before moving on to the next call? I am trying to implement Bresenhams line algorithm next and so I need to step a certain amount of steps and then only return from the call and advance to the next line of code when the steps are complete. (Essentially, how can I make this function blocking but without toggling GPIO pins/bit bashing)
https://github.com/Blargian/EPR400
It's not really an answer as to why this happened but I solved the problem by disabling the timer update and global interrupts in CubeMX under the NVIC tab. I removed the callback functions from my main.h and main.c and then I modified the step_x function to be:
void step_x(uint32_t numberSteps, uint16_t direction){
RELEASE_X=0;
steps_x_target = numberSteps;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, direction_x);
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1);
while(RELEASE_X!=1){
if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE))
{
if (__HAL_TIM_GET_IT_SOURCE(&htim1, TIM_IT_UPDATE))
{
step_update('X');
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
__HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_CC1 );
}
}
}
}
Basically just moved the code that was in the callback function to within the while loop in the step_x function. The function is now a blocking function which is atually what I need anyway.
I am writing a kernel module in which I need to trigger a function on a periodic basis. The function will access a queue and process its elements. The number of elements in the queue is dynamic and so the processing time.
In the following code, I have added 1ms sleep to represent the processing time. I am getting this error : [116588.117966] BUG: scheduling while atomic: systemd-journal/408/0x00010000. If my understanding is correct, this happens since I try to sleep 1ms when the expiry time of the hr_timer is just 1us. I can increase this expiry time but the processing time of the queue can be sometimes more than seconds, sometimes in hours even. Please help me to achieve this.
unsigned long timer_interval_ns = 1e3;
static struct hrtimer hr_timer;
enum hrtimer_restart timer_callback( struct hrtimer *timer_for_restart )
{
uint64_t rawtime;
struct timespec curtime;
ktime_t currtime , interval;
/ * My Operations would take ~ 1ms, so adding 1ms for simplicity* /
msleep(1);
currtime = ktime_get();
interval = ktime_set(0,timer_interval_ns);
hrtimer_forward(timer_for_restart, currtime, interval);
return HRTIMER_RESTART;
}
static int __init timer_init(void) {
ktime_t ktime = ktime_set( 0, timer_interval_ns );
hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
hr_timer.function = &timer_callback;
hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );
return 0;
}
BUG: scheduling while atomic
This message means that while you are in an atomic context you tried to schedule some other task.
To make it easy (so, not perfect and orthodox explanation): if a function is running in an atomic context, this function cannot stop its execution and call the scheduler (a.k.a. sleeping).
When you call msleep(1) you are actually asking the kernel to schedule some other task because for 1 millisecond you do not have anything to do and you ask the kernel to use this time to do something useful. But this is not allowed in an atomic context. Functions running in an atomic context must finish their execution without any interruption.
Another example of function that will sleep and you may have the temptation to use is kmalloc. If you need it in an atomic context then use the GFP_ATOMIC flag in order to have an atomic allocation that does not sleep (schedule).
Another example of atomic context is the interrupt handler function.
Another problem that you may have with msleep(1) is that is not guaranteed that it will sleep 1 millisecond. It it too short to be guaranteed. Indeed, it is suggested to use a sleeping time greater or equal than 20 milliseconds. If you need a shorter sleeping time, then use delay functions.
Read The following links:
https://en.wikipedia.org/wiki/Linearizability
https://lwn.net/Articles/274695/
https://www.kernel.org/doc/Documentation/timers/timers-howto.txt
I am using KEIL RTX RTOS which used pre-emptive round robin scheduler.I have a LCD for displaying data and a few tasks have access to this LCD(there are some other tasks also),These tasks need fixed time for handling the LCD(e.g. first task handle LCD for displaying it's data for 50 seconds and after 50 seconds,second task handle and displays it's data for 10 seconds). I know i must use mutex for managing accessing to LCD.but i dont know how i must manage its for fixed times?LCD tasks are in the lowest priority and if there are not any other task to execution these tasks will be execute for displaying messages.
I'll try to answer your question first but then I'm going to propose an alternate design for you to consider.
You should use a Timer in order to base anything in real-world time, especially a period that is relatively long, like these which are measured in seconds. Create two Timer objects, one with a 50 second period and one with a 10 second period. Both timers should be one-shot type. Also create two Signal objects, one to indicate that the 50 second period has expired and another to indicate that the 10 second period has expired. The two Timer objects each call distinct callback functions when they expire. The 50 second callback function should set the 50 second expiration Signal and then start the 10 second Timer. The 10 second callback function should set the 10 second expiration Signal and then restart the 50 second Timer. The timers will ping-pong back and forth, alternately setting the two signals.
Now your resource using tasks should periodically check for the appropriate expiration Signal. When the task observes that the Signal is set it can give up the resource and clear the Signal. The other task does the same thing with the other Signal. This way the two tasks know when to give up the resource and allow the other task to obtain it.
One thing that bothers me about your design is that you have two synchronization mechanisms protecting your resource. A mutex is a synchronization mechanism. When tasks are tying to use a resource asynchronously (i.e., at random times), a mutex can be used to synchronize those usages and ensure only one task is using the resource at any given time. If you already have another synchronization mechanism then you probably don't need the mutex. In other words, If your tasks have distinct time slots in which they use the resource then they're already synchronous. If they're not going to attempt to use the resource at random times then you may not need a mutex.
Your design also seems complicated and I wonder whether this alternate design might be simpler.
Consider making a single task that is responsible for the LCD display interface. This LDC task is the only task that will interface with the display. Your data tasks will send messages to the LCD task when they produce data to be displayed. The LCD task will simply wait for these messages and display the data appropriately when they arrive. You could use either a Message Queue or a Mail Queue for this message service depending upon how complicated and varied the data is. Now you don't need a mutex for the LCD display because there is only one task that uses it. And do you still need the 50/10 second time split with this design? I'm not sure because I don't know what was the source of that requirement.
Rather then have multiple threads accessing a single resource arbitrated by mutexes, it would be simpler to have a single thread handle the resource.
In this case I suggest a display manager thread, the other threads can register with the display manger providing perhaps a pointer to a display buffer, and the required display period. The display manager then simply cycles through each registered thread displaying its buffer for the required period before switching to the next.
For example (pseudo-code):
static struct
{
const char* frame_buffer ;
int display_seconds ;
OS_MUTEX mutex ;
} display_registry[MAX_DISPLAY_THREADS] = {0,0} ;
void displayLock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_lock( display_registry[handle].mutex ) ;
}
}
void displayUnock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_unlock( display_registry[handle].mutex ) ;
}
}
void lcd_thread
{
int display = 0 ;
for(;;)
{
int t = 0 ;
while( t < display_registry[display].display_seconds &&
display_registry[display].frame_buffer != 0 )
{
displayLock( display ) ;
render_display( display_registry[display].frame_buffer ) ;
displayUnlock( display ) ;
delay( ONE_SECOND ) ;
}
display = (display + 1) % MAX_DISPLAY_THREADS ;
}
}
int displayRegister( const char* frame_buffer, int display_seconds )
{
for( int i = MAX_DISPLAY_THREADS - 1;
frame_buffer[i] != 0 &&
i >= 0; i-- )
{
// do nothing
}
if( i >= 0 )
{
display_registry[i].display_seconds = display_seconds ;
display_registry[i].frame_buffer = frame_buffer ;
}
return i ; // handle for de-registering/locking
}
void displayDeregister( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
display_registry[handle].frame_buffer = 0 ;
}
}
Note that the mutexes are not to lock the LCD resource, but to lock the shared memory frame buffer resources.
Other threads then simply place data for display in their own frame buffers, entirely asynchronously to the display of that data as follows for example:
displayLock( my_display_handle ) ;
update_display_buffer() ;
displayUnlock( my_display_handle ) ;
As mentioned in previous answers, first make a single task for LCD display then use timer events to track the time slice.
Finally, Yield the task in the timer event handler (called after the time slice).
If you don't know about yield, yield is a way for a task to give up its execution to enable scheduler to move to next task.
I have a handler for SysTick exception which counts ticks and calls other functions (f1, f2, f3) whose execution time can be longer than SysTick period. These functions set and clear their active status (global variables) so if a SysTick exception occurs it can detect an overload and return to interrupted function.
I have assigned fixed priority to SysTick exception (let's say 16). I want to somehow make possible for SysTick to generate an exception regardless of it's prior active status, go to SysTickHandler, increase tick counter and return to interrupted function.
One solution which may be useful is to use BASEPRI. It can be set to priority lower than SysTick so it would enable that exception. Unfortunately, using BASEPRI got me nowhere because nothing happened (I set it to max value). BASEPRI value was 0 inside SysTickHandler before I changed it. Should that value be equal to SysTick priority when processor enters handler function? Is exception priority loaded automatically in BASEPRI?
I have also considered for NVIC to have an issue with preempting already active exception but found nothing regarding that in ARM documentation.
Also, return from handler when oveload is detected could set the processor state to thread mode. Let's ignore that for now.
void SysTickHandler(void) {
ticks++;
//set_BASEPRI(max_value);
if (f1_act || f2_act || f3_act) return;
else {
f1();
f2();
f3();
}
}
A simpler example for this problem (without return) would be to increase tick counter when having an infinite loop inside handler.
void SysTickHandler(void) {
ticks++;
set_BASEPRI(max_value);
while(1);
}
If the interrupt becomes pending while its handler is already running, the handler will run to completion and immediately re-enter. Your tick will be aperiodic, and if the functions consistently take longer that one tick period, you may never leave the interrupt context.
It may be possible I suppose to increase the priority of the interrupt in the handler so that it will preempt itself, but even if that were to work, I would hesitate to recommend it.
It sounds that what you actually need is an RTOS.
Sorry to disappoint you, but it seems a overall design problem to me...
Why won't you just set some flag in SysTick and read it somewhere else?
Like:
#include <stdbool.h>
volatile bool flag = false;
//Consider any form of atomicity here
//atomic_bool or LDREX/STREX instructions here. Bitbanding will also work
void sysTickHandler(void) {
ticks++;
if (f1_act || f2_act || f3_act) return;
else {
flag = true; //or increment some counter if you want to keep track of the amount of executions
}
And somewhere else:
int main() {
// some init code
//main loop
for(;;) {
foo();//do sth
bar(x); //do sth else
if (flag) {
f1();
f2();
f3();
flag = false;
}
}
}
Or if we assume that every interrupt wakes the microcontroller and power-down mode is needed, then sth. like this might work:
if (flag) {
f1();
f2();
f3();
flag = false;
}
goToSleep(powerDownModeX); //whatever;