在 FreeRTOS 中使用 Mutex 实现两个任务同步
Two Task Synchronisation With Mutex In FreeRTOS
我正在尝试在 IAR 中的 STM32 F401RE MCU 上使用 FreeRTOS 打开和关闭 LED Workbench IDE.
led属于STM32核板。有两个任务,一个打开 Led,另一个任务关闭同一个 LED。
代码如下:
主要代码:
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){}
}
任务:
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);
}
}
我这里的意图是:
Led on任务负责开启Led 5s
led off task负责关闭Led 5s
所以这将一直持续到断电
我的问题是:
在初始情况下,LED 保持 5s 亮起,然后 LED 保持 5s 关闭它仅适用于两次上下文切换,两次切换后 LED 保持亮起。
当我在两次切换后调试时,断点没有命中任务
经过一番尝试,我想我找到了答案:
每个任务都应该有它的延迟时间,所以我们需要添加一个延迟时间以便任务继续其操作,但我想我是在 xTakeSemaphore
和 xGiveSemaphore
方法之间添加了延迟时间, 是互斥锁延迟时间,说明资源应该如何被锁定而不是任务延迟时间。
解决方案是:
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));
}
}
}
考虑 "task A" 是 LedOn
,"task B" 是 LedOff
。或者,相反,因为这对问题无关紧要。
假设任务 B 已获取互斥量。
你的问题是任务 A 的 xSemaphoreTake
[可能] 超时,没有获取互斥量。
您应该检查 return 代码。
原因是它的超时值为 5000 ticks。但是,任务 B 做了一个 vTaskDelay(5000)
。而且,在执行此操作时,它具有互斥锁 locked
因此,任务 A 的 xSemaphoreTake
很可能会在 任务 B 释放互斥量之前 超时。
然后,翻转 LED 值,并进行延迟。但是,然后你正在对任务 A 未 锁定的互斥锁执行 xSemaphoreGive
。
换句话说,你有一个竞争条件。
要么在调用中设置一个无限超时,要么至少设置一个比你给延迟函数的值更大的值。
尝试取值(例如)10000
优先级调度 RTOS 的通常前提是准备好 运行 的最高优先级 task/thread 抢占 运行 较低优先级的优先级。
LedOff
和 LedOn
任务都是使用 相同的优先级 创建的,因此当信号量是发出信号。
当它绕过循环并尝试再次获取信号量时,可能会发生上下文切换。现在有两个任务在争夺它。
谁获胜本质上是 FreeRTOS 的一个实现细节——特别是信号量上的信号量获取操作是否以严格的 FIFO 顺序发生——ISTR,VxWorks(FreeRTOS 似乎在很大程度上模仿了它)可以选择这样做。
POSIX-threads(FreeRTOS 也支持)的另一种方法是唤醒等待的线程,并且首先安排要使用的信号量 - 这肯定会已经 运行ning 了。
作为更一般的观点,您将具有两个状态的有限状态机过于复杂化了。实现它的高度可靠的方法是:
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);
}
}
永远等待互斥锁
xSemaphoreTake( xMutex, portMAX_DELAY);
在两个任务中。
有点晚了.. ;) 但我认为您的程序中存在错误。
互斥体的初始释放(主程序中的 xSemaphoreGive)破坏了它。
已创建一个互斥锁,准备好使用,即处于释放状态。
我正在尝试在 IAR 中的 STM32 F401RE MCU 上使用 FreeRTOS 打开和关闭 LED Workbench IDE.
led属于STM32核板。有两个任务,一个打开 Led,另一个任务关闭同一个 LED。
代码如下:
主要代码:
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){}
}
任务:
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);
}
}
我这里的意图是:
Led on任务负责开启Led 5s
led off task负责关闭Led 5s
所以这将一直持续到断电
我的问题是:
在初始情况下,LED 保持 5s 亮起,然后 LED 保持 5s 关闭它仅适用于两次上下文切换,两次切换后 LED 保持亮起。
当我在两次切换后调试时,断点没有命中任务
经过一番尝试,我想我找到了答案:
每个任务都应该有它的延迟时间,所以我们需要添加一个延迟时间以便任务继续其操作,但我想我是在 xTakeSemaphore
和 xGiveSemaphore
方法之间添加了延迟时间, 是互斥锁延迟时间,说明资源应该如何被锁定而不是任务延迟时间。
解决方案是:
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));
}
}
}
考虑 "task A" 是 LedOn
,"task B" 是 LedOff
。或者,相反,因为这对问题无关紧要。
假设任务 B 已获取互斥量。
你的问题是任务 A 的 xSemaphoreTake
[可能] 超时,没有获取互斥量。
您应该检查 return 代码。
原因是它的超时值为 5000 ticks。但是,任务 B 做了一个 vTaskDelay(5000)
。而且,在执行此操作时,它具有互斥锁 locked
因此,任务 A 的 xSemaphoreTake
很可能会在 任务 B 释放互斥量之前 超时。
然后,翻转 LED 值,并进行延迟。但是,然后你正在对任务 A 未 锁定的互斥锁执行 xSemaphoreGive
。
换句话说,你有一个竞争条件。
要么在调用中设置一个无限超时,要么至少设置一个比你给延迟函数的值更大的值。
尝试取值(例如)10000
优先级调度 RTOS 的通常前提是准备好 运行 的最高优先级 task/thread 抢占 运行 较低优先级的优先级。
LedOff
和 LedOn
任务都是使用 相同的优先级 创建的,因此当信号量是发出信号。
当它绕过循环并尝试再次获取信号量时,可能会发生上下文切换。现在有两个任务在争夺它。
谁获胜本质上是 FreeRTOS 的一个实现细节——特别是信号量上的信号量获取操作是否以严格的 FIFO 顺序发生——ISTR,VxWorks(FreeRTOS 似乎在很大程度上模仿了它)可以选择这样做。
POSIX-threads(FreeRTOS 也支持)的另一种方法是唤醒等待的线程,并且首先安排要使用的信号量 - 这肯定会已经 运行ning 了。
作为更一般的观点,您将具有两个状态的有限状态机过于复杂化了。实现它的高度可靠的方法是:
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);
}
}
永远等待互斥锁
xSemaphoreTake( xMutex, portMAX_DELAY);
在两个任务中。
有点晚了.. ;) 但我认为您的程序中存在错误。 互斥体的初始释放(主程序中的 xSemaphoreGive)破坏了它。 已创建一个互斥锁,准备好使用,即处于释放状态。