最近面试,收到一份面试题,特此研究下C的串口解析
题目要求如下:
- 用C语言写一个程序,此程序持续从串口读取数据(串口速率是115200,偶校验),串口数据有可能包含DL-T 645协议格式或者DL-T 698协议格式的数据帧(协议见word文档)。
要求把收到的符合以上两种格式的数据帧检出来。
附加要求:注意程序的可扩展性(如果以后此程序再支持其他协议数据解析,要容易扩展)。*
由于文档比较内容繁琐,特贴出来,协议格式
DL-T 645协议格式
(以下简称协议A)
DL-T 698协议格式
(以下简称协议B)
串口解析思路
- 一、串口数据接收:
定义一个1024字节的buf,将串口接收到的数据依次追加到此buf中;
- 二、解析串口数据流程:
1、从buf中检索起始字符0x68的位置,->2
2、去匹配是否符合协议A,会有三种解析结果
a.解析到完整的一帧数据,->5
b.数据未接收完 ->3
c.解析不满足规则 ->3
3、去匹配是否符合协议B,会有三种解析结果
a.解析到完整的一帧数据, ->5
b.数据未接收完 ->6
c.解析不满足规则
协议A也不满足规则 ->7
协议A未接收完 ->6
5、解析到完整的一帧数据,->10
6、协议匹配未接收完 ->9
7、两个协议解析都不满足,->8
8、从1中的位置继续寻找下一个0x68的位置
a.找到0x68 ->1
b.未找到0x68 ->6
9、继续循环,等待串口数据过来
10、解析完成,将buf中剩余数据前移到位置0,
代码如下:
先定义两种协议对应的结构体
typedef struct dl_t_698
{
uint8_t st_byte;//起始字符
uint16_t data_length;//长度域--此处注意大小端的问题
uint8_t control_byte;//控制域
uint8_t address_bytes[100];//地址域
uint16_t frame_head_hcs;//帧头校验
uint8_t user_data[100];//用户数据
uint16_t frame_crc_fcs;//帧校验
uint8_t end_byte;//结束字符
}dlt_698_frame_body;
typedef struct dl_t_645
{
uint8_t st_byte;//帧起始符
uint8_t address_bytes[5];//地址欲
uint8_t mid_st_byte;//帧起始符
uint8_t control_byte;//控制码
uint16_t data_length;//长度域--此处注意大小端的问题-- 前面字符长度为10
uint8_t user_data[100];//数据data
uint16_t frame_crc_cs;//校验
uint8_t end_byte;//结束字符
}dlt_645_frame_body;
//解析到一帧数据可能出现的情况
typedef enum frame_result
{
UNKNOWN,
OK, //成功找到一帧
UNFINISHED,//未接收完成
ERROR, //不满足此协议
} frame_result_t;
//定义协议类型
typedef enum protocol_type {
PROTOCOL_UNKNOWN,
PROTOCOL_DL_T_698,
PROTOCOL_DL_T_645,
PROTOCOL_OTHER,
}protocol_type_t;
char uart_rcvd_buf[UART_BUFFER_LEN];//接收串口发送过来的数据
char frame_buf[FRAME_BUFFER_LEN];//用来存取一帧数据
uint16_t uart_rcvd_pos = 0;//当前buf接收到的数据长度
dlt_698_frame_body s_dlt_698_frame_body;
dlt_645_frame_body s_dlt_645_frame_body;
/*
* 功能:接收串口过来的数据
**/
/*void uart_rev_data(uint8_t data)
{
uart_rcvd_buf[uart_rcvd_len] = data;
uart_rcvd_len++;
if (uart_rcvd_len >= UART_BUFFER_LEN) {
//清空所有命令
uart_rcvd_len = 0;
//my_memset(uart_rcvd_buf,0,sizeof(uart_rcvd_buf));
}
}*/
/*
* 该函数由库函数调用
**/
/*void UART_INT_Func(void)
{
uint8_t u8TempData=0;
if(1 == M0P_UART1->ISR_f.RI)
{
u8TempData = M0P_UART1->SBUF_f.SBUF;
uart_rev_data(u8TempData);
M0P_UART1->ICR_f.RICLR = 0;
}
}*/
/*
* 功能:检索一帧数据将值赋给结构体
* 校验OK return 1;
**/
uint8_t parse_dlt645_frame(char *p_frame, uint16_t frame_len, dlt_645_frame_body* sframe_body) {
uint16_t temp16_t = 0;//一帧数据的总长度
uint16_t i = 0;
//计算校验码
for (i = 0; i < frame_len - 3; i ++) {
temp16_t += p_frame[i];
}
if (temp16_t == p_frame[frame_len - 3] | p_frame[frame_len - 2]) {
sframe_body->st_byte = p_frame[0];
for (i = 0; i < 6; i ++) {
sframe_body->address_bytes[i] = p_frame[1+i];
}
sframe_body->mid_st_byte = p_frame[7];
sframe_body->control_byte = p_frame[8];
temp16_t = (p_frame[9]<<8) |p_frame[10];
sframe_body->data_length = temp16_t;
for (i = 0; i < temp16_t; i ++) {
sframe_body->user_data[i] = p_frame[11 + i];
}
sframe_body->frame_crc_cs = p_frame[frame_len - 3] | p_frame[frame_len - 2];
sframe_body->end_byte = p_frame[frame_len - 1];
return 1;
}
return 0;
}
/*
* 功能:检索一帧数据将值赋给结构体
**/
uint8_t parse_dlt698_frame(char *p_frame, uint16_t frame_len, dlt_698_frame_body* sframe_body) {
uint16_t temp16_t = 0;//一帧数据的总长度
uint16_t adr_temp16_t = 0;//地址域的地址长度
uint16_t i = 0;
//校验
for (i = 0; i < frame_len - 3; i ++) {
temp16_t += p_frame[i];
}
if (temp16_t == p_frame[frame_len - 3] | p_frame[frame_len - 2]) {
sframe_body->st_byte = p_frame[0];
temp16_t = ((p_frame[1]<<8) |p_frame[2]) & 0x3FFF;
sframe_body->data_length = temp16_t;
sframe_body->control_byte = p_frame[3];
sframe_body->address_bytes[0] = p_frame[4];//地址域第一个字节
adr_temp16_t = p_frame[4] & 0x0F;
for (i = 0; i < adr_temp16_t; i ++) {
sframe_body->address_bytes[i] = p_frame[5 + i];
}
sframe_body->frame_head_hcs = (p_frame[6 + adr_temp16_t - 1] >> 8) | p_frame[6 + adr_temp16_t];
for (i = 0; i < temp16_t; i ++) {
sframe_body->user_data[i] = p_frame[adr_temp16_t + 7];
}
sframe_body->frame_crc_fcs = p_frame[frame_len - 3] | p_frame[frame_len - 2];
sframe_body->end_byte = p_frame[frame_len - 1];
return 1;
}
return 0;
}
/*
* 功能:从缓存区buf中检索dlt645帧数据
* 将一帧数据读取到frame_buf中
* line:缓存区0x68开头的数据
* out:将捡出来的帧复制到该数组中
* frame_len:捡出来的帧的长度,
* line_len:缓存区buf中0x68开头的数据长度
**/
frame_result_t find_dlt645_frame(char* line, char* out, uint16_t* frame_len, uint16_t line_len) {
uint16_t frame_length = 0;//一帧数据的总长度
uint16_t temp_len = 0;
if (line_len < DLT_645_LEAST_LEN) {
return UNFINISHED;
}
//判断第七位
if (line[7] != 0x68) {
return ERROR;
}
frame_length = 9;/*帧起始符+地址域+帧起始符+控制域*/
temp_len = (line[9]<<8) |line[10];//数据data的长度
printf("645 data len = %d\n", temp_len);
frame_length = frame_length + 2 + temp_len;/*2-长度域占的字节*/
frame_length += 3;/*校验码和结束符*/
if (frame_length > FRAME_BUFFER_LEN) {
//超过单包缓存区的最大长度
return ERROR;
} else {
if (frame_length <= line_len) {
if (line[frame_length - 1] == 0x16) {
//检到一帧数据
for (temp_len = 0; temp_len < frame_length; temp_len ++) {
out[temp_len] = *line;
line++;
}
*frame_len = frame_length;
return OK;
} else {
//不满足此协议的0x16结束符
return ERROR;
}
} else {
//数据还没接收完整
return UNFINISHED;
}
}
return UNKNOWN;
}
/*
* 功能:从缓存区buf中检索dlt698帧数据
* 将一帧数据读取到frame_buf中
* line:缓存区0x68开头的数据
* out:将捡出来的帧复制到该数组中
* frame_len:捡出来的帧的长度,
* line_len:缓存区buf中0x68开头的数据长度
**/
frame_result_t find_dlt698_frame(char* line, char* out, uint16_t* frame_len, uint16_t line_len) {
uint16_t frame_length = 0;//一帧数据的总长度
uint16_t temp_len = 0;
if (line_len < DLT_698_LEAST_LEN) {
return UNFINISHED;
}
frame_length = 4;/*起始符+长度域+控制域*/
//地址域
temp_len = line[4] & 0x0F;
//printf("698 address len = %d\n", temp_len);
frame_length = frame_length + 1 + temp_len;
//帧头校验
frame_length += 2;
//用户数据长度
temp_len = ((line[1]<<8) |line[2]) & 0x3FFF;
//printf("698 data len = %d\n", temp_len);
frame_length += temp_len;//data长度
//
frame_length += 3;//帧校验+结束符
if (frame_length > FRAME_BUFFER_LEN) {
//超过单包缓存区的最大长度
return ERROR;
} else {
if (frame_length <= line_len) {
if (line[frame_length - 1] == 0x16) {
//检到一帧数据
for (temp_len = 0; temp_len < frame_length; temp_len ++) {
out[temp_len] = *line;
line++;
}
*frame_len = frame_length;
return OK;
} else {
//不满足此协议的0x16结束符
return ERROR;
}
} else {
//数据还没接收完整
return UNFINISHED;
}
}
return UNKNOWN;
}
/*
* 功能:协议数据解析
**/
void parse_buf (void)
{
uint16_t frame_length = 0;//一帧数据的总长度
uint16_t i = 0, temp_len = 0;
uint8_t has_content = 0;//buf中是否有数据
uint8_t frame_error = 0;//缓存区当前的数据对所有协议都不满足
char* p_buf;
protocol_type_t protl_type = PROTOCOL_UNKNOWN;
frame_result_t find_frame_re = UNKNOWN;
//用来保存每个协议解析后的结果
//frame_results[0] 保存PROTOCOL_DL_T_645协议解析结果
//frame_results[1] 保存PROTOCOL_DL_T_698协议解析结果
frame_result_t frame_results[2] = {UNKNOWN, UNKNOWN};
has_content = uart_rcvd_pos > 2;
while (has_content) {
p_buf = uart_rcvd_buf;
printf("p_buf = %#x\n", *p_buf);
//检索0x68开头的数据
while (*p_buf != 0x68 && p_buf < uart_rcvd_buf + uart_rcvd_pos) {
p_buf ++;
}
if (p_buf == uart_rcvd_buf + uart_rcvd_pos) {
//检索当前包数据,都不包含,清空
uart_rcvd_pos = 0;
break;
}
//uart_rcvd_buf中剩余的数据长度
temp_len = uart_rcvd_pos - (p_buf - uart_rcvd_buf);
printf("while start has_content uart_rcvd_pos - (p_buf - uart_rcvd_buf) = %d\n", temp_len);
//以下处理不包含校验
switch(protl_type) {
case PROTOCOL_UNKNOWN:
memset(frame_buf,0,sizeof(frame_buf));
find_frame_re = UNKNOWN;
frame_error = 0;
frame_length = 0;
for (i = 0; i < 3; i ++) {
frame_results[i] = UNKNOWN;
}
case PROTOCOL_DL_T_645:
find_frame_re = find_dlt645_frame(p_buf, frame_buf, &frame_length, temp_len);
frame_results[0] = find_frame_re;
if (find_frame_re == OK) {
printf("\nfind dlt_645 OK frame_buf = %s, frame_length = %d\n", frame_buf, frame_length);
printf("\n");
memset(&s_dlt_645_frame_body, 0, sizeof(dlt_645_frame_body));
if (parse_dlt645_frame(frame_buf, frame_length, &s_dlt_645_frame_body)) {
//解析到一包有效数据
}
break;
}
case PROTOCOL_DL_T_698:
find_frame_re = find_dlt698_frame(p_buf, frame_buf, &frame_length, temp_len);
frame_results[1] = find_frame_re;
if (find_frame_re == OK) {
printf("\nfind dlt_698 OK frame_buf = %s, frame_length = %d\n", frame_buf, frame_length);
printf("\n");
memset(&s_dlt_698_frame_body, 0, sizeof(dlt_698_frame_body));
break;
}
case PROTOCOL_OTHER:
//此处添加其他协议解析
//break;
default :
if (frame_results[0] == ERROR && frame_results[1] == ERROR) {
//缓存区的数据不满足现有协议的解析
//继续找下一个0x68起始符
p_buf ++;//跳过当前的0x68
//检索0x68开头的数据
while (*p_buf != 0x68 && p_buf < uart_rcvd_buf + uart_rcvd_pos) {
p_buf ++;
}
if (p_buf == uart_rcvd_buf + uart_rcvd_pos) {
//检索当前包数据,都不包含,清空
uart_rcvd_pos = 0;
break;
}
//找到下一条0x68开头的数据帧
frame_error = 1;
}
break;
}
//当成功检索到一帧数据或缓存区的数据不满足现有协议的解析
//buf中剩余的有效数据前移
if (find_frame_re == OK || frame_error) {
//uart_rcvd_buf剩余的数据长度
temp_len = uart_rcvd_pos - (p_buf - uart_rcvd_buf) - frame_length;
if (temp_len > 0) {
//当前uart_rcvd_buf中剩余的数据前移
for (i = 0; i < temp_len; i ++) {
uart_rcvd_buf[i] = *(p_buf + frame_length + i);
*(p_buf + frame_length + i) = 0x00;
}
has_content = 1;//继续循环解析
} else {
//解析过的位清空
for (i = 0; i < (p_buf - uart_rcvd_buf) + frame_length; i ++) {
uart_rcvd_buf[i] = 0x00;
}
has_content = 0;
}
uart_rcvd_pos = temp_len;
} else {
has_content = 0;
}
printf("while end has_content = %d, uart_rcvd_pos = %d\n", has_content, uart_rcvd_pos);
}
}
int main(void)
{
uint16_t timer;
//RCH 24MHz 使用内部时钟
/*Clk_SwitchTo(ClkRCL);
Clk_SetRCHFreq(ClkFreq24Mhz);
Clk_SwitchTo(ClkRCH);
//enable module clk
M0P_CLOCK->PERI_CLKEN_f.GPIO = 1; //打开GPIO的clk
M0P_CLOCK->PERI_CLKEN_f.BASETIM = 1;
M0P_CLOCK->PERI_CLKEN_f.UART1 = 1;
M0P_CLOCK->PERI_CLKEN_f.I2C = 1;
//UART init
Gpio_SetFunc_UART1TX_P23();
Gpio_SetFunc_UART1RX_P24();
M0P_UART1->SCON_f.DBAUD = 1; //双倍波特率
timer = 0x10000-((24000000*2)/(115200*32)); //单倍波特率,定时器配置
//使用basetimer1作为串口的波特率产生器
M0P_BT1->CR_f.GATE_P = 0u;
M0P_BT1->CR_f.GATE = 0u;
M0P_BT1->CR_f.PRS = 0u;
M0P_BT1->CR_f.TOG_EN = 0u;
M0P_BT1->CR_f.CT = 0u; //定时器模式
M0P_BT1->CR_f.MD = 1u; //重载模式
M0P_BT1->ARR_f.ARR = timer;
M0P_BT1->CNT_f.CNT = timer;
M0P_BT1->CR_f.TR = TRUE;
M0P_UART1->SCON_f.SM01 = 0x1; //模式1
M0P_UART1->SCON_f.SM2 = 0; //多主机通信disable
EnableNvic(UART1_IRQn, 3u, TRUE);
M0P_UART1->SCON_f.TIEN = 0;
M0P_UART1->SCON_f.RIEN = 1;
M0P_UART1->ICR_f.RICLR = 0;
M0P_UART1->ICR_f.TICLR = 0;
M0P_UART1->SCON_f.REN = 1;*/
**
“测试代码”;
**
char* p_temp;
uint8_t i = 0;
char dlt_645_frame_msg[38] = {0x06, 0x06,0x68, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x68,0x03,
0x00,0x04, 0x0D, 0x0D, 0x0D, 0x0D, 0x02, 0x0A, 0x16,
0x68, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x68,0x03,
0x00,0x04, 0x0D, 0x0D, 0x0D, 0x0D, 0x02, 0x0A, 0x16};
char dlt_698_frame_msg[34] = {0x06, 0x06,0x68, 0x00, 0x05, 0x0C, 0x01, 0x0A, 0x00, 0xCC,
0x0A, 0x0D, 0x0D, 0x0D, 0x0E, 0x0F, 0xCC, 0x16,
0x68, 0x00, 0x05, 0x0C, 0x01, 0x0A, 0x00, 0xCC,
0x0A, 0x0D, 0x0D, 0x0D, 0x0E, 0x0F, 0xCC, 0x16 };
char error_frame_msg[20] = {0x06, 0x06,0x68, 0x00, 0x05, 0x0C, 0x01, 0x0A, 0x00, 0xCC,
0x16, 0x06,0x68, 0x60, 0x05, 0x01, 0x01, 0x0A, 0x00, 0xCC};
char dlt_645_frame_msg_half_st[29] = {0x06, 0x06,0x68, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x68,0x03,
0x00,0x04, 0x0D, 0x0D, 0x0D, 0x0D, 0x02, 0x0A, 0x16,
0x68, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x68,0x03};
char dlt_645_frame_msg_half_end[9] = {0x00,0x04, 0x0D, 0x0D, 0x0D, 0x0D, 0x02, 0x0A, 0x16};
p_temp = uart_rcvd_buf;
//DLT_645 TEST
#if 0
printf("\n ****** dlt 645 start ******\n");
//stpcpy(uart_rcvd_buf, dlt_645_frame_msg);
//strcat(uart_rcvd_buf, end_byte);
memcpy(uart_rcvd_buf, dlt_645_frame_msg, sizeof(dlt_645_frame_msg));
uart_rcvd_pos = sizeof(dlt_645_frame_msg);
printf("main start 645 msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
parse_buf();
printf("\n ****** dlt 645 end ******\n");
#endif
//DLT_698 TEST
#if 0
printf("\n ****** dlt 698 start ******\n ");
memcpy(uart_rcvd_buf, dlt_698_frame_msg, sizeof(dlt_698_frame_msg));
uart_rcvd_pos = sizeof(dlt_698_frame_msg);
printf("main start 698 msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
parse_buf();
printf("\n ****** dlt 698 end ******\n ");
#endif
//ALL TEST
#if 0
printf("\n ****** dlt 698 and 645 start ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_698_frame_msg, sizeof(dlt_698_frame_msg));
uart_rcvd_pos += sizeof(dlt_698_frame_msg);
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_645_frame_msg, sizeof(dlt_645_frame_msg));
uart_rcvd_pos += sizeof(dlt_645_frame_msg);
printf("main start 698 and 645 msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
//parse_buf();
printf("\n ****** dlt 698 and 645 end ******\n ");
#endif
//Error msg TEST
#if 1
printf("\n ****** dlt error msg start ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, error_frame_msg, sizeof(error_frame_msg));
uart_rcvd_pos += sizeof(error_frame_msg);
printf("main start error msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
//parse_buf();
printf("\n ****** dlt error msg end ******\n ");
#endif
//Half test
#if 1
printf("\n ****** dlt 645 half start ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_645_frame_msg_half_st, sizeof(dlt_645_frame_msg_half_st));
uart_rcvd_pos += sizeof(dlt_645_frame_msg_half_st);
parse_buf();
printf("\n ****** dlt 645 half middle +++ ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_645_frame_msg_half_end, sizeof(dlt_645_frame_msg_half_end));
uart_rcvd_pos += sizeof(dlt_645_frame_msg_half_end);
printf("main start 645 half msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
printf("\n ****** dlt 645 half end ******\n ");
#endif
//ALL TEST
#if 1
printf("\n ****** dlt 698 and 645 start ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_645_frame_msg, sizeof(dlt_645_frame_msg));
uart_rcvd_pos += sizeof(dlt_645_frame_msg);
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_698_frame_msg, sizeof(dlt_698_frame_msg));
uart_rcvd_pos += sizeof(dlt_698_frame_msg);
memcpy(uart_rcvd_buf + uart_rcvd_pos, dlt_645_frame_msg, sizeof(dlt_645_frame_msg));
uart_rcvd_pos += sizeof(dlt_645_frame_msg);
printf("main start 698 and 645 msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
printf("\n ****** dlt 698 and 645 end ******\n ");
#endif
//Error msg TEST
#if 1
printf("\n ****** dlt error msg start ******\n ");
memcpy(uart_rcvd_buf + uart_rcvd_pos, error_frame_msg, sizeof(error_frame_msg));
uart_rcvd_pos += sizeof(error_frame_msg);
printf("main start error msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
//parse_buf();
printf("\n ****** dlt error msg end ******\n ");
#endif
parse_buf();
printf("main end msg uart_rcvd_buf = %s, uart_rcvd_pos = %d\n", uart_rcvd_buf, uart_rcvd_pos);
}