【STM32串口】使用状态机接收HEX数据包的编程思维
1.HEX数据
1.HEX模式/十六进制模式/二进制模式:以原始数据的形式显示文本模式/字符模式:以原始数据编码后的形式显示
2.数据包
- 固定包长,含包头包尾
- 可变包长,含包头包尾
可变包长,若是接收的数据包中的数据与帧头帧尾一致,则可能会造成误判,数据发送错乱(这就需要加校验位,CRC校验),,如果采用固定包长则可以避免这一问题。
3.状态机
一个字节等于8位(0000 0000)uint8_t,0xFF
串口每收到一个字节,程序都会进一遍中断,在中断函数里我们可以拿到这个字节,但拿到之后,我们就退出了中断,对于上面数据包中的每个数据,都是独立的,很明显它具有前后关联性,包头、数据、包尾三种状态。此时我们需要有不同的处理逻辑,所以在程序中我们需要设计一个能记住不同状态的机制,在不同的状态执行不同的操作,同时还要进行状态的转移,这种程序思维就是状态机
4.程序
关于使用if
语句之后为什么是else if
?
程序是自上而下运行的,如果我们并列两个if的话,就会导致在状态1切换完之后,还没等到下一个数据到来,立马转到了状态2,这是我们不希望出现的一个状况。当然,如果使用switch case
同样也可以实现
uint8_t Serial_TxPacket[4]; //FF 01 02 03 04 FE
uint8_t Serial_RxPacket[4];
void USART1_IRQHandler(void)
{
//接收状态位,初始值为0,接收帧头,接收完后变为1,开始接收数据,接收完四个数据之后,变为2,开始等待帧尾
static uint8_t RxState = 0;
static uint8_t pRxPacket = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
uint8_t RxData = USART_ReceiveData(USART1);
if (RxState == 0)
{
if (RxData == 0xFF) //帧头来了
{
RxState = 1;
pRxPacket = 0;
}
}
else if (RxState == 1)
{
Serial_RxPacket[pRxPacket] = RxData;//把接收到的数据放到数组里面
pRxPacket ++;
if (pRxPacket >= 4)//接收的数据个数
{
RxState = 2;
}
}
else if (RxState == 2)
{
if (RxData == 0xFE)
{
RxState = 0;
Serial_RxFlag = 1;//接收完一个完整数据标志位
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u16 USART_RX_STA = 0; //接收状态标记
u8 RxFlag = 0, USART_RX_Total;
//id + x + y 240 * 160
void USART2_IRQHandler(void) //串口2中断服务程序
{
u8 Res, FlagSt;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART2); //读取接收到的数据
switch(RxFlag)
{
case 0:
if(Res == 0xaa)
RxFlag = 1;
break;
case 1:
if(Res == 0x55)
RxFlag = 2;
else
RxFlag = 0;
break;
case 2:
USART_RX_Total = Res;
RxFlag = 3;
break;
case 3:
USART_RX_BUF[USART_RX_STA ++] = Res;
if(USART_RX_STA>(USART_RX_Total-1))
{
USART_RX_STA=0;
RxFlag = 0;
}
break;
default:break;
}
}