STM32串口如何代码实现更稳定的接收消息
扫描二维码
随时随地手机看文章
在 《STM32串口向世界问好》介绍过如何发送消息,那么又如何接收消息呢?
也很简单,只需要配置好串口接收,配置好中断,并在串口中断函数里面进行数据接收就可以了。通用配置代码如下:
/***@brief初始化IO串口1*@parambound:波特率*@retvalNone*/voidUSART1_Debug_Init(u32bound){//GPIO端口设置GPIO_InitTypeDefGPIO_InitStructure;USART_InitTypeDefUSART_InitStructure;NVIC_InitTypeDefNVIC_InitStructure;assert_param(bound>0&&bound<=256000);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);USART_DeInit(USART1);//复位串口1//USART1_TXPA.9GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//PA.9GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA9//USART1_RXPA.10GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA10//USART初始化设置USART_InitStructure.USART_BaudRate=bound;USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits=USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity=USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//收发模式USART_Init(USART1,&USART_InitStructure);//初始化串口NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);USART_ClearFlag(USART1,USART_FLAG_TC);//防止第一个数据被覆盖USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启中断USART_Cmd(USART1,ENABLE);//使能串口}
中断处理接收函数为:
voidUSART1_IRQHandler(void){u8res;if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//接收中断有数据为1SET{res=(u8)USART_ReceiveData(USART1);res=res;}}
如果此时需要判断当接收的数据为1时点亮LED1,当接收数据为2时熄灭LED1则可在中断里作如下处理:
voidUSART1_IRQHandler(void){u8res;if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//接收中断有数据为1SET{res=(u8)USART_ReceiveData(USART1);if(0x01==res){LED1=ON;}if(0x02==res){LED1=OFF;}}}
但这种接收控制方法是不够稳定与灵活的,比如在传输的过程中受到干扰,0x01 变成 0x02,则就会出现错误的控制。又比如我要接收一串数据并进行处理,这样就不好控制了。
这时我们就要想着制定一套通信协议来方便通信。
在此介绍一种简单通信协议,是我在设计一款无人机数据链通信时用到的一开源协议:MAVLink,另外加上CRC校验,进一步保证接收数据的可靠性。
其通信数据格式如下:
红色部分代表起始帧 STX 为 0xFE ; LEN表示要发送的数据长度(PAYLOAD长度);SEQ表示数据的序列号,循环从0至255发送(可以检测是否丢包,并可能过此来判断信号强度);SYS是用来表示区分同一网络中不同飞行器号的,即系统ID;COMP代表组件ID,表示飞行器上各个组成部分,如飞控单元,GPS等;MSG则代表消息ID,即要发送不同控制命令ID;PAYLOAD表示此命令的内容;最后两字节是自动生的的CRC校验码 。
从上图也可以看出PAYLOAD有效长度可为0至255字节(因为LEN来表示,它们都是无符号8位数据类型),所以一条消息长度最小为8字节,最大为263字节。
至此一简单通信协议就介绍过了,说的有点多。下面就是如何对其解析,话不多说直接代码说明:
#defineMavlinkLenMin8#defineMavlinkLenMax263#defineSTX0xFE//MAVLINKHEAD#defineAdd_STX0x00#defineAdd_LEN0x01#defineAdd_SEQ0x02#defineAdd_SYS0x03#defineAdd_COMP0x04#defineAdd_MSG0x05#defineAdd_PAYLOAD0x06//PAYLOADstartfrom0x06typedefenum{BEEN_RECEIVED=0,RECEIVING=!BEEN_RECEIVED}Receive_Status;typedefstruct{booleanGet;u16Len;u8Cache[MavlinkLenMax];}MAVLink_Data_Struct,*MAVLink_Data_Struct_p;MAVLink_Data_StructMsg_Rev;voidMsg_Recv_Data_Analyse_Irq(u8data){if(RECEIVING==Msg_Rev.Get){Msg_Rev.Cache[Msg_Rev.Len++]=data;if(STX!=Msg_Rev.Cache[Add_STX]){Msg_Rev.Len=0;}if(((u16)Msg_Rev.Cache[Add_LEN]+MavlinkLenMin)==Msg_Rev.Len){Msg_Rev.Get=BEEN_RECEIVED;}}}
可在串口中断接收函数里调用此函数用作协议数据接收解析
voidUSART1_IRQHandler(void){u8res;if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//接收中断有数据为1SET{res=(u8)USART_ReceiveData(USART1);Msg_Recv_Data_Analyse_Irq(res);}}
当一条消息接收完成后,Msg_Rev.Get的状态就会被设置成BEEN_RECEIVED ,这时就可在相关函数中对此条消息进行处理。
另外为了消息的更可靠,还可加入CRC校验,如下函数就是一简单通用的CRC16校验码生成函数:
u16crc_chk_value(u8*data_value){u16crc_value=0xFFFF;u16length=(uint16_t)data_value[1]+6;u16i;while(length--){crc_value^=*data_value++;for(i=0;i<8;i++){if(crc_value&0x0001)crc_value=(crc_value>>1)^0xa001;elsecrc_value=crc_value>>1;}}return(crc_value);}