串口收发解析数据包
之前在正点原子的官方历程中我们就可以看到,在串口中断服务函数里面,对接受的数据用一个十六位的数据来判断是否接受完成(即是否在数据包的末尾接收到0x0D,0x0A,他们分别对应的是\r \n),利用高位处理状态,低位则将数据包数量储存下来,
u16 USART_RX_STA
那么既然可以判断是否接受到了数据,那么我们是不是可以将数据进行拆分处理,进行储存,然后对程序中的一些变量进行赋值,然后实现人机交互的功能,通过串口;
例如说,我们发送 X90 Y90 Z90那么就会对后面的数据进行接受处理;
在这里我们发送的数据包主要分为2中类型:
HEX数据包:
用于原始数据的接受,比如陀螺仪,以及温度传感器;
文本数据包:
常见的使用场景有CNC,以及3D打印使用的G代码;
然后在处理的时候,相信你也看到,需要用到状态机的知识;进行逐层判别,
例如正点原子用的是定义一个变量:
u16 USART_RX_STA
当然我们也可以通过将一个变量置0,1,2,3,,等等来表示不同的状态;
数据包为例的话:
你可能会有一个疑问就是,为什么HEX的包头包尾设置为0xFF与0xFE,这是因为,如果在数据包中存在一样的数值,可能会导致接受混乱的时候,这两个数据是影响比较小的,毕竟接受的数据不可能一直为很大的值;或者说出现为0xFF与0xFE的可能性比较小;通过这样的设置就极大程度的减小了数据包紊乱的问题;
针对文本数据包就比较好表示了,这了用到的是 @ 这个用的不是很多的字符作为包头,然后 \n 回车为包尾;
这里以文本数据包为例,简要讨论一下实现所需要的步骤:
1,串口的中断处理函数,在接受到数据中断以后,根据之前提到的状态机的知识判断数据的有效性;然后储存到char类型的数组中;
2,清空空格这种多余符号,使数据紧凑
3,数据包大体分为两大类:比如说,类似于这个"LED_ON"与"G 91"这么两种数据,处理起来肯定是不一样的;这里他们分为a,b两种类型:
? a:字符串类型的话,调用strcmp(A,B)//if ture return 0 把接受数据与事先定义好的字符串进行比较,然后类似于蓝牙的编程方式一样,进入相应的 if 语句,执行相关操作;
? b:CNC,G代码类型的:在数组中寻找特定字母,然后标记位置,最后取出该字母与下一个字母之间的数据;注意一点的是,这些数据是 char 类型的单个数字,需要转化为 int 类型以后乘以每一位相应的位权,得到数据;
字符串类型的数据包,控制LED的开关:
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\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_ON_OK");
}
else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
{
LED1_OFF();
Serial_SendString("LED_OFF_OK\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_OFF_OK");
}
else
{
Serial_SendString("ERROR_COMMAND\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "ERROR_COMMAND");
}
Serial_RxFlag = 0;
}
}
}
然后这里放几个我写过的函数,适用于G代码,和CNC代码解析;
串口中断处理函数:
void USART1_IRQHandler(void)
{
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 == '@' && Serial_RxFlag == 0)
{
RxState = 1;
pRxPacket = 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;
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
清除空格:
void clear_blank(char *a)
{
int i=0, j=0;
for (i = 0; i < strlen(a); i++) {
if (a[i] != ' ') {
a[j++] = a[i];
}
else if(a[i]==' '){
continue;
}
}
a[j] = '\0';
}
查找(搜索)特殊字符在数组中的位置:
int search_array( char *a, int n, char x )
{
int i,x_Tab_dowm;
int flag=-1;
x_Tab_dowm=x+32;
for(i=0;i<n;i++)
{
if(a[i]==x)
{
flag=1;
break;
}
else if(a[i]==x_Tab_dowm)
{
flag=1;
break;
}
}
if(flag==1)
return i;
else
return flag;
}
读取特定字符后面的数据,然后转换成 int 类型:
int get_array_number( char *a, int start , int place)
{
int mi = 0, di = 0;
int temp = 0, result = 0, real_place = 0, i = 0, j = 0;
for (i = 0; i < place; i++)
{
if ( ('0' <= a[start + i + 1]) && (a[start + i + 1] <= '9') ) { real_place++; }
if ( ('A' <= a[start + i + 1]) && (a[start + i + 1]<= 'z') ) { break; }
if (a[start + i + 1] == '\0') { break; }
}
for (j = 0; j < real_place; j++)
{
mi = pow(10, real_place - j - 1);
di = (a[start + j + 1] - '0');
temp = di * mi;
result = temp+result;
}
return result;
}
数据处理函数:
int x_place,y_place,z_place;
int x_num,y_num,z_num;
void Data_Handler(void)
{
if (Serial_RxFlag == 1)
{
clear_blank(Serial_RxPacket);
x_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'X');
y_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'Y');
z_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'Z');
x_num = get_array_number(Serial_RxPacket, x_place, Serial_RxPacket_Size);
y_num = get_array_number(Serial_RxPacket, y_place, Serial_RxPacket_Size);
z_num = get_array_number(Serial_RxPacket, z_place, Serial_RxPacket_Size);
Serial_RxFlag = 0;
}
}
主函数:
int main(void)
{
OLED_Init();
LED_Init();
Serial_Init();
OLED_ShowString(1, 1, "RxPacket");
while (1)
{
Data_Handler();
OLED_ShowString(2, 1, "X_num:");
OLED_ShowString(3, 1, "Y_num:");
OLED_ShowString(4, 1, "Z_num:");
OLED_ShowNum(2,7,x_num,5);
OLED_ShowNum(3,7,y_num,5);
OLED_ShowNum(4,7,z_num,5);
}
}
Serial.h:
Serial.c:
|