一起使用二进制信号量和互斥量
Using Binary Semaphore and Mutex Together
我是 FreeRTOS 的新手,一直在阅读 FreeRTOS 文档并在 STM32F767 Nucleo 板上使用 FreeRTOS 编写简单代码。在我编写的简单程序中,我使用二进制信号量仅在 LPTIM 和 GPIO 中断发生时通过 xSemaphoreGiveFromISR()
向某些任务发出信号,并通过 xSemaphoreGive()
向不同的任务发出信号以执行来自另一个任务的某些操作。
假设我有一个 I2C1 外围设备连接到两个不同的设备:
- 一个加速度计,每当 activity/movement 发生时就会触发微控制器的 GPIO 中断。此 GPIO 中断向微控制器发出信号,表明必须读取其中断事件寄存器中的一段数据,以便可以再次发出下一个 activity/movement 事件信号。
- 必须周期性读取的设备,会通过LPTIM或TIM外设触发
我可以在上述情况下使用互斥量和二进制信号量吗?
二进制信号量将根据触发的相应中断向任务指示需要执行的操作,但互斥锁将在这两个任务之间共享,其中 Task1 将负责从加速度计,Task2 将负责从其他设备读取数据。我在想将使用互斥体,因为这两个操作永远不会同时发生,这样总线上就不会发生重叠的 I2C 事务,从而可能锁定任何一个 I2C 设备。
代码如下所示:
void Task1_AccelerometerOperations(void *argument)
{
/* The Semaphore will be given from the GPIO Interrupt Handler, signalling that a piece of
data needs to be read from the accelerometer through I2C. */
if(xSemaphoreTake(xSemaphore_GPIOInterruptFlag, portMAX_DELAY) == pdTRUE)
{
/* This Mutex should ensure that only one I2C transaction can happen at a time */
if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
{
/* Perform I2C Transaction */
/* Perform operations with the data received */
/* Mutex will be given back, indicating that the shared I2C Bus is now available */
xSemaphoreGiveRecursive(xMutex_I2CBus);
}
else
{
/* Mutex was not available even after 2 seconds since the GPIO interrupt triggered.
Perform Error Handling for the event that the I2C bus was locked */
}
/* Piece of code that could take a few hundreds milliseconds to execute */
}
}
void Task2_OtherEquipmentOperations(void *argument)
{
/* The Semaphore will be given from the LPTIM Interrupt Handler, signalling that some maintenance
or periodic operation needs to be performed through I2C. */
if(xSemaphoreTake(xSemaphore_LPTIMInterruptFlag, portMAX_DELAY) == pdTRUE)
{
/* Only perform the I2C operations when the Mutex is available */
if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
{
/* Perform I2C Transaction */
/* Mutex will be given back, indicating that the shared I2C Bus is now available */
xSemaphoreGiveRecursive(xMutex_I2CBus);
}
else
{
/* Mutex was not available even after 2 seconds since the LPTIM interrupt triggered.
Perform Error Handling for the event that the I2C bus was locked */
}
/* Piece of code that could take a few seconds to execute */
}
}
互斥量是否经常用于避免优先级反转场景,或者它们(更经常)广泛用于防止两个操作可能同时发生?我想不出一个一个简单的场景,如果发生优先级反转,它可能对软件至关重要。
谢谢!
总的来说,我认为你的设计可行。信号量是两个任务完成工作的信号。互斥体保护共享的 I2C 资源。
但是,使用互斥量共享资源可能会导致复杂情况。首先,您的操作任务在等待 I2C 资源互斥锁时不响应新信号量 signals/events。其次,如果应用程序变得更加复杂并且您添加了更多的阻塞调用,那么设计可能会陷入阻塞、饥饿、竞争条件和死锁的恶性循环。您的简单设计还不存在,但您已经开始了。
作为替代方案,考虑让第三个任务负责处理所有 I2C 通信。 I2C 任务将等待消息(在队列或邮箱中)。当消息到达时,I2C 任务将执行相关的 I2C 通信。操作任务将像现在一样等待信号量 signal/event。但是,现在操作任务不会等待 I2C 互斥锁可用,而是 send/post 向 I2C 任务发送消息。使用此设计,您不需要互斥体来序列化对 I2C 资源的访问,因为 I2C 任务的 queue/mailbox 完成了序列化来自其他任务的消息通信请求的工作。同样在这个新设计中,每个任务只在一个地方阻塞,这样更干净并且允许操作任务对 signals/events.
IMO 这是一个完全错误的设计。
如果要通知任务有新数据 - 使用队列和 post 到队列。如果不止一个任务想要获取该数据,您可以 post 到多个队列(您可以发送一组队列)。您甚至可以在运行时添加队列。
我是 FreeRTOS 的新手,一直在阅读 FreeRTOS 文档并在 STM32F767 Nucleo 板上使用 FreeRTOS 编写简单代码。在我编写的简单程序中,我使用二进制信号量仅在 LPTIM 和 GPIO 中断发生时通过 xSemaphoreGiveFromISR()
向某些任务发出信号,并通过 xSemaphoreGive()
向不同的任务发出信号以执行来自另一个任务的某些操作。
假设我有一个 I2C1 外围设备连接到两个不同的设备:
- 一个加速度计,每当 activity/movement 发生时就会触发微控制器的 GPIO 中断。此 GPIO 中断向微控制器发出信号,表明必须读取其中断事件寄存器中的一段数据,以便可以再次发出下一个 activity/movement 事件信号。
- 必须周期性读取的设备,会通过LPTIM或TIM外设触发
我可以在上述情况下使用互斥量和二进制信号量吗?
二进制信号量将根据触发的相应中断向任务指示需要执行的操作,但互斥锁将在这两个任务之间共享,其中 Task1 将负责从加速度计,Task2 将负责从其他设备读取数据。我在想将使用互斥体,因为这两个操作永远不会同时发生,这样总线上就不会发生重叠的 I2C 事务,从而可能锁定任何一个 I2C 设备。
代码如下所示:
void Task1_AccelerometerOperations(void *argument)
{
/* The Semaphore will be given from the GPIO Interrupt Handler, signalling that a piece of
data needs to be read from the accelerometer through I2C. */
if(xSemaphoreTake(xSemaphore_GPIOInterruptFlag, portMAX_DELAY) == pdTRUE)
{
/* This Mutex should ensure that only one I2C transaction can happen at a time */
if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
{
/* Perform I2C Transaction */
/* Perform operations with the data received */
/* Mutex will be given back, indicating that the shared I2C Bus is now available */
xSemaphoreGiveRecursive(xMutex_I2CBus);
}
else
{
/* Mutex was not available even after 2 seconds since the GPIO interrupt triggered.
Perform Error Handling for the event that the I2C bus was locked */
}
/* Piece of code that could take a few hundreds milliseconds to execute */
}
}
void Task2_OtherEquipmentOperations(void *argument)
{
/* The Semaphore will be given from the LPTIM Interrupt Handler, signalling that some maintenance
or periodic operation needs to be performed through I2C. */
if(xSemaphoreTake(xSemaphore_LPTIMInterruptFlag, portMAX_DELAY) == pdTRUE)
{
/* Only perform the I2C operations when the Mutex is available */
if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
{
/* Perform I2C Transaction */
/* Mutex will be given back, indicating that the shared I2C Bus is now available */
xSemaphoreGiveRecursive(xMutex_I2CBus);
}
else
{
/* Mutex was not available even after 2 seconds since the LPTIM interrupt triggered.
Perform Error Handling for the event that the I2C bus was locked */
}
/* Piece of code that could take a few seconds to execute */
}
}
互斥量是否经常用于避免优先级反转场景,或者它们(更经常)广泛用于防止两个操作可能同时发生?我想不出一个一个简单的场景,如果发生优先级反转,它可能对软件至关重要。
谢谢!
总的来说,我认为你的设计可行。信号量是两个任务完成工作的信号。互斥体保护共享的 I2C 资源。
但是,使用互斥量共享资源可能会导致复杂情况。首先,您的操作任务在等待 I2C 资源互斥锁时不响应新信号量 signals/events。其次,如果应用程序变得更加复杂并且您添加了更多的阻塞调用,那么设计可能会陷入阻塞、饥饿、竞争条件和死锁的恶性循环。您的简单设计还不存在,但您已经开始了。
作为替代方案,考虑让第三个任务负责处理所有 I2C 通信。 I2C 任务将等待消息(在队列或邮箱中)。当消息到达时,I2C 任务将执行相关的 I2C 通信。操作任务将像现在一样等待信号量 signal/event。但是,现在操作任务不会等待 I2C 互斥锁可用,而是 send/post 向 I2C 任务发送消息。使用此设计,您不需要互斥体来序列化对 I2C 资源的访问,因为 I2C 任务的 queue/mailbox 完成了序列化来自其他任务的消息通信请求的工作。同样在这个新设计中,每个任务只在一个地方阻塞,这样更干净并且允许操作任务对 signals/events.
IMO 这是一个完全错误的设计。
如果要通知任务有新数据 - 使用队列和 post 到队列。如果不止一个任务想要获取该数据,您可以 post 到多个队列(您可以发送一组队列)。您甚至可以在运行时添加队列。