STM32CubeMX+FreeRTOS使用osMessageGet,osMessagePut问题记录

本文详细描述了如何在STM32F103C8上利用STM32Cubemx配置FreeRTOS工程,包括串口1的空闲中断处理、DMA接收不定长数据、消息队列操作以及内存管理。特别关注osMessagePut和osMessageGet的使用以及为何选择uint32_t作为队列项大小。
摘要由CSDN通过智能技术生成

stm32cubemx生成工程

  1. 本次使用STM32F103C8,串口1演示空闲中断+DMA接收不定长数据,发送到消息队列,从任务中读取消息,通过printf打印。
  2. 本文只留干货,关于STM32系统和串口配置不做讲解,仅讲解FreeRTOS配置部分,串口和DMA使用的是LL库。

stm32cubemx配置freertos工程

RTOS配置只做如下修改,内核版本选择V1,内存分配选择动态,将堆栈尺寸改大。
在这里插入图片描述
添加队列和任务,将队列长度设置为3,每项大小设置为uint32_t(此处设置为uint32_t原因是:32位单片机的指针大小为4字节),为什么设置为uint32_t,后文讲解。
在这里插入图片描述

osMessagePut,osMessageGet函数

osMessagePut

使用inHandlerMode()判断当前是否在中断中,这样一来,在中断和任务中,我们都只用调用 osMessagePut函数 即可 ,osMessageGet函数也是如此。

osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
{
  portBASE_TYPE taskWoken = pdFALSE;
  TickType_t ticks;
  
  ticks = millisec / portTICK_PERIOD_MS;
  if (ticks == 0) {
    ticks = 1;
  }
  
  if (inHandlerMode()) {
    if (xQueueSendFromISR(queue_id, &info, &taskWoken) != pdTRUE) {
      return osErrorOS;
    }
    portEND_SWITCHING_ISR(taskWoken);
  }
  else {
    if (xQueueSend(queue_id, &info, ticks) != pdTRUE) {
      return osErrorOS;
    }
  }
  
  return osOK;
}

osMessageGet

osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
{
  portBASE_TYPE taskWoken;
  TickType_t ticks;
  osEvent event;
  
  event.def.message_id = queue_id;
  event.value.v = 0;
  
  if (queue_id == NULL) {
    event.status = osErrorParameter;
    return event;
  }
  
  taskWoken = pdFALSE;
  
  ticks = 0;
  if (millisec == osWaitForever) {
    ticks = portMAX_DELAY;
  }
  else if (millisec != 0) {
    ticks = millisec / portTICK_PERIOD_MS;
    if (ticks == 0) {
      ticks = 1;
    }
  }
  
  if (inHandlerMode()) {
    if (xQueueReceiveFromISR(queue_id, &event.value.v, &taskWoken) == pdTRUE) {
      /* We have mail */
      event.status = osEventMessage;
    }
    else {
      event.status = osOK;
    }
    portEND_SWITCHING_ISR(taskWoken);
  }
  else {
    if (xQueueReceive(queue_id, &event.value.v, ticks) == pdTRUE) {
      /* We have mail */
      event.status = osEventMessage;
    }
    else {
      event.status = (ticks == 0) ? osOK : osEventTimeout;
    }
  }
  
  return event;
}

如果是先学习了FreeRTOS,再来使用osMessageGet这个函数的时候,就会有个坑,这个函数返回的是event,由osEvent 定义,如下

typedef struct  {
  osStatus                 status;     ///< status code: event or error information
  union  {
    uint32_t                    v;     ///< message as 32-bit value
    void                       *p;     ///< message or mail as void pointer
    int32_t               signals;     ///< signal flags
  } value;                             ///< event value
  union  {
    osMailQId             mail_id;     ///< mail id obtained by \ref osMailCreate
    osMessageQId       message_id;     ///< message id obtained by \ref osMessageCreate
  } def;                               ///< event definition
} osEvent;

osMessageGet中调用xQueueReceiveFromISR和xQueueReceive,其中传入的参数为 &event.value.v,在osEvent定义的结构体中,它是一个共用体,可以作为uint32_t/int32_t(数值),或者void *(指针)使用。osMessagePut如果只是传入一个数值使用并没有问题,但在freertso是以队列项传入的,而在cmsis-rtos中传入消息队列的是一个4字节数据(可以是指针,可以是一个值),所以在调用osMessageGet返回后值中,能够使用的就是一个指针,或者为一个4字节数值。stm32cubemx虽然使用freertos,但是也做了cmsis-rtos,这就是我为什么在stm32cubemx配置队列的时候,将项的大小选择了uint32_t(4字节),目的就是把队列项作为一个指针使用。

应用

参考CMSIS-RTOS V1.03https://www.keil.com/pack/doc/CMSIS/RTOS/html/index.html
打开stm32cubemx创建的工程,在main.h定以我的串口消息数据包类型

typedef struct{
   uint8_t buff[128];
   uint16_t len;
}USART_Msg_Def;

使用cmsis-rtos中的内存申请函数,为我的消息缓冲区申请内存。

osPoolId  USART_Msg_pool;//定义我消息缓冲区的内存池句柄
uint8_t Rx1_buff[128];//串口1的DMA接受缓冲区

在队列创建代码的后面加入内存申请代码

/* definition and creation of myQueue01 */
  osMessageQDef(myQueue01, 3, uint32_t);
  myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
	osPoolDef(USART_Msg_pool, 3, USART_Msg_Def);
	USART_Msg_pool = osPoolCreate(osPool(USART_Msg_pool));
  
  /* USER CODE END RTOS_QUEUES */

在StartDefaultTask加入消息读取函数和配置DMA+空闲中断接收

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
	USART_Msg_Def *message;
	osEvent evt;
	// 接收DMA配置
    LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_5, LL_USART_DMA_GetRegAddr(USART1));// 配置外设地址
    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_5, (uint32_t)Rx1_buff);// 配置缓冲区地址
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, 128);// 配置缓冲区大小
    // LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_5);// 开启DMA传输完成中断
    // LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_5);// 开启DMA传输错误中断
    NVIC_DisableIRQ(DMA1_Channel5_IRQn);
    LL_USART_EnableDMAReq_RX(USART1);// 使能串口的DMA接收功能
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5); // 使能DMA通道
    LL_USART_EnableIT_IDLE(USART1);// 开启串口1中断
  /* Infinite loop */
  for(;;)
  {
	  evt = osMessageGet(myQueue01Handle,0);
	  if (evt.status == osEventMessage) 
	  {
		message = (USART_Msg_Def *)evt.value.p;
		  printf("队列读取到消息:(%d)%s",message->len,message->buff);
		osPoolFree(USART_Msg_pool, message);//释放内存
		  
      }
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
  	 //任务二中就加入一个简单的闪灯代码吧
	  LL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
    osDelay(500);
  }
  /* USER CODE END StartTask02 */
}

中断加入消息发送函数

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	USART_Msg_Def *message;
	if (LL_USART_IsActiveFlag_IDLE(USART1) && LL_USART_IsEnabledIT_IDLE(USART1))
    {
        LL_USART_ClearFlag_IDLE(USART1);// 清除空闲中断标志位
        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);// 关闭DMA通道
        LL_USART_ReceiveData8(USART1);// 读取一下接收寄存器
		message = (USART_Msg_Def*)osPoolAlloc(USART_Msg_pool);//申请内存
        message->len = 128 - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_5);// 计算接收长度
		memcpy(message->buff,Rx1_buff,message->len);//数据拷贝
		osMessagePut(myQueue01Handle, (uint32_t)message, osWaitForever);//写入队列
		printf("队列写入到消息:(%d)%s",message->len,message->buff);
        // 重新设置数据长度并打开DMA,使DMA从头开始接收
        LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, 128);
        LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
    }
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

测试

在这里插入图片描述
第一次我发送了hello word,下面勾选上了加回车换行,总字节为13,从任务函数中读取出了。
第二次我发送了This is a test program,勾选上了加回车换行,总字节为24,从任务函数中读取出了。
第三次我发送了hello word,下面勾选上了加回车换行,总字节为13,从任务函数中读取出了,但是在打印的时候“hello word”的后面却多了“t program”,这是为什么呢,留给爱学习的你去思考(温馨提示:这与内存管理有关)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾风染尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值