stm32-串口数据包
数据包:将多个数据打包,以便进行多个字节的通信。还要对这多个数据进行分割,防止连续发送数据包的时候接收数据错位。
串口数据包常用分割方式是添加包头包尾。



如图,0xFF、@就是包头,接收到它们就知道后面是一个数据包了;0xFE、\r\n换行就是包尾,接收到说明数据结束了。包头包尾可以自己设置。
包头包尾和载荷数据重复问题
1. 可以限制载荷数据的范围,从几到几,而包头包尾在这个范围外,就能避免重复导致误判。
2. 如果不能限制范围就使用固定包长,这样就能分辨哪个地方开始是数据。
3. 还可以增加包头包尾的数量,就能降低重复率。
可以只要包头不要包尾,这样也能接收,但重复概率更大。
如果确定数据不会和包头包尾重复,那么可以选择可变包长;如果有重复几率,可变包长更容易导致误判,最后选择固定包长。
HEX数据包容易遇到重复问题,因为是以原始数据显示的;但文本数据包是以字符形式显示,可以使用大量字符,遇到重复的概率很小,并且字符表达意思很直观。
HEX数据包优点是传输直接,数据解析也简单,适合模块发送原始数据,如陀螺仪,传感器;缺点是缺乏灵活性,数据容易和包头包尾重复。
文本数据包优点是数据直观容易理解,具有灵活性,适合人机交互的场合;缺点是数据解析麻烦。
数据包发送
发送比较简单,如果是HEX数据包,就定义一个数组,写入包头数据包尾,然后通过串口发送数组函数发送;如果是文本数据包,就把包头数据包尾写入一个字符串中,通过串口发送字符串函数发送。
数据包接收(状态机)

在不同状态下做不同工作,同时实现状态转移,就是状态机。
例如接收固定包长HEX数据包:在状态S=0下接收包头0xFF,如果在中断里接收到数据是0xFF则跳转到状态S=1,然后退出中断;
下一次接收到数据进入中断,是状态S=1就说明是数据,将数据存入数组,然后退出中断,同时应该有一个变量来判断固定包长的数据收够没有,如果收够了就跳转到状态S=2,退出中断;
下一次进入中断时是S=2就可以接收包尾了,收到包尾0xFE就跳转到状态S=0,退出中断。
当不满足状态跳转条件时状态不改变。

又比如接收可变包长文本数据包:状态0到状态1都差不多,不同的是由于包长可变,所以在状态1下除了接收数据还要检测包尾,如果接收到包尾\r就跳转到状态2,去接收包尾\n,\n后跳转状态0。因为这里包尾有两个,如果只有一个就可以直接跳转回状态0
收发HEX数据包代码
串口.c文件
uint8_t serial_TXpacket[4];//存发送的数据包
uint8_t serial_RXpacket[4];//存接收的数据包
uint8_t RXflag=0;//接收数据标志位
void serial_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//USART1的TX是PA9,RXPA10
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使用USART1
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
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;//上拉输入,或者浮空输入,因为空闲时默认高电平
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//初始化USART
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);
//配置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
USART_Cmd(USART1,ENABLE);
}
//发送数据包
void serial_sendpacket(void)
{
serial_send(0xFF);
serial_array(serial_TXpacket,4);
serial_send(0xFE);
}
uint8_t serial_getRXflag(void)//返回值 标志位
{
if(RXflag==1)
{
RXflag=0;
return 1;
}
return 0;
}
void USART1_IRQHandler(void)//中断函数中使用状态机
{
static uint8_t RXstate=0;
static uint8_t count=0;
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
{
uint8_t RXDATA=USART_ReceiveData(USART1);
if(RXstate==0)
{
if(RXDATA==0xFF)
{
RXstate=1;
count=0;
}
}
else if(RXstate==1)
{
serial_RXpacket[count]=RXDATA;
count++;
if(count>=4)
{
RXstate=2;
}
}
else if(RXstate==2)
{
if(RXDATA==0xFE)
{
RXstate=0;
RXflag=1;
}
}
}
}
串口.h文件
#ifndef __SERIAL_H_
#define __SERIAL_H_
void serial_init(void);
void serial_send(uint8_t byte);
void serial_array(uint8_t *array,uint16_t length);
void serial_string(uint8_t *string);
void serial_number(uint32_t num,uint16_t length);
uint8_t serial_receive(void);
void serial_sendpacket(void);
extern uint8_t serial_TXpacket[4];
uint8_t serial_getRXflag(void);
extern uint8_t serial_RXpacket[4];
#endif
主函数
uint8_t keynum;
int main()
{
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)
{
if(key_num()==1)
{
serial_TXpacket[0]++;
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);//按一下按键,数据包里的数据就++并发送数据包,同时显示在OLED
}
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);
}
}
}
收发文本数据包代码
发数据包用发字符串的函数就可以了。
串口.c文件
char serial_RXpacket[100];//存接收的数据包
uint8_t RXflag=0;//接收数据标志位
void serial_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//USART1的TX是PA9,RXPA10
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使用USART1
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
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;//上拉输入,或者浮空输入,因为空闲时默认高电平
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//初始化USART
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);
//配置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
USART_Cmd(USART1,ENABLE);
}
uint8_t serial_getRXflag(void)//返回值标志位
{
if(RXflag==1)
{
RXflag=0;
return 1;
}
return 0;
}
void USART1_IRQHandler(void)//中断函数中使用状态机
{
static uint8_t RXstate=0;
static uint8_t count=0;
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
{
uint8_t RXDATA=USART_ReceiveData(USART1);
if(RXstate==0)
{
if(RXDATA=='@')//包头
{
RXstate=1;
count=0;
}
}
else if(RXstate==1)
{
if(RXDATA=='\r')//包尾
{
RXstate=2;
}
else{
serial_RXpacket[count]=RXDATA;
count++;
}
}
else if(RXstate==2)
{
if(RXDATA=='\n')//包尾
{
RXstate=0;
RXflag=1;
serial_RXpacket[count]='\0';
}
}
}
}
串口.h文件
#ifndef __SERIAL_H_
#define __SERIAL_H_
void serial_init(void);
void serial_send(uint8_t byte);
void serial_array(uint8_t *array,uint16_t length);
void serial_string(uint8_t *string);
void serial_number(uint32_t num,uint16_t length);
uint8_t serial_receive(void);
uint8_t serial_getRXflag(void);
extern char serial_RXpacket[100];
#endif
主函数
int main()
{
OLED_Init();
serial_init();
led_init();
OLED_ShowString(1,1,"TXpacket:");
OLED_ShowString(3,1,"RXpacket:");
while(1)
{
if(serial_getRXflag()==1)
{
OLED_ShowString(4,1," ");
OLED_ShowString(4,1,serial_RXpacket);
if(strcmp(serial_RXpacket,"led on")==0)//接收到led on就开灯
{
led1_on();
}
else if(strcmp(serial_RXpacket,"led off")==0)//接收到led off就关灯
{
led1_off();
}
else
{
serial_string("error command");//接收到其他指令就显示错误并发送
OLED_ShowString(2,1,"error command");
}
}
}
}
更多推荐

所有评论(0)