通信协议
通信协议在很多单片机的使用中都是挺重要的内容,我自己学的时候看到很多配置挺头晕的,本文将不从配置的角度谈通信协议的编写,本文将从整体的角度编写通信协议。
我以stm32c8t6为例子编写下面的代码
举例
首先对于通信协议我们直接理解为接收和发送数据的格式就行了。对于接收来说,像这种
数据格式
5A 0A为帧头,ff ff ff为帧尾,第3、4位数字为数据位,如5A 0A 01 00 ff ff ff,这其实就是一串高低电平用十六进制表示而已,用电脑发给单片机的时候用相关串口软件就行了,至于接收就是下面的内容了
首先呢单片机的接收大部分都是使用中断函数,就是配置好相关参数后,接收到信息后就会跳转到中断的函数,然后在中断函数中调用读取串口传来的信息,一般单片机的库函数都是读取一个字节即8位,如0x5A就是0101 1010
过程
中断接收完一个字节后,清空标志位就能接收下一位,即重新进入中断函数,有几位中断就能会进几次。这是整个代码接收的过程。
我写了一个实现的代码
void USART1_IRQHandler(void) //串口中断
{
static uint8_t Rx_State=0;
static uint8_t Rx_Pstart=0;
static uint8_t Rx_PMessage=0;
static uint8_t Rx_PEnd=0;
static uint8_t RX_Cnt=0;
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){//接收中断非空标志位
Recive_Data=USART_ReceiveData(USART1);
RX_Cnt++;
//搜索帧头
if(Rx_State==0){
if(Recive_Data==0x5A){
//RX_Cnt=0;
Rx_Pstart=1;
}
if(Recive_Data==0x0A&&Rx_Pstart==1){
Rx_PMessage=0;//准备接收数据
Rx_Pstart=0;//准备重新搜索帧头
Rx_State=1;
}
}
if(Rx_State==1 ){//这样可以使第二位也能是0x0A,直接不等于就行
if(Recive_Data!=0x0A && Rx_PMessage==0){
Serial_RecievePacket[Rx_PMessage]=Recive_Data;//问题所在
Rx_PMessage=1;
}
if( Rx_PMessage==1 && RX_Cnt==4){//第4位
Serial_RecievePacket[Rx_PMessage]=Recive_Data;
Rx_PEnd=0;//准备判断帧尾
Rx_State=2;
}
}
if(Rx_State==2){
if(Recive_Data==0xFF)Rx_PEnd++;
if(Rx_PEnd>=3){
Recive_Flag=1;
RX_Cnt=0;
Frame_times_Cut++;//Frame_times_Cut 的值为有效帧的帧数
Rx_State=0;
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
我用了一个RX_Cnt来表示代码进入的次数,方便整个代码思路的研究,实际上直接else if else if就可以了不用使用这样来判断到了第几位,因为中断会重复进入7次,因为上面我用7位字节当作一次完整的数据接收。
实际上大部分通信接收的过程都是这样,在配置好串口或者其他代码的时候,中断重复进入,清楚中断标志位后接收下一位数据。我上面的代码在最后还分辨了帧数的个数,你发两帧我还可以接收第二帧。
直接用if else其实代码可以非常简短,我为了表明中断会多次进入使用以上的编写方式
调用接收的数据
那么调用要的数据其实也简单,在代码后面我让一个Recive_Flag=1,在主循环中if之后清楚,中间就可以调用。
while(1){
if(Recive_Flag==1 &&Frame_times_Cut==1){//截取第一帧
USART_SendPackets(Serial_RecievePacket);//Receive;
Recive_Flag=0;
}
}
了解整体的过程后其实其他很多配置网上都能找到,甚至模块化的库都有,对于一些单片机比如stc32逐飞的库配置就非常方便,我们只需要在意通信代码的编写就行。
总结
接收的过程其实就是接收到一个字节,然后进入中断函数,清楚中断标志位后再次进入中断,直到把数据全部读完。以次为基础寻找配置的代码,其实就中间那段代码判断帧头帧尾的要自己写,串口接收的效果实现其实是非常简单的。