一起使用二进制信号量和互斥量

Using Binary Semaphore and Mutex Together

我是 FreeRTOS 的新手,一直在阅读 FreeRTOS 文档并在 STM32F767 Nucleo 板上使用 FreeRTOS 编写简单代码。在我编写的简单程序中,我使用二进制信号量仅在 LPTIM 和 GPIO 中断发生时通过 xSemaphoreGiveFromISR() 向某些任务发出信号,并通过 xSemaphoreGive() 向不同的任务发出信号以执行来自另一个任务的某些操作。

假设我有一个 I2C1 外围设备连接到两个不同的设备:

我可以在上述情况下使用互斥量和二进制信号量吗?

二进制信号量将根据触发的相应中断向任务指示需要执行的操作,但互斥锁将在这两个任务之间共享,其中 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 到多个队列(您可以发送一组队列)。您甚至可以在运行时添加队列。