概述
前面的文章
下面先自定义将串口协议定出来,按照这个协议发送的数据格式进行解析,因为串口数据快,多帧的数据就混到一起,我们要把多帧的数据按照协议提出解析出来,因此就有这篇文章进行数据解析,我的注释写的很详细,串口的接收是一个循环的fifo,也就是每个字节接收到之后从0开始存到缓存的最后,存满之后再从缓存的0开始存,是一个循环的结构
14字节设置命令'S'协议基本格式 | |||
字节序号 | Hex内容 | 说明 | 备注 |
0 | 0x55 | 固定桢头 | 固定桢头 |
1 | 0xAA | 固定桢头 | |
2 | 0x0E | 表示字节长度 | 固定为0x0E,表示14字节 |
3 | 0x53 | 大写字符'S' | 0x53 = 字符'S'(表示set) |
4 | 0xmn | u8ModuleType | 模块类型码,详见模块类型列表,最多255种模块类型 |
5 | (m<<6)|n | (u8PageProperty<<6)|u8EQBandIndex | 因为属性页面只有0x01或者0x02,所以只用2bit来表示属性,而EQ的段属性从0开始,占用6个bit位,所以最大可以表示0b111111 = 63(段) m表示页面属性占用高2位,n表示段属性占用低6位, |
6 | 0x00~0x04(最多4个参数) | u8ModuleItem | 模块的第几个参数0x00~0xmn(模块参数顶多6个) |
7 | 0x00~0x07,从0x00开始 | u8StartCh开始通道号 | 如果开始通道号与结束通道号相等,则表明只设置该一个通道号,且结束通道号需要>=开始通道号,否则不执行 |
8 | 0x00~0x07,从0x00开始 | u8EndCh结束通道号 | |
9 | ParamAccess | 一个float或者int类型的设置参数 | 用ParamAccess方法转换,用这种方式表示浮点数据占4字节 |
10 | |||
11 | |||
12 | |||
13 | CRC | 校验位字节 | 最后一个字节做"和校验",将前13个字节相加后取低八位 |
下面插入解析代码,可能有些宏定义没有贴出来,我定义的2种协议是14和56字节,也就是上位机发送和传输要么是14字节或者56字节两种情况进行解析
/*********************************************************************************************************
** 处理和解析显示串口数据,处理原则是快收慢发
** 设备和上位机之间传输要么是14字节,要么是56字节
** Uart0RxDataProcess(void)
** 时间计算 56*16*10/115200 = 77.77778ms约78ms缓存
** 上位机发送不能78ms内发送太多字节
*********************************************************************************************************/
void Uart0RxDataProcess(void)
{
int i = 0;
//串口数据处理,g_u8Uart0RxDataMem是运行到这函数里面时接收到数据的总缓存
unsigned char *pUart0RxDataMem = (unsigned char * )g_u8Uart0RxDataMem;
unsigned char u8UartParaBuf[UART0_FRAME_MAX_LEN] = {0}; //临时变量,每次处理一帧数据
unsigned char u8DataLen = 0;
unsigned short u16TempRxStartIndex = 0;
if(pUart0RxDataMem == NULL)
{
return ;
}
//开始解析数据,gUart0RxByteNum是表示的接收到的字节个数
u8DataLen = 0;
while(gUart0RxByteNum >= 3) //如果接收到的数据长度大于3个字节,就可以进行帧头0x55AA的判断
{
//用桢头完整字节判断,一个字节容易出错
if((pUart0RxDataMem[gUart0RxStartIndex] == 0x55) &&
(pUart0RxDataMem[(gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN] == 0xAA))
{
u8DataLen = pUart0RxDataMem[((gUart0RxStartIndex + 2) % UART0_RX_BUFF_LEN)];//第3个字节时本帧的长度,取出来判断一下是不是14或者56
if((UART_RX_PROTOCOL_SIZE == u8DataLen) ||
(UART_RX_ExPROTOCOL_SIZE == u8DataLen) ) //检测到指定的协议长度就结束循环
{
break;
}
}
//如果帧长不等于14或者56,那么把帧头的第一个字节置0,去掉一个字节,再循环判断,直到检测到0x55AA就跳出循环
pUart0RxDataMem[gUart0RxStartIndex % UART0_RX_BUFF_LEN] = 0;
gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN; //gUart0RxStartIndex的大小始终都在0~UART0_RX_BUFF_LEN之间,防止溢出
gUart0RxByteNum--; //接收到的总字节数减掉1
gUart0RxByteNum = ( gUart0RxByteNum <= 0) ? 0 : gUart0RxByteNum; //防接收负数处理
}
//执行到这里,表示检测到了0x55AA
if((gUart0RxByteNum >= u8DataLen) && (u8DataLen > 0)) //如果接收的字节数大于协议中的帧长
{
//先把数据取出(gUart0RxStartIndex读取的偏置先不变)做校验
u16TempRxStartIndex = gUart0RxStartIndex;
for(i = 0; i < u8DataLen; i++)
{
u8UartParaBuf[i] = pUart0RxDataMem[u16TempRxStartIndex];//校验通过将数据取出存放到u8UartParaBuf
u16TempRxStartIndex = (u16TempRxStartIndex + 1) % UART0_RX_BUFF_LEN;
}
if(CheckReciveDataCRC(u8UartParaBuf, u8DataLen))//如果校验正确即可从原大缓存中清除对应的字节数后按照协议提取数据
{
for(i = 0; i < u8DataLen; i++) //清除取出的数据
{
pUart0RxDataMem[gUart0RxStartIndex] = 0;
gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;
gUart0RxByteNum--;
gUart0RxByteNum = ( gUart0RxByteNum <= 0) ? 0 : gUart0RxByteNum; //防接收负数处理
}
u8DataLen = ProcessingProtocolData(u8UartParaBuf, u8DataLen);
UART0_SendBuf(u8UartParaBuf, u8DataLen); //返回发送最大消耗时间为56*10/115200 = 4.86ms
}
else
{
//如果校验错误,前面的数据已经被清除为0,因此这桢数据就丢弃
pUart0RxDataMem[gUart0RxStartIndex] = 0; //只需清除一个字节让while循环去清除其他数据
gUart0RxStartIndex = (gUart0RxStartIndex + 1) % UART0_RX_BUFF_LEN;
gUart0RxByteNum--;
gUart0RxByteNum = ( gUart0RxByteNum <= 0) ? 0 : gUart0RxByteNum; //防接收负数处理
}
}
}
最后
以上就是昏睡茉莉为你收集整理的高效的串口循环Buffer接收处理思路及代码2的全部内容,希望文章能够帮你解决高效的串口循环Buffer接收处理思路及代码2所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复