07、Linux 串口编程
一、串口介绍
串口(Serial Port),也叫COM口,是电脑和外部设备(如传感器、仪器等)之间传输数据的接口。它通过逐个发送数据点的方式工作,一次只传输一个二进制位。
这种接口最基础的连接只需要三根线:
- 地线(GND):作为公共参考点
- 发送线(TX):发送数据用
- 接收线(RX):接收数据用
特别说明:
- 支持双向通信(能同时收发数据)
- 不需要额外的时钟线同步,因为设备之间通过预设的通信协议自动匹配传输速度
简单来说,就像两个人用三根电话线通话:一根地线做基础,一根负责你说,另一根负责我听,双方约定好语速后就可以随时互相交流了。
串口在实际工作中经常用到。比如在嵌入式Linux设备中,调试串口能让我们登录系统并查看运行日志。此外,它还常用来和外部设备通信,例如连接GPS定位模块或RS485总线设备。
根据信号电压的不同,串口主要分为两种类型:TTL电平和RS232电平。TTL电平使用3.3V或5V的低电压信号,适合近距离连接;而RS232电平使用-15V到+15V的高电压信号,能实现更长距离的稳定传输。
二、串口通讯的数据结构
termios
是 Linux 系统中用来设置串口或终端设备参数的结构体,定义在头文件 <linux/termios.h>
里。它包含了一系列配置项,可以控制设备的通信细节,比如:
- 传输速度(波特率)
- 数据位数量
- 奇偶校验方式
- 停止位数量
简单来说,这个结构体就是用来调整串口或终端设备如何发送和接收数据的设置参数集合。:
struct termios {
tcflag_t c_iflag; // 输入模式标志
tcflag_t c_oflag; // 输出模式标志
tcflag_t c_cflag; // 控制模式标志
tcflag_t c_lflag; // 本地模式标志
cc_t c_cc[NCCS]; // 控制字符数组
};
2
3
4
5
6
7
以下是 struct termios
结构体的几个关键字段的简单说明:
**c_iflag**
控制输入行为的设置,比如如何处理输入的特殊字符或数据。例如,是否启用回车转行转换、输入过滤等功能。**c_oflag**
控制输出行为的设置,比如如何处理输出的数据。例如,是否添加特殊字符或进行格式化处理。**c_cflag**
设置终端的物理通信参数,如波特率(传输速度)、数据位、校验方式和停止位等。这些参数决定了设备如何物理层面地传输数据。**c_lflag**
管理终端的本地行为,比如是否启用回显、 canoncial 模式(输入按行处理)或信号处理等。这些影响用户输入和程序交互的方式。**c_cc**
一个特殊字符数组,定义终端的控制字符。例如,设置退格键、结束输入键(如Ctrl+D
)或暂停/继续输出的按键等。
每个字段都通过一组“标志位”(bit flag)快速配置终端的行为,而无需复杂操作。
三、串口的使用步骤
串口是嵌入式开发中常用的外设,主要用于调试程序和连接其他设备(如打印机、蓝牙、WiFi、GPS等)。
使用串口的步骤如下:
- 打开串口设备
使用open()
函数连接串口设备。 - 获取当前配置
用tcgetattr()
读取串口的默认设置(如波特率、数据位等)。 - 修改配置参数
根据需求调整参数(例如设置波特率为 9600)。 - 保存新配置
用tcsetattr()
将修改后的参数应用到串口。 - 读写数据
使用read()
接收数据,或write()
发送数据。 - 关闭串口
完成后用close()
关闭设备,释放资源。
::: 关键点:
每一步都对应一个函数(如 open()
、tcsetattr()
等),直接操作串口。
配置修改需先获取参数、调整后再保存,确保设置生效。
数据读写完成后记得关闭串口,避免资源占用。 :::
四、常用串口控制函数
4.1 tcgetattr() 和 tcsetattr() 函数
这两个函数用于读取或修改串口设备的配置。
- tcgetattr()
作用:获取当前串口的配置参数(比如波特率、数据位等)。
调用方式:
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
示例:读取串口设置到 termios
结构体中。
tcsetattr()
作用:设置串口的新配置参数。
参数说明:fd
:设备文件描述符(如/dev/ttyS0
)。TCSANOW
:立即生效。TCSADRAIN
:等当前发送的数据完成后生效。TCSAFLUSH
:先清空收发缓冲区再生效。
termios_p
:包含新配置的struct termios
结构体。
典型用途:设置波特率、数据位、校验位等。
4.2 cfgetispeed() 和 cfgetospeed() 函数
这两个函数用于查看当前串口的输入/输出波特率。
- cfgetispeed()
作用:获取输入波特率的值。
调用方式:
cfgetispeed(const struct termios *termios_p)
- cfgetospeed()
作用:获取输出波特率的值。
调用方式:
cfgetospeed(const struct termios *termios_p)
注意:这两个函数需要先通过 tcgetattr()
获取配置参数结构体。
4.3 cfsetispeed() 和 cfsetospeed() 函数
这两个函数用于设置输入/输出波特率。
- cfsetispeed()
作用:设置输入波特率。
调用方式:
cfsetispeed(struct termios *termios_p, speed_t speed):
- cfsetospeed()
作用:设置输出波特率。
调用方式:
cfsetospeed(struct termios *termios_p, speed_t speed):
示例:
cfsetispeed(&termios, B9600); // 设置输入波特率为 9600
cfsetospeed(&termios, B9600); // 设置输出波特率为 9600
2
4.4 tcflush() 和 tcflow() 函数
这两个函数用于控制串口缓冲区和数据流。
tcflush()
作用:清空输入或输出缓冲区。
参数说明:TCIFLUSH
:清空输入缓冲区。TCOFLUSH
:清空输出缓冲区。TCIOFLUSH
:同时清空输入和输出缓冲区。
调用方式:
tcflush(int fd, int queue_selector)
tcflow()
作用:暂停或恢复数据传输。
参数说明:TCOOFF
:暂停发送数据。TCOON
:恢复发送数据。TCIOFF
:暂停接收数据。TCION
:恢复接收数据。
调用方式:
tcflow(int fd, int action
:::
总结
配置串口流程:
用 tcgetattr()
读取当前配置。
修改配置参数(如波特率、数据位)。
用 tcsetattr()
应用新配置。
调试时常用操作:
用 tcflush()
清空缓冲区避免残留数据干扰。
用 tcflow()
暂停/恢复数据流控制通信节奏。 :::