第三个实验例子:FreeRTOS USART DMA 空闲中断接收 队列

第三个实验例子:FreeRTOS USART DMA 空闲中断接收 队列

上一篇是串口中断接收数据,然后通过消息队列转发。它实现的方法是每收到一个字节发送一次,这个做法用在串口转发数据上实时性还是不错的,但是在平时多数的串口应用中更多的是需要实际通讯。往往是单片机接收一帧数据,根据通讯协议实现某些功能,然后再回答。为此我尝试采用通过采用DMA方式,利用空闲中断来接收一帧数据,然后发送数据到队列,线程收到数据后处理数据。我采用modbus通讯协议做实验。

CubeMx的FreeRTOS中创建Queue不能直接支持结构体或数组,为了这个问题我反复做了好几种实验。先总结一下:

方法一:

当空闲中断产生,收到一帧数据时,通过xQueueSendToBackFromISR()函数发送帧数据的指针值,然后在线程中用osMessageGet()函数接收后,处理数据。这个做法可以实现功能,但是由于传递的是指针,所以在线程中处理数据时如果串口又有新的数据到达,就会发生问题。

方法二:

自己建一个FIFO的帧数据缓存(定义一帧数据的结构体,再做一个结构体的数组),息队列传递当前一帧数据的指针,这个方法也可以能实现功能,但是我担心自己定义FIFO帧数据缓存,如果代码写得不好是否有不可预测的问题。

方法三:

         第三种方法是我要重点介绍的:

 

  1. CubeMx设置

串口设置

FreeRTOS设置:

只新建二个Task,Queue之后在Keil手工创建

 

 

然后生成代码。

 

  1. 程序代码

串口部分参考我之前的博文:STM32的串口空闲中断接收不定长数据

其中串口空闲中断函数要增加消息队列发送的代码。

 

void Usart1Receive_IDLE(UART_HandleTypeDef *huart)

{

         uint32_t temp;

         BaseType_t xHigherPriorityTaskWoken = pdFALSE;        

        

         if(huart->Instance==USART1) //USART1

         {

                   if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))

                   {

                            __HAL_UART_CLEAR_IDLEFLAG(&huart1);

                            HAL_UART_DMAStop(&huart1);

                            temp = huart1.hdmarx->Instance->CNDTR;

                            ModbusReceiveData.rx_len =  RECEIVELEN - temp;

                            ModbusReceiveData.receive_flag=1;

                            //队列

                            xQueueSendToBackFromISR(myQ02Handle,&ModbusReceiveData,&xHigherPriorityTaskWoken);

                            ModbusReceiveData.receive_flag=0;

                            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);       

                           

                            HAL_UART_Receive_DMA(&huart1,ModbusReceiveData.usartDMA_rxBuf,RECEIVELEN);

                   }

         }

}

 

在freertos.c中添加下列语句:

  1. 声明

/* USER CODE BEGIN Variables */

extern UART_HandleTypeDef huart1;

osMessageQId myQ02Handle;        //声明MessageQueue句柄

 

/* USER CODE END Variables */

  1. 创建MessageQueue

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

         myQ02Handle=xQueueCreate(3, sizeof(_USART_BUFF_TYPE));

  /* USER CODE END RTOS_QUEUES */

队列深度可以根据实际情况,我尝试设置为1做测试,可以正常运行。前提是在上位机检查到通讯超时之前单片机已经应答。为了保险起见我最后设置了2 。

 

  1. 线程接收

/* USER CODE BEGIN Header_StartTask02 */

/**

* @brief Function implementing the myTask02 thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTask02 */

void StartTask02(void const * argument)

{

  /* USER CODE BEGIN StartTask02 */

         _USART_BUFF_TYPE   p;

  /* Infinite loop */

  for(;;)

  {

                  

                   xQueueReceive(myQ02Handle,(void *)&p,osWaitForever);   //接收信息

                   HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);

                   if((p).receive_flag)

                   {

                            HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET);

                            checkComm0Modbus(&p)       ;        //Modbus数据解析

                            HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);

                   }

                   HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);

    osDelay(1);

 

  }

  /* USER CODE END StartTask02 */

}

 

 

 

实际效果:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值