【linux】系统调用版串口分析&源码实战
前言
- 目前不涉及驱动源码
参考
1. 实战分析
1.1 开发步骤
- 获取串口设备路径
- 打开设备文件
- 配置串口
- 对该设备文件进行读写,相当于对该串口设备进行读写,即通信
- 关闭设备文件
以下代码段默认从 附件-最终串口测试源码 中摘取
1.1.1 获取串口设备路径
- 使用数组或者宏定义在相关文件前面定义默认串口路径,方便修改,源码段如下:
/* 不同的设备,不同的路径 */
const char def_uart_path[] = "/dev/ttymxc2" // 默认串口路径(备用)
- 串口路径优先从传入参数中获取,如果参数中没有传入,便使用
def_uart_path
默认路径
/* 终端设备选择 */
if(argc > 1)
path = argv[1];
else
path = (char *)def_uart_path;
1.1.2 打开设备文件
- 获取设备句柄,如果获取失败,便结束
/* 打开终端 */
fd = open(path, O_RDWR);
if(fd < 0){
printf("[%s] open err!", path);
return 0;
}
1.1.3 配置串口
- 定义一个结构体 termios 用于获取、设置终端设备的参数
- 包括波特率、数据位数、校验位等
termios 结构体
- 成员值作用,推荐先看官方手册,看不懂再看本笔记中文表格
- termios 结构体定义在编译链接工具的头文件默认路径中的bits文件夹中
- 如如下源码来自 /usr/arm-linux-gnueabihf/include/bits/termios.h
成员 | 说明 |
---|---|
c_iflag | 输入模式标志 |
c_oflag | 输出模式标志 |
c_cflag | 控制模式标志 |
c_lflag | 本地模式标志 |
c_line | 行控制 |
c_cc[NCCS] | 控制字符 |
c_ispeed | 输入波特率 |
c_ospeed | 输出波特率 |
struct termios
{
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
};
1. c_iflag 输入模式标志
- 用于控制如何对串口输入的字符进行处理
选项值 | 说明 |
---|---|
IGNBRK | 忽略输入中的 BREAK 状态。 (忽略命令行中的中断) |
BRKINT | (命令行出 现中断时,可产生一插断)如果设置了IGNBRK,中断条件被忽略。如果没有设置IGNBRK而设置了BRKINT,中断条件清空输入输出队列中所有的数据并且向tty的前 台进程组中所有进程发送一个SIGINT信号。如果这两个都没有设置,中断条件会被看作一个0字符。这时,如果设置了PARMRK,当检测到一个帧误差时 将会向应用程序发送三个字节'/377''/0''/0',而不是只发送一个'/0' |
IGNPAR | 忽略桢错误和奇偶校验错 |
PARMRK | 如果设定了IGNPAR,则忽略接收到的数据的奇偶检验错误或帧错误(除了前面提到的中断条件)。如果没有设置IGNPAR而设置了PARMRK, 当接收到的字节存在奇偶检验错误或帧错误的时候。将向应用程序发送一个三字节的'/377''/0''/n'错误报告。其中n表示所接收到的字节。如果两 者都没有设置,除了接收到的字节存在奇偶检验错误或帧误差之外的中止条件都会向应用程序发送一个单字节('/0')的报告 |
INPCK | 如果设置,则进行奇偶校验。如果不进行奇偶检验,PARMRK和IGNPAR将对存在的奇偶校验错误不产生任何的影响 |
ISTRIP | 如果设置,所接收到的所有字节的高位将会被去除,保证它们是一个7位的字符 |
INLCR | 如果设置,所接收到的换行字符('/n')将会被转换成回车符('/r') |
IGNCR | 如果设置,则会忽略所有接收的回车符('/r') |
ICRNL | 如果设置,但IGNCR没有设置,接收到的回车符向应用程序发送时会变换成换行符 |
IUCLC | 如果IUCLC和IEXTEN都设置,接收到的所有大写字母发送给应程序时都被转换成小写字母。POSIX中没有定义该标记 |
IXON | 如果设置,接收到S后会停止向这个tty设备输出,接收到Q后会恢复输出 |
IXANY | 如果设置,则接到任何字符都会重新开始输出,而不仅仅是^Q字符 |
IXOFF | 如果设置,为避免tty设备的输入缓冲区溢出,tty设备可以向终端发送停止符S和开始符Q,要求终端停止或重新开始向计算机发送数据。通过停 止符和开始符来控制数据流的方式叫软件流控制,软件流控制方式较少用,我们主要还是用硬件流控制方式。硬件流控制在c_cflag标志中设置 |
IMAXBEL | 如果设置,当输入缓冲区空间满时,再接收到的任何字符就会发出警报符'/a'。POSIX中没有定义该标记 |
IUTF8 | (不属于 POSIX)输入 IUTF8 ,这是允许 character-erase 在 cooked 模式下被正确执行 |
2. c_oflag 输出模式标志
- 用于控制串口的输出模式
选项值 | 说明 |
---|---|
OPOST | 开启该标记,后面的输出标记才会生效。否则,不会对输出数据进行处理 |
OLCUC | 如果设置,大写字母被转换成小写字母输出 |
ONLCR | 如果设置,在发送换行符('/n')前先发送回车符('/r') |
OCRNL | 如果设置,回车符会被转换成换行符。另外,如果设置了ONLRET,则current column会被设为0 |
ONOCR | 如果设置,当current column为0时,回车符不会被发送也不会被处理 |
ONLRET | 如果设置,当一个换行符或回车符被发送的时候,current column会被设置为0 |
OFILL | 发送填充字符作为延时,而不是使用定时来延时 |
OFDEL | (不属于 POSIX) 填充字符是 ASCII DEL (0177)。如果不设置,填充字符则是 ASCII NUL |
VTDLY | 竖直跳格延时掩码。取值为 VT0 或 VT1 |
3. c_cflag 控制模式标志
- 用于控制串口的基本参数,如数据位、停止位等,常用配置见下表,特别地,c_cflag结构体成员还包含了波特率的参数
选项值 | 说明 |
---|---|
CLOCAL | 如果设置,modem的控制线将会被忽略。如果没有设置,则open()函数会阻塞直到载波检测线宣告modem处于摘机状态为止 |
CREAD | 只有设置了才能接收字符,该标记是一定要设置的 |
CSIZE | 设置传输字符的位数。CS5表示每个字符5位,CS6表示每个字符6位,CS7表示每个字符7位,CS8表示每个字符8位 |
CSTOPB | 设置停止位的位数,如果设置,则会在每帧后产生两个停止位,如果没有设置,则产生一个停止位。一般都是使用一位停止位。需要两位停止位的设备已过时 了 |
HUPCL | 如果设置,当设备最后打开的文件描述符关闭时,串口上的DTR和RTS线会减弱信号,通知Modem挂断。也就是说,当一个用户通过Modem拔号 登录系统,然后注销,这时Modem会自动挂断 |
PARENB | 允许输出产生奇偶信息以及输入的奇偶校验(启用同位产生与侦测) |
PARODD | 输入和输出是奇校验(使用奇同位而非偶同位) |
CRTSCTS | 使用硬件流控制。在高速(19200bps或更高)传输时,使用软件流控制会使效率降低,这个时候必须使用硬件流控制 |
4. c_lflag 本地模式标志
- 主要用于控制驱动程序与用户的交互,在串口通信中,实际上用不到该成员变量
选项值 | 说明 |
---|---|
ISIG | 当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号 |
ICANON | 启用标准模式 (canonical mode)。允许使用特殊字符EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和WERASE,以及按行的缓冲 |
ECHO | 它可以让你阻止键入字元的回应 |
ECHOE | 如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词 |
ECHOK | 如果同时设置了 ICANON,字符 KILL 删除当前行 |
ECHONL | 如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO |
NOFLSH | 禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列,即关闭queue中的flush |
TOSTOP | 向试图写控制终端的后台进程组发送 SIGTTOU 信号(传送欲写入的信息到后台 处理) |
IEXTEN | 启用实现自定义的输入处理。这个标志必须与 ICANON同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和WERASE,IUCLC 标志才有效 |
5. c_cc[NCCS] 控制字符
- 该数组包含了终端的所有特殊字符,可以修改特殊字符对应的键值(Ctrl+C产生的^C,ASCII码为0x03)
- 仅列出常用的
选项值 | 说明 |
---|---|
VINTR | 中断字符。发出 SIGINT 信号。当设置了c_lflag的ISIG标志位时,该字母不再作为输入传递 |
VERASE | 删除字符。删除上一个还没有删掉的字符,但不删除上一个EOF 或行首。当设置 ICANON 时可被识别,不再作为输入传递 |
VIM | 设置非标准模式读取的最小字节数 |
VTIM | 设置非标准模式读取时的延时值,单位为十分之一秒 |
6. c_ispeed和c_ospeed 波特率
- 注意以 0 开头的数字在是 C语言 的 8进制 数字形式
#define B0 0000000 /* hang up */
#define B50 0000001
#define B75 0000002
#define B110 0000003
#define B134 0000004
#define B150 0000005
#define B200 0000006
#define B300 0000007
#define B600 0000010
#define B1200 0000011
#define B1800 0000012
#define B2400 0000013
#define B4800 0000014
#define B9600 0000015
#define B19200 0000016
#define B38400 000001
#define B57600 0010001
#define B115200 0010002
#define B230400 0010003
#define B460800 0010004
#define B500000 0010005
#define B576000 0010006
#define B921600 0010007
#define B1000000 0010010
#define B1152000 0010011
#define B1500000 0010012
#define B2000000 0010013
#define B2500000 0010014
#define B3000000 0010015
#define B3500000 0010016
#define B4000000 0010017
分析
- 以上只是介绍了 termios 结构体,在编写代码时,我们使用相关 api 去配置该结构体从而配置串口
- api 接口推荐先看本文推荐链接,不懂再看本文
// api 如下
#include <termios.h>
#include <unistd.h>
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
int tcsendbreak(int fd, int duration);
int tcdrain(int fd);
int tcflush(int fd, int queue_selector);
int tcflow(int fd, int action);
void cfmakeraw(struct termios *termios_p);
speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
int cfsetspeed(struct termios *termios_p, speed_t speed);
- 清空接收缓冲区,获取串口参数,配置,更新配置
/* 定义串口结构体 */
struct termios opt;
/* 清空串口接收缓冲区 */
tcflush(fd, TCIOFLUSH);
/* 获取串口参数 */
tcgetattr(fd, &opt);
/* 设置输入、输入波特率 */
cfsetospeed(&opt, B9600);
cfsetispeed(&opt, B9600);
/* 设置数据位数 */
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
/* 校验位 */
opt.c_cflag &= ~PARENB;
opt.c_iflag &= ~INPCK;
/* 设置停止位 */
opt.c_cflag &= ~CSTOPB;
/* 更新配置 */
tcsetattr(fd, TCSANOW, &opt);
1.1.4 串口收发测试
- 串口收发测试就是对该串口进行读写
/* 发送测试 */
write(fd, bufW, strlen(bufW));
/* 接收测试 */
res = read(fd, bufR, 512);
if(res > 0){
printf("receive data is %s", bufR);
}
1.1.5 关闭设备文件
- 程序正常结束,不要忘记关闭设备文件
/* 关闭文件 */
close(fd);
附件
最终串口测试源码
/** @file main.c
* @brief 串口测试文件-系统调用版
* @details 详细说明
* @author lzm
* @date 2020-11-23 19:18:20
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
*
**********************************************************
* @LOG 修改日志:
**********************************************************
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <string.h>
#include <sys/ioctl.h>
/* 不同的设备,不同的路径 */
const char def_uart_path[] = "/dev/ttymxc2"; // 默认串口路径(备用)
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(int argc, char *argv[])
{
int fd;
int res;
char *path;
char bufW[512] = "This is sys uart test!\n";
char bufR[512];
/* 终端设备选择 */
if(argc > 1)
path = argv[1];
else
path = (char *)def_uart_path;
/* 打开终端 */
fd = open(path, O_RDWR);
if(fd < 0){
printf("[%s] open err!", path);
return 0;
}
/* 定义串口结构体 */
struct termios opt;
/* 清空串口接收缓冲区 */
tcflush(fd, TCIOFLUSH);
/* 获取串口参数 */
tcgetattr(fd, &opt);
/* 设置输入、输入波特率 */
cfsetospeed(&opt, B9600);
cfsetispeed(&opt, B9600);
/* 设置数据位数 */
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
/* 校验位 */
opt.c_cflag &= ~PARENB;
opt.c_iflag &= ~INPCK;
/* 设置停止位 */
opt.c_cflag &= ~CSTOPB;
/* 更新配置 */
tcsetattr(fd, TCSANOW, &opt);
do{
/* 发送测试 */
write(fd, bufW, strlen(bufW));
/* 接收测试 */
res = read(fd, bufR, 512);
if(res > 0){
printf("receive data is %s", bufR);
}
}while(res >= 0);
/* 读取错误 */
printf("read error, res = %d", res);
/* 关闭文件 */
close(fd);
return 0;
}
【linux】系统调用版串口分析&源码实战的更多相关文章
- 【linux】i2c使用分析&源码实战
目录 前言 1. 设备检查命令 1.1 查看I2C驱动 1.2 i2c-tools 1.2.1 I2C-detect安装 1.2.2 i2cdetect 命令 1.2.3 i2cget 命令 1.2. ...
- 【芯片手册开发】Sil9136音频开发详细分析+源码实战
目录 前言 参考 手册使用+实战 配置 Configuring Audio Using I2S 总结实现 前言 默认在开发了视频方面后 这方面的工作本来可以找技术支持拿个例程参考下,很快就可以的写出来 ...
- Linux下USB suspend/resume源码分析【转】
转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...
- 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02
百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...
- 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 百篇博客分析OpenHarmony源码 | v18.04
百篇博客系列篇.本篇为: v18.xx 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 51.c.h .o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
- Netty4.x 源码实战系列(一): 深入理解ServerBootstrap 与 Bootstrap
转载自:https://www.cnblogs.com/itdriver/p/8149913.html 从Java1.4开始, Java引入了non-blocking IO,简称NIO.NIO与传统s ...
- Netty4.x 源码实战系列(一): 深入理解ServerBootstrap 与 Bootstrap (1)
从Java1.4开始, Java引入了non-blocking IO,简称NIO.NIO与传统socket最大的不同就是引入了Channel和多路复用selector的概念.传统的socket是基于s ...
- java分析源码-ReentrantLock
一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...
- android版猜拳游戏源码分享
android版猜拳游戏源码分享安卓版猜拳游戏源码,该文件中带有安装测试包的,这个游戏源码比较简单的,现在有两个代码,一个自定义VIEW的,一个就是普通的imageView图片,游戏非常适合一些新手的 ...
随机推荐
- 利用云主机搭建MySQL服务器
前言:有了一台云服务器之后,就想着如何物尽其用.利用其不关机(意外除外)的特性,我们可以在服务器上安装数据库服务,实现云数据库服务器,这样就可以随时随地的访问数据库了,不再受各种限制. 这里以MySQ ...
- GoSDK的安装及环境变量配置 入门详解 - 精简归纳
GoSDK的安装及环境变量配置 入门详解 - 精简归纳 JERRY_Z. ~ 2020 / 10 / 29 转载请注明出处!️ 目录 GoSDK的安装及环境变量配置 入门详解 - 精简归纳 一.进入G ...
- 基于gin的golang web开发:模型验证
Gin除了模型绑定还提供了模型验证功能.你可以给字段指定特定的规则标签,如果一个字段用binding:"required"标签修饰,在绑定时该字段的值为空,那么将返回一个错误.开发 ...
- 水题挑战3: NOIP 2017 宝藏
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 \(n\) 个深埋在地下的宝藏屋, 也给出了这 \(n\) 个宝藏屋之间可供开发的 \(m\) 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋 ...
- 【CHOJ】磁力块
题意描述 磁力块 在平面内分布着 \(N\) 个磁力块,同时你的手上也有一块. 你一开始站在给定的坐标上,当磁力块之间满足互相吸引的条件时就可以吸引. 当你拿到新的磁石时你就可以用它来吸引更多的石头, ...
- 走进C# abstract,了解抽象类与接口的异同
前言: 最近在研究.NET Core的一些类库源码的时候发现在底层类库中使用了大量的抽象类,然而发现自己搬砖这么多年好像从来没有在项目中使用抽象类或者抽象方法去实现某个功能模块,并且对修饰符Abstr ...
- 一个 Task 不够,又来一个 ValueTask ,真的学懵了!
一:背景 1. 讲故事 前几天在项目中用 MemoryStream 的时候意外发现 ReadAsync 方法多了一个返回 ValueTask 的重载,真是日了狗了,一个 Task 已经够学了,又来一个 ...
- https中引入http资源资源所导致的问题
问题描述 因为公司要求所有生产环境为了安全性需求,全部都走https, 并且在Nginx里面加入了Content-Security-Policy "upgrade-insecure-requ ...
- 双十一,就用turtle画个单身狗送给自己
今年的双十一到了 但还有谁记得双十一是 单身狗的节日 单身狗的我是时候站出来 捍卫自己的权益了 单身是一种怎样的状态? 我们所有人都单身过,但也许只有很少的人真正体验过. 短视频内容完全是假的,全程是 ...
- CSS浮动好文章
http://www.cnblogs.com/iyangyuan/archive/2013/03/27/2983813.html 看完上面这篇文章,我哭了.写的真好,我这块更菜.