当前位置: 首页 > news >正文

Linux应用开发-7-串口通讯与终端设备

基础知识:

  • tty(teletype([通信] 电传打字机) 的缩写用于串口设备)
  • ls /dev/tty* 查看设备所有的串口打印设备
  • ls /dev/ttySTM* 查看用于串口通信的几个口
    • /dev/ttySTM0 开发板的串口4(对于板子接线来说),它被默认被用在命令行的终端,对应USB转串口
    • /dev/ttySTM1 对应开发板串口1
    • /dev/ttySTM3 对应开发板串口3

stty(set teletype)命令

#它会输出当前终端的参数即STM0
stty
/*
speed 115200 baud; line = 0;
-brkint ixoff -imaxbel iutf8
-iexten
*/
#指定设备输出信息
#-F`(--file) 告诉 stty命令不要操作我当前正在使用的这个终端,而是去操作你后面指定的那个设备文件
stty -F /dev/ttySTM3# 还可以设置通讯速率,其中 ispeed 为输入速率,ospeed 为输出速率
stty -F /dev/ttySTM0 ispeed 9600 ospeed 9600

如果没有设备ttySTM3,则进行启用

#编辑启动文件
nano /boot/uEnv.txt
#找到对应串口取消注释
#重启板子
reboot

文件中内容修改见以下位置

#Docs: http://elinux.org/Beagleboard:U-boot_partitioning_layout_2.0uname_r=4.19.94-stm-r1
#uuid=
dtb=stm32mp157a-basic.dtb###U-Boot Overlays###
###Documentation: https://embed-linux-tutorial.readthedocs.io/zh_CN/latest/linu$
###Master Enable
enable_uboot_overlays=1
#overlay_start# BUS class/function
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-i2c1.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-i2c2.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-ltdc.dtbo
# DEV class/function
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-485r1.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-485r2.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-adc.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-bluetooth.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-btwifi.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-can.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-cam.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-hdmi.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-key.dtbodtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-lcd.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-led.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-mipi.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-mpu6050.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-sound.dtbo
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-touch-capacitiv$
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-touch-capaciti$
#dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-usart1.dtbo
#===================================文件中下面位置取消注释=================================
dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlays/stm-fire-usart3.dtbo
#overlay_endcmdline=coherent_pool=1M net.ifnames=0 vt.global_cursor_default=0#In the event of edid real failures, uncomment this next line:
#cmdline=coherent_pool=1M net.ifnames=0 vt.global_cursor_default=0 video=HDMI-A$#Use an overlayfs on top of a read-only root filesystem:
#cmdline=coherent_pool=1M net.ifnames=0 vt.global_cursor_default=0 overlayroot=$
#flash_firmware=continued
#flash_firmware=once# specify kernel eth0 mac address
ethaddr=6e:c8:57:e6:34:f5# specify storage_media
storage_media=init=/opt/scripts/tools/eMMC/init-eMMC-flasher-v3.sh

串口通讯配置接线

  • 查看串口的详细信息,最主要的是波特率,windows和串口波特率保持一致,stty -F /dev/ttySTM3

  • 关闭回显 stty -F /dev/ttySTM3 -echo -表示减,-echo代表禁用或关闭echo这个设置

    • 关闭回显(输入的内容返回回来)原因:进行串口通信时,发送给设备的命令,设备自己处理后可能会返回结果,你不需要设备把你发的命令原样再发回来。如果开启 echo,可能会导致数据混乱或重复。
  • 连接串口线及跳线帽,跳帽(UART3_TXD<—->T2IN、UART3_RXD<—->R2OUT),串口3与主机进行连接(串口usb转DB9)

    在这里插入图片描述

方法一、 Windows 主机通讯

下载调试信息软件-野火多功能调试助手上位机 — 野火产品资料下载中心 文档

数据发送

#在开发板上的终端执行执行数据发送
echo board > /dev/ttySTM3
echo embedfire > /dev/ttySTM3
#windows上接收数据#在开发板上的终端执行以下指令
#使用 cat命令读取终端设备文件
cat /dev/ttySTM3
#cat命令会等待接受window发来的信息

注意:windows发送时,输入的字符串之后一定要加回车enter
在这里插入图片描述

方法二、 Ubuntu 主机通讯

分配 USB 转串口设备

串口转usb插入主机,虚拟机会弹出选项,选择连接到虚拟机
在这里插入图片描述
执行ls /dev/tty*
在这里插入图片描述
注:如果未显示ttyUSB的选项执行下面步骤
ls /dev/tty*
在这里插入图片描述
1.排查USB设备是否插入虚拟机lsusb
在这里插入图片描述
2.查看内核日志进行排查
在这里插入图片描述
显示名为 brltty 的程序(BRLTTY 6.4 Linux Screen Driver)强行介入,它也想“认领”这个USB设备,与 ch341 驱动发生了冲突。(brltty 是一个为盲人用户提供的辅助功能服务,用于将终端内容输出到盲文显示器(可以放心卸载))
执行卸载:sudo apt remove brltty 再次执行就会出现ttyUSB这个设备文件了。

安装和配置 minicom

虚拟机中执行:

#安装minicom插件
sudo apt install minicom
#运行配置
sudo minicom -s
#执行这个插件
sudo minicom

在这里插入图片描述
以下三个地方需要修改:
在这里插入图片描述
配置完要保存配置:
在这里插入图片描述

9600这里是因为板子串口的配置,要对应才能进行数据传输。
在这里插入图片描述
板子端输入echo board > /dev/ttySTM3 虚拟机就能接收到
在这里插入图片描述
:上述操作流程 上下左右选中,然后enter选中,以及shift+字母
说明minicom命令键:先按下 Ctrl+A 组合键,然后松开,再按下 o键弹出配置,同理先按下 Ctrl+A 组合键,然后松开,再按下 x键是退出。

串口通讯(系统调用)termios

说明:termios-串口(或任何终端设备)的“设置总控制面板”(所有配置项的集合体,类似C的struct);用Linux打开一个串口设备文件(本文:/dev/ttySTM3)内核会为这个文件描述符关联一套默认的 termios 配置。编程可以通过系统调用(system calls)来读取这个“控制面板”,修改上面的“旋钮”和“开关”,然后再把新的设置应用回去。

1)termios 的核心作用:定义串口的行为

主要控制四大方面的行为

  • 1.物理层参数 (Hardware Parameters) :定义最基本的电气信号规则。

    • 波特率 (Baud Rate) :通信双方的速度,必须完全一致。

    • 数据位 (Data Bits) :一帧数据包含多少个bit(通常是8)。

    • 停止位 (Stop Bits) :一帧数据的结束标记(通常是1位)。

    • 校验位 (Parity Bit) :用于简单的数据错误检测(通常是无校验)。

  • 2.输入数据处理 (Input Processing) :定义内核在将接收到的数据交给应用程序之前,要对数据做的预处理

    • 是否要检查校验位错误?是否要将回车符\r自动转换成换行符\n?是否要处理软件流控字符(XON/XOFF)?
  • 3.输出数据处理 (Output Processing) :定义内核在将应用程序数据发送串口硬件之前,要对数据做什么后处理

    • 是否要将换行符\n自动转换成回车换行符\r\n(很多Windows设备需要)?
  • 4.本地模式 (Local Modes) :定义终端驱动程序行为模式,这对于串口通信至关重要。

    • 是否回显 (Echo) :把收到的字符再发送回去(用stty -F "操作的串口文件路径" -echo关掉)。

    • 是否是“规范模式”(Canonical Mode) :这是默认模式,数据以“行”为单位进行处理,直到你输入回车键,数据才会被提交给程序。

    • 是否处理信号字符:比如按下 Ctrl+C 时,是否要给程序发送一个 SIGINT 信号来中断它。

2)termios 结构体详解(控制面板上的分区)

termios 结构体总览,5个功能区:

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_cc[NCCS]; /* 控制字符 (Control characters) */
};
  • 1. c_cflag (Control Flags) - 硬件控制区

    • 设置波特率:B115200, B9600,通过 cfsetispeed()cfsetospeed() 两个函数。

    • 设置数据位 (Character Size)CS8, CS7, CS6, CS5CS8 代表8位数据位,最常用)

    • 设置停止位 CSTOPB,设置则是2位停止位;不设置则是1位停止位。

    • 开启校验位 (Parity Enable) PARENB

    • PARODD 开启校验位,设置此标志则为奇校验,否则为偶校验。

    • 开启硬件流控 (RTS/CTS) CRTSCTS

  • 2. c_lflag (Local Flags) - 模式控制区 (决定串口是作为“原始数据通道”还是“交互式终端”)

    • ICANON-icanon: 启用规范模式。默认此模式。在此模式下,输入基于“行”,内核会处理退格、删除等编辑操作,只有当用户按下回车时,整行数据才对 read() 可见。-串口数据通信时,必须禁用

    • ECHO-echo: 回显输入字符。 -必须禁用

    • ISIG-isig: 当接收到 INTR, QUIT, SUSP 等字符时(例:Ctrl+C, Ctrl+ ),产生相应的信号。-必须禁用

  • 3. c_iflag (Input Flags) - 输入处理区 (控制对接收到的字节的处理)

    • IXON-ixon, IXOFF-ixoff, IXANY-ixany: 启用软件流控 (XON/XOFF)。通常禁用。

    • INPCK-inpck: 启用输入校验检查。

    • ISTRIP-istrip: 剥离第8位(即只保留7-bit ASCII)。通常禁用。

    • ICRNL-icrnl: 将接收到的回车符 \r 转换成换行符 \n。是否需要看对方设备的要求。

  • 4. c_oflag (Output Flags) - 输出处理区(控制对要发送的字节的处理)

    • OPOST-opost: 启用输出处理。如果禁用,所有其他 c_oflag 选项都会被忽略,数据将“按原样”发送,是实现原始数据传输的快捷方式。
  • 5. c_cc[NCCS] (Control Characters) - 特殊字符定义区(定义了各种特殊控制字符的功能)

    • 两个主要功能:

      • VMIN-vmin: read() 函数在返回前需要读取的最少字节数
      • VTIME-vtime: read() 函数的超时时间,单位是 1/10 秒。
    • VMINVTIME 的组合可以实现对 read() 行为的精确控制:

      • vmin > 0, vtime = 0: 阻塞式读取(返回字节数>0,持续读取无时间超时代表立即返回)。read() 会一直等待,直到读满 VMIN 个字节才返回。
      • vmin = 0, vtime > 0: 定时读取(无限制返回字节数,超时时间>0代表等待)。read() 会等待 VTIME * 0.1 秒,然后返回它在这段时间内读到的所有字节(可能是0)。
      • vmin > 0, vtime > 0: 带超时的阻塞读取。read() 等待 VMIN 个字节,但如果在两个字节之间等待超过 VTIME * 0.1 秒,就会超时并返回当前已读到的字节。
      • vmin = 0, vtime = 0: 纯非阻塞。read() 立即返回,不管有没有数据。

3)termios 的标准用法 (编程步骤)

**思想:**先获取当前的设置,然后在此基础上修改。

**具体操作:**程序首先打开/dev/ttySTM3串口设备,接着通过termios接口将其精心配置为适合机器间通信的“原始模式”,设定了9600波特率、8N1数据格式以及一个短暂的读取超时以避免程序阻塞。在将这些配置应用到硬件并清空缓冲区后,程序会发送一条“Hello from Lubancat!”问候语,随即短暂等待并尝试读取任何可能返回的响应数据,最后规范地关闭串口以释放系统资源,从而完成一个健壮的初始化、配置、数据收发和清理的完整通信周期。

**具体目的:**如何编写一个C语言程序,来“代替” minicom 或 stty,用代码去编程控制和配置串口。

**思想核心证明:**1.Linux系统中所有的串口配置都保存在一个名为 termios 的结构体中;2.您可以通过C函数 tcgetattr() 来读取这个结构体;3.可以通过修改这个结构体的成员(如 c_cflag、c_iflag)或使用特定函数(如 cfsetispeed())来修改波特率、数据位、停止位和校验位;4.可以通过 tcsetattr() 函数将修改后的配置写回给串口,使其立即生效。

#include <stdio.h>
#include <fcntl.h>   //
#include <unistd.h>  //
#include <termios.h> // 包含termios相关的函数和结构体
#include <string.h>  // 包含 strerrorint main() {// 1. 打开串口设备文件// O_RDWR: 可读可写// O_NOCTTY: 不将此设备设置为主控终端// O_NDELAY: 非阻塞模式 (在某些系统上可能需要)int fd = open("/dev/ttySTM3", O_RDWR | O_NOCTTY);if (fd < 0) {perror("Failed to open serial port");return -1;}// 2. 获取当前的termios配置struct termios options;//创建一个临时的、本地的容器(变量),//这个容器的结构正好可以用来完整地存放串口的所有配置信息。//接下来的所有修改操作,都是先在这个名为 `options` 的“工作台”上完成。//tcgetattr(fd, &options)=>读取串口当前的实际配置,并填写到您的“工作台”上。//在这个函数中:fd 告诉 tcgetattr 函数:请去获取由这个号码所代表的那个串口的当前配置。”tcgetattr(fd, &options);// 3. 修改配置 (这是核心步骤)// 3.1 设置为原始模式 (Raw Mode)// 这是与设备进行纯数据通信的关键cfmakeraw(&options); // cfmakeraw会帮你禁用ICANON, ECHO, ISIG等,并禁用输入输出处理// 3.2 设置波特率 (例如 9600)cfsetispeed(&options, B9600); // 设置输入波特率cfsetospeed(&options, B9600); // 设置输出波特率// 3.3 设置数据位、停止位、校验位 (通过c_cflag)// `|` (按位或 `OR`) || `&` (按位与 `AND`) || `~` (按位非 `NOT`)//options.c_cflag相当于一个控制面板,一位(bit)都连接着一个**物理开关**,控制着一项功能//以下每一个变量设置对应位上的值options.c_cflag &= ~CSIZE;    // 清除数据位掩码options.c_cflag |= CS8;       // 设置为8位数据位options.c_cflag &= ~PARENB;   // 禁用校验位options.c_cflag &= ~CSTOPB;   // 设置为1位停止位options.c_cflag |= CREAD;     // 启用接收options.c_cflag |= CLOCAL;    // 忽略调制解调器状态线// 3.4 设置read()的超时行为 (通过c_cc)options.c_cc[VMIN]  = 0;  // read()立即返回options.c_cc[VTIME] = 5;  // 超时时间 0.5 秒// 4. 应用新的配置// TCSANOW: 立即生效if (tcsetattr(fd, TCSANOW, &options) != 0) {perror("Failed to set attributes");close(fd);return -1;}// 清空串口的输入输出缓冲区 (可选,但推荐)tcflush(fd, TCIOFLUSH);// 5. 进行读写操作char *msg = "Hello from Lubancat!\n";int n_written = write(fd, msg, strlen(msg));if (n_written < 0) {perror("Failed to write to port");} else {printf("Sent %d bytes: %s", n_written, msg);}sleep(1); // 等待一会,看看有没有数据返回char read_buf[256];int n_read = read(fd, read_buf, sizeof(read_buf));if (n_read > 0) {read_buf[n_read] = '\0'; // 添加字符串结束符printf("Received %d bytes: %s\n", n_read, read_buf);}// 6. 关闭串口close(fd);return 0;
}

对照函数表

#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);//获取输入波特率
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);//清空buffer数据
int tcflush(int fd, int queue_selector);//等待所有输出都被发送
int tcdrain(int fd);//在一个指定的时间区内发送连续的0位流
int tcsendbreak(int fd, int duration);//用于对输入和输出流控制进行控制
int tcflow(int fd, int action);//将终端设置为原始模式
void cfmakeraw(struct termios *termios_p);

具体应用时代码

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>int main() {/***************************************************************** 配置阶段 (Initialization) - 这部分代码在程序生命周期中只执行一次****************************************************************/// 1. 打开串口设备文件int fd = open("/dev/ttySTM3", O_RDWR | O_NOCTTY);if (fd < 0) {perror("Failed to open serial port");return -1;}// 2. 获取并修改termios配置 (所有stty/termios操作都在这里完成)struct termios options;tcgetattr(fd, &options);// 设置为原始模式, 9600, 8N1等...cfmakeraw(&options);cfsetispeed(&options, B9600);cfsetospeed(&options, B9600);options.c_cflag &= ~CSIZE;options.c_cflag |= CS8;options.c_cflag &= ~PARENB;options.c_cflag &= ~CSTOPB;options.c_cflag |= CREAD | CLOCAL;options.c_cc[VMIN]  = 0;options.c_cc[VTIME] = 5; // 0.5秒超时// 3. 应用新的配置if (tcsetattr(fd, TCSANOW, &options) != 0) {perror("Failed to set attributes");close(fd);return -1;}// 4. 清空缓冲区tcflush(fd, TCIOFLUSH);printf("Serial port configured. Starting communication loop...\n");/***************************************************************** 通信循环 (Communication Loop) - 这部分代码会无限次地重复执行****************************************************************/while (1) {// --- 循环读取 ---char read_buf[256];int n_read = read(fd, read_buf, sizeof(read_buf));if (n_read > 0) {read_buf[n_read] = '\0';printf("Received %d bytes: %s", n_read, read_buf);// 在这里添加对接收到数据的处理逻辑...} else if (n_read < 0) {perror("Error reading from serial port");// 可以选择在这里跳出循环或进行错误处理break; }// 如果 n_read == 0, 说明超时了但没有读到数据,这是正常情况,继续下一次循环即可。// --- 循环写入 (示例) ---// 比如可以每隔5秒发送一次心跳包// write(fd, "HEARTBEAT\n", 10);// sleep(5);}/***************************************************************** 清理阶段 (Cleanup) - 正常情况下, 程序不会执行到这里,除非循环被break****************************************************************/printf("Closing serial port.\n");close(fd);return 0;
}

(tty_demo)在开发板上运行时,它会打开开发板自己的串口(/dev/ttySTM3),配置它(9600, 8N1等),然后向这个串口发送数据(write(fd, msg, …)),并接收数据(read(fd, read_buf, …))。

**顺带一提:**ioctl 是“底层实现”: 在Linux内核的“幕后”,tcgetattr() 和 tcsetattr() 这些函数内部调用的,正是 ioctl 这个更底层的、更“原始”的系统调用。

http://www.dtcms.com/a/556981.html

相关文章:

  • 河北廊坊做网站一个网站后台怎么做
  • 企业培训考试系统源码php答题考试、题库、错题、练习考试等功能
  • 开拓视野:漫谈WebView领域相关技术
  • 如何在机器学习中使用特征提取对表格数据进行处理
  • UMI企业智脑助力数字化转型与智能化升级
  • xshell使用scp命令上传和下载文件
  • 命令行传参及调试——vscode平台
  • 【面试进阶】JavaScript 函数与对象进阶知识总结(重难点+记忆模板)
  • 《赋能AI解锁Coze智能体搭建核心技能(2)--- 智能体开发基础》
  • 自贡网站优化js网站开发视频教程
  • 驱动增长,而非浪费:8步整合SEO与PMax,解锁Google AI的隐藏流量
  • 【图像处理基石】如何在图像中实现光晕的星芒效果?
  • Node.js 解释环境变量的定义、作用及在Node.js中的重要性,区分开发、测试、生产环境配置需求。
  • Rust 快速入门:从零到上手的系统指南
  • 做家政网站网站公司做的网站有最字
  • kafka 延迟消费配置
  • Win32 API 简洁版
  • RocketMQ 是什么?它的架构是怎么样的?和 Kafka 又有什么区别?
  • 企业微信网站建设方案模板下载wordpress几种系统
  • Token Activation Map to Visually Explain Multimodal LLMs
  • RHCSA-15网络管理
  • cpp 02
  • 中国建设银行官网网站忻州市城乡建设管理局网站
  • 【机器学习深度学习】强化学习与监督学习SFT、RL、RLHF、PPO、DPO
  • CSS3 框大小:深入解析与优化技巧
  • 用web端实现抠图,以及大模型本地化部署遇到坑
  • 【推荐系统9】重排模型:基于贪心、个性化的重排
  • Pandas-之Series 数据结构详解
  • 关键词解释:多视图学习(Multi-view Learning)
  • 类与对象(上):面向过程到面向对象的跨越,类的定义、封装与 this 指针等核心概念深度剖析