FreeRTOS通过消息队列实现串口命令解析(串口中断)

作者:Jack_G
时间:2023.08.08
版本:V1.0
上次修改时间:
环境:
\quad \quad \quad \quad STM32Cube MX V6.8.1
\quad \quad \quad \quad STM32CubeH7 Firmware Package V1.11.0 / 04-Nov-2022
\quad \quad \quad \quad Keil: V5.29

一、串口配置:

正常配置,不过需要勾选全局中断,后续在接收中断中将接收到的数据送入消息队列。
在usart.c文件中添加以下代码以重定义fputc以支持printf

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)  
//解决HAL库使用时,某些情况可能报错的bug
int _ttywrch(int ch)    
{
    ch=ch;
	return ch;
}
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
	/* Whatever you require here. If the only file you are using is */ 
	/* standard output using printf() for debugging, no file handling */ 
	/* is required. */ 
}; 
/* FILE is typedef’ d in stdio.h. */ 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->ISR&0X40)==0);//循环发送,直到发送完毕   
	USART1->TDR = (unsigned char) ch;      
	return ch;
	
//	HAL_UART_Transmit_DMA(&huart1, (uint8_t *)&ch, 1); 
//  return (ch);
	
}
#endif 

记得添加头文件

#include "stdio.h"	
#include "stdlib.h"
#include "string.h"

二、代码部分

创建指令解析的任务和消息队列和信号量:

其中信号量是用于串口接收完一次完整消息后通知任务开始获取消息队列的消息。
这部分代码主要:
创建命令解析任务、创建命令解析所用的消息队列、创建命令解析所用的信号量

void StartCMDAnalysisTask(void const * argument);//声明命令解析函数

osThreadId cmdAnalysisTaskHandle;//定义命令解析任务的句柄
SemaphoreHandle_t  uartSemaphore;//定义串口接收一次完成的信号量
QueueHandle_t  uartQueue;//定义串口消息队列的句柄

/* 创建指令解析任务 */
osThreadDef(cmdAnalysisTask, StartCMDAnalysisTask, osPriorityBelowNormal, 0, 512);
cmdAnalysisTaskHandle = osThreadCreate(osThread(cmdAnalysisTask), NULL);

/* 创建串口的消息队列 */
uartQueue = xQueueCreate((UBaseType_t ) 128,/* 消息队列的长度 */
                            (UBaseType_t ) sizeof( char));/* 消息的大小 */
if(NULL == uartQueue)
    printf("uartQueue created failed\r\n");
	
/* 创建串口接收完成的信号量 */
uartSemaphore = xSemaphoreCreateBinary();
if(NULL == uartSemaphore)
   printf("uartSemaphore created failed\r\n");

具体实现:

实现逻辑:
①开启串口的接收中断(UART_IT_RXNE)和空闲中断(UART_IT_IDLE)
\quad \quad 接收中断是每接收一个字符时 产生一次中断
\quad \quad 空闲中断是每完成一次接收后产生的中断

②每当接收中断时(即接收一个字符),将获取的字符送入消息队列
③当完成一个串口传输时(即本次传输完成),释放信号(通知任务传输完成)
④任务获取到信号量后,将消息队列中的数据尽数取出,直到’\r’,(这个\r是我自己定义的)
⑤将取出的消息放提前准备好的缓冲区中,使用strcmp函数进行对比
⑥清空缓冲区,等待下一次的’\r’

开启串口中断

笔者实测STM32H7将开启串口中断代码放于串口初始化后,会莫名奇妙直接进入空闲中断,查阅资料发现有些F1不会出现这种情况,因此笔者建议这部分放于创建完任务后,实测可行。

__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);//开启接收中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//开启空闲中断
__HAL_UART_CLEAR_IDLEFLAG(&huart1);	

串口中断函数

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 1 */
	uint8_t res = 0;
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET)
	{
		HAL_UART_Receive(&huart1,&res,1,1000);
		//将数据放入消息队列
		xQueueSendFromISR(uartQueue,&res,NULL);
		__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);
	}
	//空闲中断
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)
	{
		//一帧数据接收完成		
		//释放信号量
		xSemaphoreGiveFromISR(uartSemaphore,NULL);
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	}
  /* USER CODE END USART1_IRQn 1 */
}

解析任务

void StartCMDAnalysisTask(void const * argument)
{
	char Rx_Buffer[128];
	int index=0;
	memset(Rx_Buffer,'\0',128);
  for(;;)
  {
		if(uxSemaphoreGetCount(uartSemaphore) == 1) //串口完成了一次接收
		{
			xSemaphoreTake(uartSemaphore, ( TickType_t ) 10);//获取信号量

/* 获取消息队列中的数据到Buffer中直至获取到\r */		
			do{
				if(xQueueReceive(uartQueue,&Rx_Buffer[index],( TickType_t ) 10) == pdTRUE)
				{
					index++;
				}				
				else
					osDelay(10);
			}while(Rx_Buffer[index-1] != '\r');
			Rx_Buffer[index-1] = '\0';
			index = 0;	
/* 命令解析 命令 存于Rx_Buffer中,解析完一次则清空一次*/
			if(strcmp(Rx_Buffer,"AT") == 0)
			{
				printf("ok\r\n");
			}
			/* 放置你需要解析的命令
			if(strcmp(Rx_Buffer,"your CMd") == 0)
			{
				//定义处理的方式
			}
			*/
			memset(Rx_Buffer,'\0',128);			
		}
    osDelay(2);
  }
}

这里只写了AT,返回OK,
FreeRTOS通过消息队列实现串口命令解析(串口中断)_第1张图片

你可能感兴趣的:(单片机,嵌入式硬件)