CMSIS/FreeRTOS中队列(Queue)的使用

在有流式数据处理的嵌入式系统中,队列(Queue)是几乎必然被使用的工具,但大多数开发板提供的FreeRTOS例程是不包含队列的,要使用还要自己研究。这次我的样例把串口收到的数据按字节塞入队列,再让另一个线程处理,是一种相对画蛇添足的做法,这么做主要目的是说明队列如何使用。队列更适合用来处理ADC/DAC采样数据、通信模块固定大小的数据包等。

这次我仍是使用自制的STM32F0模块实验,与上次一样,我们还是用STM32CubeMX来生成初始代码,在FreeRTOS中添加队列:
队列设置这里,我设队列的每个项目为uint8_t类型,队列元素最多16个。其实更常见的是设置成某个结构体,这可以等到代码生成后,在代码里修改。这一设置将生成以下代码:

osMessageQueueId_t QueueUartByteHandle;
const osMessageQueueAttr_t QueueUartByte_attributes = {
  .name = "QueueUartByte"
};

QueueUartByteHandle = osMessageQueueNew (16, sizeof(uint8_t), &QueueUartByte_attributes);

FreeRTOS的队列占用空间可以根据以下公式计算:
S p a c e ( B y t e s ) = 92 + E l e m S i z e × n E l e m Space(Bytes) =92 + ElemSize \times nElem Space(Bytes)=92+ElemSize×nElem
比如我这里设置一个元素是1字节,最多16个,那么就会占用92+16=108字节。

我们还是用CMSIS v2的API,而非直接用FreeRTOS的API。参考CMSIS的文档,CMSIS把队列叫做“消息队列”(Message Queue),实现FIFO功能,入队和出队API分别为:

osStatus_t 	osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
osStatus_t 	osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);

其中,mq_id是队列的变量名,即我们定义的QueueUartByteHandlemsg_ptr是要入队/出队的数据块指针;msg_prio比较奇怪,入队和出队对应的变量类型不同,表示优先级,但一般都赋NULL;最后的timeout,不等待就为0,永远等待用osWaitForever。这两个函数都可以在中断中运行。

对于FreeRTOS而言,各个API都有普通线程版本和中断版本,CMSIS在编写它的API时,就根据当时状态判断,以CMISIS的osMessageQueuePut为例,通过IS_IRQ()宏进行判断:

// From cmsis_os2.c
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout) {
  QueueHandle_t hQueue = (QueueHandle_t)mq_id;
  osStatus_t stat;
  BaseType_t yield;

  (void)msg_prio; /* Message priority is ignored */

  stat = osOK;

  if (IS_IRQ()) {
    if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
      stat = osErrorParameter;
    }
    else {
      yield = pdFALSE;

      if (xQueueSendToBackFromISR (hQueue, msg_ptr, &yield) != pdTRUE) {
        stat = osErrorResource;
      } else {
        portYIELD_FROM_ISR (yield);
      }
    }
  }
  else {
    if ((hQueue == NULL) || (msg_ptr == NULL)) {
      stat = osErrorParameter;
    }
    else {
      if (xQueueSendToBack (hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {
        if (timeout != 0U) {
          stat = osErrorTimeout;
        } else {
          stat = osErrorResource;
        }
      }
    }
  }

  return (stat);
}

我让串口接收中断回调函数把接收到的数据压入队列,让一个任务线程去处理,中断回调通过一个计数信号量通知任务,信号量也在STM32CubeMX中定义:
计数信号量代码生成后,还要把信号量的初始值改为0:

semAddToQueueHandle = osSemaphoreNew(16, 0, &semAddToQueue_attributes);

最后,我把中断回调函数和任务线程函数的代码拍上来,展示如何信号量如何同步、如何向队列存取数据:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	uint8_t recv_char;
	HAL_UART_Receive_IT(huart, &recv_char, 0x01);
	osMessageQueuePut(QueueUartByteHandle, &recv_char, 0U, 0U); // without waiting
	osSemaphoreRelease(semAddToQueueHandle);
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13); // just to show uart is working
}
void task1fxn(void *argument)
{
  /* Infinite loop */
	uint8_t  recv_char;
  for(;;)
  {
		osSemaphoreAcquire(semAddToQueueHandle,osWaitForever);
    osDelay(1);
		osMessageQueueGet(QueueUartByteHandle, &recv_char, NULL, 0);
		HAL_UART_Transmit_IT(&huart1, &recv_char, 0x01);
  }
}
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值