程序员探案之被吃掉的串口数据


2018电影春节档,唐人街探案居然拔得头筹,原因我分析了下,个人觉得 多数人都有一颗了解真相的心吧

程序员的世界里,总是充满着悬念,而带来悬念的就是挥之不去的bug 修复bug,就像侦办一起案件

我就蹭蹭热度 来个 程序员探案 系列吧


直击"案发"现场

前两天做嵌入式开发的一哥们在用ARM和一串口设备进行通信时, 碰到了诡异的问题,受尽折磨的他告诉我:

数据被"吃掉"了,还有人"调包"

"案情"分析

通过大量分析发送和接收的数据对比,看出了些端倪

数据被"吃掉"

程序在接收数据时 0x13,0x11总是收不到

数据被"调包"

串口发送方发0x0D,接收方收到0x0A 串口发送方发0x0A,接收方收到0x0D

找证据

从termios结构中找到有几个关键位设置对其有影响 c_iflag 中的INLCR,ICRNL,IXON,IXOFF,IXANY(具体含义参见下面表格宏说明)

c_iflag用于设置如何处理串口上接收到的数据,包含如下内容:

英文说明

中文说明

INPCK

Enable parity check

允许输入奇偶校验

IGNPAR

Ignore parity errors

忽略奇偶校验错误

PARMRK

Mark parity errors

标识奇偶校验错误

ISTRIP

Strip parity bits

去除字符的第8个比特

IXON

Enable software flow control (outgoing)

允许输出时对XON/XOFF流进行控制

IXOFF

Enable software flow control (incoming)

允许输入时对XON/XOFF流进行控制

IXANY

Allow any character to start flow again

输入任何字符将重启停止的输出

IGNBRK

Ignore break condition

忽略BREAK键输入

BRKINT

Send a SIGINT when a break condition is detected

如果设置了IGNBRK,BREAK键输入将被忽略

INLCR

Map NL to CR

将输入的NL换行转换成CR回车

IGNCR

Ignore CR

忽略输入的回车

ICRNL

Map CR to NL

将输入的回车转化成换行(如果IGNCR未设置的情况下)

IUCLC

Map uppercase to lowercase

将输入的大写字符转换成小写字符(非POSIX)

IMAXBEL

Echo BEL on input line too long

当输入队列满的时候开始响铃

c_oflag用于设置如何处理输出数据,包含如下内容:

英文说明

中文说明

OPOST

Postprocess output (not set = raw output)

是否处理(原始数据)

OLCUC

Map lowercase to uppercase

将输出的小写字符转换成大写字符(非POSIX)

ONLCR

Map NL to CR-NL

将输出的NL(换行)转换成CR(回车)及NL(换行)

OCRNL

Map CR to NL

将输出的CR(回车)转换成NL(换行)

NOCR

No CR output at column 0

第一行不输出回车符

ONLRET

NL performs CR function

不输出回车符

OFILL

Use fill characters for delay

发送填充字符以延迟终端输出

OFDEL

Fill character is DEL

以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL

NLDLY

Mask for delay time needed between lines

换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)

CRDLY

Mask for delay time needed to return carriage to left column

回车延迟,取值范围为:CR0、CR1、CR2和 CR3

TABDLY

Mask for delay time needed after TABs

水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3

BSDLY

Mask for delay time needed after BSs

空格输出延迟,可以取BS0或BS1

VTDLY

Mask for delay time needed after VTs

垂直制表符输出延迟,可以取VT0或VT1

FFDLY

Mask for delay time needed after FFs

换页延迟,可以取FF0或FF1

c_lflag用于设置本地模式,控制终端编辑功能,决定串口驱动如何处理输入字符,设置内容如下:

英文说明

中文说明

ISIG

Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals

当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号

ICANON

Enable canonical input (else raw)

使用标准输入模式

XCASE

Map uppercase \lowercase (obsolete)

在ICANON和XCASE同时设置的情况下,终端只使用大写

ECHO

Enable echoing of input characters

显示输入字符

ECHOE

Echo erase character as BS-SP-BS

如果ICANON同时设置,ERASE将删除输入的字符

ECHOK

Echo NL after kill character

如果ICANON同时设置,KILL将删除当前行

ECHONL

Echo NL

如果ICANON同时设置,即使ECHO没有设置依然显示换行符

ECHOPRT

Echo erased character as character erased

如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)

TOSTOP

Send SIGTTOU for background output

向后台输出发送SIGTTOU信号

破案实战总结

结合上面的宏定义说明,对应修改配置如下,问题解决:

1opt.c_iflag &= ~(ICRNL | INLCR);
2opt.c_iflag &= ~(IXON | IXOFF | IXANY);
3opt.c_oflag &= ~(ONLCR | OCRNL); 

水落石出,最后给出我的串口配置实战

  1/**
  2*打开串口
  3*/
  4int open_dev(char *dev_name)
  5{
  6    int fd = open(dev_name, O_RDWR | O_NOCTTY);
  7
  8    if (-1 == fd)
  9    {
 10        perror("Can't open serial port");
 11        return -1;
 12    }
 13    else
 14        return fd;
 15
 16}
 17
 18
 19/***设置串口通信速率和模式标识 
 20*@param  fd     类型 int  打开串口的文件句柄 
 21*@param  speed  类型 int  串口速度 
 22*@return  void*/
 23void set_speed(int fd, int speed)
 24{
 25    int i;
 26    int status;
 27    int speed_arr[] = {B921600, B460800, B230400, B115200, B57600,
 28                       B38400, B19200, B9600, B4800, B2400, B1200,
 29                       B300, B38400, B19200, B9600, B4800, B2400,
 30                       B1200, B300, };
 31    int name_arr[] = {921600, 460800, 230400, 115200, 57600, 38400,
 32                      19200, 9600, 4800, 2400, 1200, 300, 38400, 
 33                      19200,  9600, 4800, 2400, 1200, 300, };
 34    struct termios opt;
 35    tcgetattr(fd, &opt);
 36    opt.c_iflag &= ~ (INLCR | ICRNL | IGNCR);
 37    opt.c_oflag &= ~(ONLCR | OCRNL);
 38    opt.c_iflag &= ~(IXON);
 39    for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
 40    {
 41        if (speed == name_arr[i])
 42        {
 43            tcflush(fd, TCIOFLUSH);
 44            cfsetispeed(&opt, speed_arr[i]);
 45            cfsetospeed(&opt, speed_arr[i]);
 46            status = tcsetattr(fd, TCSANOW, &opt);
 47            if (status != 0)
 48                perror("tcsetattr fd");
 49            return;
 50        }
 51        tcflush(fd,TCIOFLUSH);
 52    }
 53}
 54
 55
 56/**
 57*设置串口数据位,停止位和效验位
 58*@param fd 类型 int 打开的串口文件句柄*
 59*@param databits 类型 int 数据位 取值 为 7 或者8*
 60*@param stopbits 类型 int 停止位 取值为 1 或者2*
 61*@param parity 类型 int 效验类型 取值为N,E,O,,S
 62*/
 63int set_Parity(int fd,int databits,int stopbits,int parity)
 64{
 65    struct termios options;
 66    if ( tcgetattr( fd,&options) != 0)
 67    {
 68        perror("tcgetattr error");
 69        return -5;
 70    }
 71    options.c_cflag &= ~CSIZE;
 72    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
 73    options.c_oflag &= ~OPOST;
 74    switch (databits) /*设置数据位数*/
 75    {
 76    case 7:
 77        options.c_cflag |= CS7;
 78        break;
 79    case 8:
 80        options.c_cflag |= CS8;
 81        break;
 82    default:
 83        fprintf(stderr,"unsupported data size\n");
 84        return -4;
 85    }
 86    switch (parity)
 87    {
 88    case 'n':
 89    case 'N':
 90        options.c_cflag &= ~PARENB; /* Clear parity enable */
 91        options.c_iflag &= ~INPCK; /* Enable parity checking */
 92        break;
 93    case 'o':
 94    case 'O':
 95        options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
 96        options.c_iflag |= INPCK; /* Disnable parity checking */
 97        break;
 98    case 'e':
 99    case 'E':
100        options.c_cflag |= PARENB; /* Enable parity */
101        options.c_cflag &= ~PARODD; /* 转换为偶效验*/
102        options.c_iflag |= INPCK; /* Disnable parity checking */
103        break;
104    case 'S':
105    case 's': /*as no parity*/
106        options.c_cflag &= ~PARENB;
107        options.c_cflag &= ~CSTOPB;
108        break;
109    default:
110        fprintf(stderr,"unsupported parity\n");
111        return -3;
112    }
113    /* 设置停止位*/
114    switch (stopbits)
115    {
116    case 1:
117        options.c_cflag &= ~CSTOPB;
118        break;
119    case 2:
120        options.c_cflag |= CSTOPB;
121        break;
122    default:
123        fprintf(stderr,"unsupported stop bits\n");
124        return -2;
125    }
126    /* Set input parity option */
127    if (parity != 'n')
128        options.c_iflag |= INPCK;
129    options.c_cc[VTIME] = 0; // 15 seconds
130
131    options.c_cc[VMIN] = 0;
132
133    tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
134    if (tcsetattr(fd,TCSANOW,&options) != 0)
135    {
136        perror("setup serial options error");
137        return -1;
138    }
139    return 0;
140}

原文发布于微信公众号 - chafezhou(gh_5b8f0c59b682)

原文发表时间:2018-03-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏非著名程序员

仿苹果数字键盘以及判断信用卡有效期的Editext

这次带来一个小小的信用卡有效期规则的Editext,额外赠送内置数字键盘的开发 首先来看下需求: 1) 月份数字: λ 数字输入0:后一位数字可输入...

2135
来自专栏用户2442861的专栏

Emmet for Dreamweaver:HTML/CSS代码快速编写神器

Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择器的语法来生成代码,大大提高了HTML...

1682
来自专栏我的小碗汤

一个神秘现象引发对beego框架的思考

小强最近在项目中遇到了一个很奇怪的问题:在整改日志规范时,为了避免影响现有的代码结构以及改动尽可能小的前提下,在调用记日志的SDK处将某一个字段值首字母改为大写...

724
来自专栏zhangdd.com

ceph性能测试

该工具的语法为:rados bench -p <pool_name> <seconds> <write|seq|rand> -b <block size> -t...

1872
来自专栏.NET后端开发

Highcharts使用指南

摘要 Highcharts图表控件是目前使用最为广泛的图表控件。本文将从零开始逐步为你介绍Highcharts图表控件。通过本文,你将学会如何配置Highcha...

2995
来自专栏北京马哥教育

python实现简单爬虫功能

iOS开发如果之前没接触过除了c和c++(c++太难了,不花个十来年基本不可能精通)的语言,第二门语言最好的选择就是Python.原因就是 1.语法简单 2.库...

3117
来自专栏更流畅、简洁的软件开发方式

见到了“公司”定义一个Company类,那么见到了“字段”是不是也可定义一个Column类?

  既然见到了公司,我们可以定义一个Class Company ,那么我们见到了字段,是不是也可以定义一个Class ColumnInfo呢? 公司的描述信息类...

2449
来自专栏君赏技术博客

Jekyll-Admin-Mac-列表

接下来我们需要就是做出这个列表数据,我们可以使用 NSTableView来做出这个效果。

1471
来自专栏Spring相关

Springboot用官方建议访问Html页面并接传值

我们以前通常习惯用webapp来放置jsp页面,但是到了Springboot中,官方建议用Static文件夹来存放及静态的资源,

2594
来自专栏为数不多的Android技巧

Android Studio你不知道的快捷键(二)

在Android Studio你不知道的快捷键(一)里面,主要讲述了一些窗口操作的快捷键还有补全参数提示等,这一篇会分享一些代码代码编辑的快捷键。(默认Keym...

1082

扫码关注云+社区