【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图片,游戏非常适合一些新手的 ...
随机推荐
- BAT大佬推荐使用的HTML5的十个功能
HTML5不是新事物.自从最初发布(2008年1月)以来,我们一直在使用它的一些功能.后来,我再次仔细查看了HTML5功能列表.看到我发现了什么?到目前为止,我还没有真正使用过它! 在本文中,我列出了 ...
- AtCoder 1807 食塩水
题意 有 \(n\) 瓶食盐水,第 \(i\) 瓶为质量 \(w_i\),浓度 \(p_i\%\) 的食盐水,需要选出 \(k\) 瓶食盐水混合在一起,问最大浓度. \(\texttt{Data Ra ...
- NB-IoT技术适合在哪些场景应用
LPWAN,Low Power Wide Area Network,低功耗广域网.名字里就有它的两个最重要的特点:低功耗.广覆盖.目前比较主流的有:NB-IoT.LoRa.Sigfox.eMTC.NB ...
- LWJGL3的内存管理,第三篇,剩下的两种策略
LWJGL3的内存管理,第三篇,剩下的两种策略 上一篇讨论的基于 MemoryStack 类的栈上分配方式,是效率最高的,但是有些情况下无法使用.比如需要分配的内存较大,又或许生命周期较长.这时候就可 ...
- .netcore简单集成swagger
为什么要集成Swagger 在前后端分离比较普遍的当下,当后端开发完接口后,还需把接口的信息.参数说明.返回参数等信息编写好提供给调用者.对于对外开放接口需提供说明文档是必不可少的.但是对于内部开发, ...
- WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一)
WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一) 很多小伙伴在web开发中都遇倒的问题? JavaWeb项目注销后,可能存在通过浏览器缓存回退的方式进入主页系统 WEB项目 ...
- Java swing实现酒店管理系统
今天给大家提供一个由今天给大家提供一个由Java swing实现的酒店管理系统,数据库采用sqlserver,我会贴上部分代码,完整的代码请看文章最下方下载,下面看代码: 1.主框架代码: packa ...
- 7.1range函数和冒泡排序
range函数和冒泡排序 一.range函数 在Python开发应用中 range函数相当重要,也比较常用: 首先看range函数的原型: range(start, end, scan) 参数 ...
- C++ 基础 6:模板
1 函数模板 泛型(Generic Programming),即是指具有在多种数据类型上皆可操作的含意. 泛型编程 的代表作品 STL 是一种高效.泛型.可交互操作的软件组件. 泛型编程最初诞生于 C ...
- 3.4 spring5源码系列--循环依赖的设计思想
前面已经写了关于三篇循环依赖的文章, 这是一个总结篇 第一篇: 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 第二篇: 3.2spring源码系列----循环依赖源 ...