HEX数据包

固定包长,含包头包尾:每个数据包的长度固定不变,前面是包头,后面是包尾
可变包头,含包头包尾:每个数据包的长度可以不一样,前面是包头,后面是包尾
固定包头这里规定,每组4个字节,在这4个字节之前,加上定义的包头0xFF,后面加上定义的包尾0xFE
另外,还可以不要包尾,只使用一个包头FF,接收到包头FF后,开始接收,收够4个字节之后,置标志位,一个数据包接收完成。
如果传输的数据本身就是FF和FE怎么办呢?
限制载荷数据的范围,在发送时进行限幅;
或者使用固定长度的数据包,可以通过包头包尾对其数据,哪个数据是包头,哪个数据是包尾,哪个数据是载荷数据,在接收载荷数据时,不会判断这个数据是否为包头或包尾;
或者增加包头包尾的数量,让它尽量呈现出载荷数据出现不了的状态
如果载荷不会和包头包尾重复,可以选择可变包长
数据长度可以任意改变,因为包头和包尾都是唯一的,出现包头,就接收,出现包尾,就停止接收。
文本数据包

在文本数据包中,数据经过了一次编码和译码,表现出来的文本格式。
由于数据译码成了字符形式,就可以有大量的字符作为包头和包尾,可以有效避免载荷和包头包尾重复的问题。
HEX数据包接收

对于数据包来说,接收具有前后的关联性,包头->数据->包尾,对于这三种状态,需要不同的处理逻辑,即状态机。
对于接收数据,可以定义为三个状态。等待包头,然后接收数据,最后等待包尾,每个状态用一个变量来标志一下。
文本数据包接收

串口收发HEX数据包
Serial.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 | uint8_t Serial_TxPacket[4];//发数组uint8_t Serial_RxPacket[4];//收数组uint8_t Serial_RxFlag;//收到标志位 void Serial_Init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//引脚模式,TX是USART外设控制的输出脚,选择复用推挽输出;RX引脚是USART外设控制的输入脚,选择输入模式,一般RX配置为浮空输入或者上拉输入。 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//RX配置为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600;//波特率 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制 USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送模式 USART_InitStructure.USART_Parity = USART_Parity_No;//校验位,选择不需要校验 USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长,由于前面没有选择校验,这里选8位 USART_Init(USART1,&USART_InitStructure); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART1,ENABLE);} void Serial_SendByte(uint8_t Byte){ USART_SendData(USART1, Byte); while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );//获取标志位,发送寄存器空标志位,避免产生数据覆盖的问题 } void Serial_SendArray(uint8_t *Array, uint16_t Length){ uint16_t i; for (i=0;i<Length;i++) { Serial_SendByte(Array[i]); }} void Serial_SendString(char *String){ uint8_t i; for ( i = 0; String[i] != '\0'; i++ ) { Serial_SendByte(String[i]); }} uint32_t Serial_Pow(uint32_t X,uint32_t Y){ uint32_t Result = 1; while (Y--) { Result *= X; } return Result;} void Serial_SendNumber(uint32_t Number, uint8_t Length){ uint8_t i; for ( i = 0; i<Length; i++ ) { Serial_SendByte(Number / Serial_Pow(10,Length - i - 1) % 10 + '0'); }} int fputc(int ch, FILE *f)//printf 输出到串口{ Serial_SendByte(ch); return ch;} void Serial_Printf(char *format, ... ){ char String[100]; va_list arg; va_start(arg,format); vsprintf(String, format, arg); va_end(arg); Serial_SendString(String);} void Serial_SendPacket(void)//发送数据包{ Serial_SendByte(0xFF);//发送包头 Serial_SendArray(Serial_TxPacket,4);//发送数据包内容 Serial_SendByte(0xFE);//发送包尾} uint8_t Serial_GetRxFlag(void){ if(Serial_RxFlag == 1) { Serial_RxFlag = 0; return 1; } return 0;} void USART1_IRQHandler(void){ static uint8_t RxState = 0; static uint8_t PRxPacket = 0; if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET) { //三种状态 uint8_t RxData = USART_ReceiveData(USART1);//获取当前接收到的数据 if(RxState == 0) { if(RxData == 0xFF)//接收到包头 { RxState = 1;//进入下一步,开始接收有效载荷 } } else if(RxState == 1) { Serial_RxPacket[PRxPacket] = RxData;//接收一个数据 PRxPacket++;//存的位置自增 if(PRxPacket >= 4)//4组数据全部记录完 { RxState = 2;//进入下一步,等待包尾 PRxPacket = 0;//记录的位置清0,为下一次准备 } } else if(RxState == 2) { if(RxData == 0xFE)//接收到包尾 { RxState = 0;//回到最初状态,一个数据包接收完成 Serial_RxFlag = 1;//接收完成标志位置1 } } USART_ClearITPendingBit(USART1,USART_IT_RXNE); }} |
main.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | uint8_t KeyNum; int main(void){ OLED_Init(); KEY_Init(); Serial_Init(); OLED_ShowString(1,1,"TxPacket:"); OLED_ShowString(3,1,"RxPacket:"); //给数组一个初值 Serial_TxPacket[0] = 0x01; Serial_TxPacket[1] = 0x02; Serial_TxPacket[2] = 0x03; Serial_TxPacket[3] = 0x04; while (1) { KeyNum = Key_GetNum(); if (KeyNum == 1)//按钮按下 { Serial_TxPacket[0] ++;//发数组的值自增1 Serial_TxPacket[1] ++; Serial_TxPacket[2] ++; Serial_TxPacket[3] ++; Serial_SendPacket();//发送数组 OLED_ShowHexNum(2,1,Serial_TxPacket[0],2);//显示发送的数组 OLED_ShowHexNum(2,4,Serial_TxPacket[1],2); OLED_ShowHexNum(2,7,Serial_TxPacket[2],2); OLED_ShowHexNum(2,10,Serial_TxPacket[3],2); } if (Serial_GetRxFlag() == 1)//收到数组 { OLED_ShowHexNum(4,1,Serial_RxPacket[0],2);//显示收到的数组 OLED_ShowHexNum(4,4,Serial_RxPacket[1],2); OLED_ShowHexNum(4,7,Serial_RxPacket[2],2); OLED_ShowHexNum(4,10,Serial_RxPacket[3],2); } }} |
串口收发文本数据包
Serial.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 | char Serial_RxPacket[100];uint8_t Serial_RxFlag; void Serial_Init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//引脚模式,TX是USART外设控制的输出脚,选择复用推挽输出;RX引脚是USART外设控制的输入脚,选择输入模式,一般RX配置为浮空输入或者上拉输入。 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//RX配置为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600;//波特率 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制 USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送模式 USART_InitStructure.USART_Parity = USART_Parity_No;//校验位,选择不需要校验 USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长,由于前面没有选择校验,这里选8位 USART_Init(USART1,&USART_InitStructure); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART1,ENABLE);} void Serial_SendByte(uint8_t Byte){ USART_SendData(USART1, Byte); while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );//获取标志位,发送寄存器空标志位,避免产生数据覆盖的问题 } void Serial_SendArray(uint8_t *Array, uint16_t Length){ uint16_t i; for (i=0;i<Length;i++) { Serial_SendByte(Array[i]); }} void Serial_SendString(char *String){ uint8_t i; for ( i = 0; String[i] != '\0'; i++ ) { Serial_SendByte(String[i]); }} uint32_t Serial_Pow(uint32_t X,uint32_t Y){ uint32_t Result = 1; while (Y--) { Result *= X; } return Result;} void Serial_SendNumber(uint32_t Number, uint8_t Length){ uint8_t i; for ( i = 0; i<Length; i++ ) { Serial_SendByte(Number / Serial_Pow(10,Length - i - 1) % 10 + '0'); }} int fputc(int ch, FILE *f)//printf 输出到串口{ Serial_SendByte(ch); return ch;} void Serial_Printf(char *format, ... ){ char String[100]; va_list arg; va_start(arg,format); vsprintf(String, format, arg); va_end(arg); Serial_SendString(String);} void USART1_IRQHandler(void){ static uint8_t RxState = 0; static uint8_t PRxPacket = 0; if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET) { //三种状态 uint8_t RxData = USART_ReceiveData(USART1);//获取当前接收到的数据 if(RxState == 0) { if(RxData == '@' && Serial_RxFlag == 0)//接收到包头 { RxState = 1;//进入下一步,开始接收有效载荷 PRxPacket = 0;//计数器清0 } } else if(RxState == 1) { if(RxData == '\r')//先判断接收到的是不是包尾 { RxState = 2; } else//不是包尾,记录数据 { Serial_RxPacket[PRxPacket] = RxData;//接收一个数据 PRxPacket++;//存的位置自增 } } else if(RxState == 2) { if(RxData == '\n')//接收到包尾 { RxState = 0;//回到最初状态,一个数据包接收完成 Serial_RxPacket[PRxPacket] = '\0'; Serial_RxFlag = 1;//接收完成标志位置1 } } USART_ClearITPendingBit(USART1,USART_IT_RXNE); }} |
main.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | int main(void){ OLED_Init(); LED_Init(); Serial_Init(); OLED_ShowString(1,1,"TxPacket:"); OLED_ShowString(3,1,"RxPacket:"); while (1) { if(Serial_RxFlag==1) { OLED_ShowString(4,1," "); OLED_ShowString(4,1,Serial_RxPacket); if(strcmp(Serial_RxPacket,"LED_ON") == 0) { LED1_ON(); Serial_SendString("LED_ON_OK"); OLED_ShowString(4,1," "); OLED_ShowString(4,1,"LED_ON_OK"); } else if(strcmp(Serial_RxPacket,"LED_OFF") == 0) { LED1_OFF(); Serial_SendString("LED_OFF_OK"); OLED_ShowString(4,1," "); OLED_ShowString(4,1,"LED_OFF_OK"); } else { Serial_SendString("ERROR"); OLED_ShowString(4,1," "); OLED_ShowString(4,1,"ERROR"); } Serial_RxFlag = 0; } }} |