UART STM32多任务多数据串口接收及处理方法

UART

STM32多任务多数据串口接收及处理方法

设立大小缓冲区,接收存储与处理调用数据分开。

基本思路

  • 采用数据帧的方式进行传递,数据帧格式为 帧头 数据1 数据2 数据3 数据4 帧尾

  • 采用状态机思想进行解析比对缓存区中的数据正确性

  • 通过串口中断接收函数,每次将接收到的一个数据存入大缓冲区,如何再建立小缓冲区将要处理使用的数据从大缓存区中取出使用。

  • 优点:接收与处理分别进行,有利于防止数据处理卡死、混乱或丢包。

  • 缺点:要明确定义好每个数据的帧头帧尾在缓冲区的位置,存储调用数据要精确。

实现代码

  • 本代码以STM32与Openmv串口通信为例,STM32接收来自Openmv的传递的数据帧格式的数据,使用HAL库*

  • 第一项

使用HAL_UART_Receive_IT开启串口接收中断函数,开始接受Openmv传来的每一帧数据。

  • 第二项

定义好接受缓冲区和定义各种所需变量,定义好处理比对函数状态机。

//定义接收缓冲区大小和帧头帧尾
#define MAX_RX_BUFFER_SIZE 255 //大缓冲区的大小
#define  HEAD  0xFC    //帧头
#define  END   0xFE    //帧尾

// 定义接收缓冲区和定义的各种变量
uint8_t rxBuffer[MAX_RX_BUFFER_SIZE]; //大缓冲区
uint8_t JugleBuffer[15];         //判断缓冲区索引
uint8_t rxIndex = 0;             //缓冲区的索引
volatile uint8_t state_flag=0;   //因为主频执行速度太快,而串口中断相对较慢,所以需要一个标志位来判断是否执行 

/*数据帧状态机*/
typedef enum {
    FRAME_HEAD = 0,  //帧头
    FRAME_DATA1,     //数据1
    FRAME_DATA2,     //数据2
    FRAME_DATA3,     //数据3
    FRAME_DATA4,     //数据4
    FRAME_END        //帧尾
} FRAME_STATE;

FRAME_STATE frame_state = FRAME_HEAD; /*定义帧检测状态机,初始化检测帧头状态,定义了一个名为frame_state 的变量,类型为 FRAME_STATE 枚举类型,初始值为 FRAME_HEAD,用于表示数据帧的状态机,用于检测数据帧的帧头*/
  • 第三项

在串口中断接收回调函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)中将接收到的数据存入大缓冲区rxBuffer中。

// 串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART3)
	{
		// 将接收到的数据存入缓冲区
		rxBuffer[rxIndex] = huart->Instance->DR;//huart->Instance->DR是一个串口接收寄存器的值,它存储了从串口接收到的数据。
		if (rxIndex >= MAX_RX_BUFFER_SIZE)
		{
			rxIndex = 0;
		}
		else
		{
			rxIndex++;
		}
		state_flag=1;     //接收到数据,标志位置1。特别注意!!!!此时在主频中开始调用比对获得函数 `FrameJudge(uint8_t *Recv_Data,uint8_t *Sendbuffer)`
		// 继续接收下一个字节
		HAL_UART_Receive_IT(huart, (uint8_t *)&huart->Instance->DR, 1);
	}
}
  • 第四项

书写取出小缓冲区数据的函数。GetBufferdata(uint8_t *my_array)

//取出小缓冲区数据的函数
void GetBufferdata(uint8_t *my_array)
{
    my_data1=my_array[1];//取出数据
    my_data2=my_array[2];
    my_data3=my_array[3];
    my_data4=my_array[4];
    //有几个数据取几个就行,记得定义my_data1,my_data2等变量
} 
  • 第五项 最重要的一项!验证比对数据的正确性并从大缓冲区中取出至小缓冲区。定义比对获得函数FrameJudge(uint8_t *Recv_Data,uint8_t *Sendbuffer)其中Recv_Data为大缓冲区参数,Sendbuffer为小缓冲区参数。
void FrameJudge(uint8_t *Recv_Data,uint8_t *Sendbuffer)
{
    volatile static uint8_t HeadIndex = 0;           //帧头索引
    volatile static uint8_t EndIndex = 0;            //帧尾索引
    static uint8_t BufferIndex = 0;         //判断缓冲区索引,大缓冲区的数据位置
    //如果要想改成>=,就要把下面这个if放到整个函数的最后面
    //if((BufferIndex > MAX_RX_BUFFER_SIZE)||(HeadIndex>=MAX_RX_BUFFER_SIZE-3))//Recv_Data这个数组在主函数里要对应UART2_Rx_Buffer[MAX_RX_BUFFER_SIZE]这个数组
    if(BufferIndex > MAX_RX_BUFFER_SIZE)    //MAX_RX_BUFFER_SIZE大小为255
    {
        BufferIndex = 0; //防止缓冲区溢出,如果大于了缓冲区,就从头开始覆盖。
        frame_state = FRAME_HEAD;//也就是说,当缓冲区溢出了,就让帧检测状态机从头开始检测,就算最后一次检测到帧头,也不要了
        HeadIndex = 0;
        EndIndex = 0;  
    }
    switch (frame_state)//此次使用先前定义的状态机进行数据的解析比对
    {
		case FRAME_HEAD://也就是0
        {
            if (Recv_Data[BufferIndex] == HEAD)    //此前定义的HEAD为帧头0xFC,时刻铭记Recv_Data为大缓冲区!!!
            {
                HeadIndex = BufferIndex;//记录帧头在那个大的缓冲区的索引
                frame_state = FRAME_DATA1;//帧检测状态机转移到数据状态,从0变到1
                BufferIndex++;//从0开始找帧头,找到了就加1,下次就从上次帧头的位置开始找
            }
            else
            {
                BufferIndex++;//如果没读到帧头就往下找
            }
            state_flag=0;//主频中执行完一次,就让其标志位清零,等待下一次串口中断,也就是说,让主频的处理与串口中断的处理同步  
            break;
        }
			case FRAME_DATA1://也就是1
        {
            frame_state = FRAME_DATA2;//帧检测状态机转移到数据2状态,从1变到2
            BufferIndex++;
            state_flag=0;
            break;
        }
        	case FRAME_DATA2://也就是1
        {
            frame_state = FRAME_DATA3;//帧检测状态机转移到数据3状态,从2变到3
            BufferIndex++;
            state_flag=0;
            break;
        }

        	case FRAME_DATA3://也就是1
        {
            frame_state = FRAME_DATA4;//帧检测状态机转移到数据4状态,从3变到4
            BufferIndex++;
            state_flag=0;
            break;
        }

        	case FRAME_DATA4://也就是1
        {
            frame_state = FRAME_END;//帧检测状态机转移到帧尾状态,从4变到END
            BufferIndex++;
            state_flag=0;
            break;
        }
			case FRAME_END:
        {
            if (Recv_Data[BufferIndex] == END)
            {
                EndIndex = BufferIndex;//记录帧尾在那个大的缓冲区的索引
                // memcpy(Sendbuffer,&Recv_Data[HeadIndex],EndIndex-HeadIndex+1);//将帧数据拷贝到判断缓冲区
                memcpy(Sendbuffer,Recv_Data + HeadIndex,4);//将帧数据拷贝到判断缓冲区,即小缓冲区
                GetBufferdata(Sendbuffer);//将判断缓冲区的数据拷贝到帧缓冲区
//                memset(Sendbuffer,0,15);//清空串口缓冲区
                BufferIndex++; 
                frame_state = FRAME_HEAD;//帧检测状态机转移到帧头状态,从2变到0
            }
            else
            {
                BufferIndex++;
                frame_state = FRAME_HEAD;//帧检测状态机转移到帧头状态,从2变到0,也就是说如果帧尾不是帧尾,那么就从头开始找
            }
            state_flag=0;
            break;
        }
        default:
        {
            break;
            //没在这加state_flag=0;是有深意的,因为如果发生错误跳出,但此时state_flag还没清零
            //又因为主频会比串口中断处理快很多,所以主频就会把上一个发生意外的再次处理一遍
        }
    }  
}
  • 第六项 主频函数处理 main.c中
while(1)
{
    if(state_flag == 1)
    {
      FrameJudge(rxBuffer, JugleBuffer);//rxBuffer大缓冲区,JugleBuffer小缓冲区
    }	
}
    	

``


  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值