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提供了一个名为Queue(队列)的功能,可以用于在任务之间发送和接收数据。在你的应用中,你可以使用队列来实现串口收发。 以下是一个示例,展示如何使用队列来实现串口收发: 1. 创建一个队列 在你的应用中,你可以使用函数xQueueCreate()来创建一个队列。例如: ``` #define QUEUE_LENGTH 10 #define ITEM_SIZE sizeof(char) QueueHandle_t xSerialRxQueue; xSerialRxQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE); ``` 在这里,我们创建了一个名为xSerialRxQueue的队列,它有10个元素,每个元素的大小为一个字符。 2. 从串口接收数据并发送到队列 当你从串口接收到数据时,你可以通过调用函数xQueueSend()将数据发送到队列中。例如: ``` char received_char; // Receive a character from the serial port received_char = receive_char_from_serial_port(); // Send the received character to the queue xQueueSend(xSerialRxQueue, &received_char, 0); ``` 在这里,我们从串口接收一个字符,并将该字符发送到xSerialRxQueue队列中。 3. 从队列中接收数据并发送到串口 当你需要从队列中获取数据并将其发送到串口时,你可以通过调用函数xQueueReceive()获取队列中的数据,并将其发送到串口中。例如: ``` char received_char; // Receive a character from the queue xQueueReceive(xSerialRxQueue, &received_char, portMAX_DELAY); // Send the received character to the serial port send_char_to_serial_port(received_char); ``` 在这里,我们从xSerialRxQueue队列中接收一个字符,并将该字符发送到串口中。 请注意,我们在调用xQueueReceive()函数时,使用了portMAX_DELAY参数。这意味着该函数会一直等待,直到队列中有数据可用。如果你希望函数在没有数据可用时立即返回,则可以使用其他参数。 以上就是使用FreeRTOS队列实现串口收发的示例。你可以根据你的应用需求进行修改和扩展。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值