点击上方蓝字【囧囧妹】一起学习,一起成长!
b5 62 01 07 5c 00 3c 96 20 12 e6 07 09 1c 0c 1c1b 3f 70 bb 46 04 a6 16 cb 1d 00 00 e4 00 00 0000 00 00 00 00 00 00 00 00 00 98 bd ff ff ff ffff ff 00 0d c7 df 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 20 4e 00 00 80 a812 01 0f 27 00 00 0e e2 49 34 00 00 00 00 3e fe5a 00 dd 7b
00 00 84 05 00 00 f8 ff ff ff 08 00 00 00 e1 ffff ff 0c 00 00 00 6a 60 18 01 6c 00 00 00 80 a812 01 6e 00 00 00 0e e2 49 34 6a 60 18 01 43 fd3c 00 4b a1 b5 62 01 07 5c 00 2c a6 a8 20 e6 0700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 000a 08 08 0b 2d 3f 18 00 00 00 01 ff d2 1d 03 01ea 1c 62 de 4c 45 28 ff dd 17 7e 01 02 00 88 2402 00 b2 00 00 83 05 00 00 f6 ff ff ff 03 00 0000 e4 ff ff ff 0b 00 00 00 6a 60 18 01 67 00 0000 80 a8 12 01 6d 00 00 00 0e e2 49 34 6a 60 1801 43 fd 3c 00 86 50 b5 62 01 07 5c 00 20 a8 a8
完全杂乱无章,在分析后发现我接收到的数据有正确的数据也有莫名其妙的字节,也会有丢帧的现象,然后开始调试。
最开始我怀疑是丢包了,尽管波特率设置成115200且ubx每秒只会上传一帧100个字节理论绝不会丢包但我还是怀疑了丢包,首先我尝试在用户态做了kfifo用来缓存ubx数据,然后通过一个线程只读取ubx数据直接放到kfifo,在另一个线程从kfifo中取数据并对数据进行打印,发现在kfifo中读取到的还是不对,这时我感觉可能是gps芯片出来的数据就不对,我把gps的串口又接出来一路,通过u-center来监听数据,运行我的串口程序,发现u-center接收到的是对的,而我接收到的是乱的,如图(左侧u-center,右侧我接收数据)

这时我怀疑了内核驱动,通过设备树找到了串口驱动实现在drivers\tty\serial\8250文件夹,通过阅读8250_core.c没有发现什么问题,通过打印中断计数发现也很稳定,这时我把问题定位到串口驱动数据到tty框架以及线路规程可能出了问题,但是tty很成熟,把问题先定位到线路规程,然后重点开始看串口的属性设置,有问题代码如下:
/** @brief serial_set_attr* 设置串口* @param serial_dev[in] - gnss串口句柄* @param baudrate[in] - 波特率* @param data_bit[in] - 数据位* @param parity[in] - 校验位* @param stop_bit[in] - 停止位* @return 0-成功;其它错误码* @discri* @note*/static uint32_t serial_set_attr(void *serial_dev, uint32_t baudrate,uint32_t data_bit, char parity, uint32_t stop_bit){struct termios opt;serial_dev_st *pdev = nil;if(serial_dev == nil){return ERR_HANDLE;}pdev = (serial_dev_st *)serial_dev;//获取串口属性tcgetattr(pdev->fd, &opt);//设置波特率serial_set_baudrate(&opt, baudrate);opt.c_cflag |= CLOCAL | CREAD;//设置数据位serial_set_databit(&opt, data_bit);//设置校验serial_set_parity(&opt, parity);//设置停止位serial_set_stopbit(&opt, stop_bit);tcflush(pdev->fd, TCIFLUSH);//设置等待时间和最小接收字符opt.c_cc[VTIME] = 11;opt.c_cc[VMIN] = 0;//关闭回显opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH);//写入内核属性return (tcsetattr(pdev->fd, TCSANOW, &opt));}
在分析后发现关闭回显时清空了ICANON以及相关bit位,ICANON是做什么的呢?按着man手册的说法线路规程应该是有规范式和非规范式两种输出模式,设置ICANON则允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的缓冲,此时收到特殊字符会按着特殊字符在c_lflag中的定义对缓冲区进行处理,清除ICANON标志使用非规范式输出所有数据都当作标准数据进行输出。
按现象看如果置位了ICANON应该会出现我现在的现象,但是我在程序中清除了该位,然后在配置c_lflag前后加了打印看看该值,通过打印发现默认还配置了ISIG,查看man手册发现ISIG的含义是当接收到字符 INTR, QUIT, SUSP, 或 DSUSP 中的任意一个时,则产生相应的信号,以前确实也没注意过这个标志,然后我清除该标志再试发现读取两帧缓冲区的错误帧后结果正常了。

这也就是说ISIG标志会在收到特殊字符后也会对缓冲区进行处理,特殊字符的定义可参考网上的stty参数详解(http://www.ouvps.com/?p=547)c_lflag 本地标志总结如下,宏定义在内核源码include/uapi/asm-generic/termbits.h
/* c_lflag bits */#define ISIG 0000001 //当接收到字符 INTR, QUIT, SUSP, 或 DSUSP 任何一个时则产生相应的信号#define ICANON 0000002 //使能规范式模式。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的缓冲#define XCASE 0000004 //(不属于 POSIX; Linux 下不被支持) 如果同时设置了 ICANON,终端只有大写。输入被转换为小写,除了以 \ 前缀的字符。输出时,大写字符被前缀 \,小写字符被转换成大写#define ECHO 0000010 //回显输入字符#define ECHOE 0000020 //如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词#define ECHOK 0000040 //如果同时设置了 ICANON,字符 KILL 删除当前行#define ECHONL 0000100 //如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO#define NOFLSH 0000200 //禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列#define TOSTOP 0000400 //向试图写控制终端的后台进程组发送 SIGTTOU 信号#define ECHOCTL 0001000 //(不属于 POSIX) 如果同时设置了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大 0x40 的 ASCII 码。例如,字符 0x08 (BS) 被回显为 ^H#define ECHOPRT 0002000 //(不属于 POSIX) 如果同时设置了 ICANON 和 IECHO,字符在删除的同时被打印#define ECHOKE 0004000 // (不属于 POSIX) 如果同时设置了 ICANON,回显 KILL 时将删除一行中的每个字符,如同指定了 ECHOE 和 ECHOPRT 一样#define FLUSHO 0010000 //(不属于 POSIX; Linux 下不被支持) 输出被刷新。这个标志可以通过键入字符 DISCARD 来开关#define PENDIN 0040000 //(不属于 POSIX; Linux 下不被支持)当读取下一个字符时,输入队列中的所有字符都将被重新打印。#define IEXTEN 0100000 //启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效#define EXTPROC 0200000




