FreeRTOS任务中如何等待两个DMA完成事件?xTaskNotify能否一行实现?
双DMA完成后的任务同步实现方案
用xTaskNotify实现(单等待逻辑)
完全可以通过xTaskNotify实现,核心思路是利用位掩码式通知来同步两个DMA完成事件:
- 为两个DMA分配独立的位标识(例如DMA1对应
0x01,DMA2对应0x02) - 在DMA完成回调(中断上下文)中,使用中断安全的通知API置位对应标识:
// 全局或任务可见的处理任务句柄 TaskHandle_t processingTaskHandle; // DMA1完成回调 void HAL_DMA1_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xTaskNotifyFromISR(processingTaskHandle, 0x01, eSetBits, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // DMA2完成回调 void HAL_DMA2_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xTaskNotifyFromISR(processingTaskHandle, 0x02, eSetBits, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } - 在数据处理任务中,用一行逻辑等待两个位都被置位:
uint32_t notifiedBits; // 等待0x01和0x02位都被置位,完成后清除这两个位 xTaskNotifyWait(0x00, 0x03, ¬ifiedBits, portMAX_DELAY); // 此时两个DMA均已完成,可开始处理ADC原始数据
其他可行方案
1. 事件组(更直观的多事件同步)
FreeRTOS事件组专为多事件等待场景设计,可读性更强:
// 全局事件组句柄 EventGroupHandle_t dmaEventGroup; // 初始化事件组(任务创建前执行) dmaEventGroup = xEventGroupCreate(); // DMA回调中设置事件位 void HAL_DMA1_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xEventGroupSetBitsFromISR(dmaEventGroup, (1 << 0), &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void HAL_DMA2_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xEventGroupSetBitsFromISR(dmaEventGroup, (1 << 1), &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // 处理任务中等待两个事件触发 xEventGroupWaitBits( dmaEventGroup, (1 << 0) | (1 << 1), // 等待的事件位掩码 pdTRUE, // 完成后清除事件位 pdTRUE, // 需所有事件位都置位才返回 portMAX_DELAY // 无限等待 ); // 两个DMA均完成,开始处理数据
2. 二值信号量
为每个DMA分配独立信号量,任务中依次获取两个信号量实现同步:
// 全局信号量句柄 SemaphoreHandle_t dma1Sem, dma2Sem; // 初始化信号量(任务创建前执行) dma1Sem = xSemaphoreCreateBinary(); dma2Sem = xSemaphoreCreateBinary(); // DMA回调中释放信号量 void HAL_DMA1_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(dma1Sem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void HAL_DMA2_CompleteCallback(DMA_HandleTypeDef *hdma) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(dma2Sem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // 处理任务中等待两个信号量 xSemaphoreTake(dma1Sem, portMAX_DELAY); xSemaphoreTake(dma2Sem, portMAX_DELAY); // 两个DMA均完成,开始处理数据
注意:所有中断上下文的FreeRTOS API调用,必须使用带
FromISR后缀的版本,并根据返回值决定是否触发任务切换,避免中断阻塞。
内容的提问来源于stack exchange,提问作者Gokhan Sahin
相关产品推荐
相关产品推荐

