<Linux开发> linux应用开发-之-uart通信开发例程
创始人
2025-05-31 04:46:27
0

一、简介
串口全称叫做串行接口,串行接口指的是数据一个一个的按顺序传输,通信线路简单。使用两条线即可.
实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。
关于串口的基础知识以及通行原理、通行数据格式等之类的问题。串口(UART)在嵌入式 Linux 系统中常作为系统的标准输入、输出设备,系统运行过程产生的打印信息通过串口输出;同理,串口也作为系统的标准输入设备,用户通过串口与 Linux 系统进行交互。
更加详细介绍说明读者可自行查阅相关资料。

二、环境搭建
本次测试uart通信的应用例程是运行在ubuntu pc上的;当然也是可以运行在linux开发板 或相关linux设备上的。
如果在Linux开发板上运行,需要有交叉编译工具。
本次测试实现的是自发自收,在PC上插入一个串口模块(uart转CH340模块,其它模块也可),然后将RX 引脚 与TX引脚 通过杜邦线连接即可。

三、例程代码
本次代码会使用单独的一个c文件用来编写uart代码,用以接收数据并在终端打印;
代码如下:

/***************************************************************
Copyright © OneFu Co., Ltd. 1998-2022. All rights reserved.
文件名 : uart.c
作者 : waterfxw
版本 : V1.0
描述 : uart 示例代码
其他 : 主要是测试 使用cmake
日志 : 初版 V1.0 2023/03/20 waterfxw创建
***************************************************************/#define _GNU_SOURCE //在源文件开头定义_GNU_SOURCE 宏
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include typedef struct uart_hardware_cfg {unsigned int baudrate;          /* 波特率 */unsigned char dbit;             /* 数据位 */char parity;                    /* 奇偶校验 */unsigned char sbit;             /* 停止位 */
} uart_cfg_t;static struct termios old_cfg;      //用于保存终端的配置参数
static int fd;                      //串口终端对应的文件描述符/**
** 串口初始化操作
** 参数 device 表示串口终端的设备节点
**/
static int uart_init(const char *device) {/* 打开串口终端 */fd = open(device, O_RDWR | O_NOCTTY);if (0 > fd) {fprintf(stderr, "open error: %s: %s\n", device, strerror(errno));return -1;}/* 获取串口当前的配置参数 */if (0 > tcgetattr(fd, &old_cfg)) {fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));close(fd);return -1;}return 0; 
}/**
** 串口配置
** 参数 cfg 指向一个 uart_cfg_t 结构体对象
**/
static int uart_cfg(const uart_cfg_t *cfg) {struct termios new_cfg = {0}; //将 new_cfg 对象清零speed_t speed;/* 设置为原始模式 */cfmakeraw(&new_cfg);/* 使能接收 */new_cfg.c_cflag |= CREAD;/* 设置波特率 */switch (cfg->baudrate) {case 1200: speed = B1200;break;case 1800: speed = B1800;break;case 2400: speed = B2400;break;case 4800: speed = B4800;break;case 9600: speed = B9600;break;case 19200: speed = B19200;break;case 38400: speed = B38400;break;case 57600: speed = B57600;break;case 115200: speed = B115200;break;case 230400: speed = B230400;break;case 460800: speed = B460800;break;case 500000: speed = B500000;break;default: //默认配置为 115200speed = B115200;printf("default baud rate: 115200\n");break;}if (0 > cfsetspeed(&new_cfg, speed)) {fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));return -1;}/* 设置数据位大小 */new_cfg.c_cflag &= ~CSIZE; //将数据位相关的比特位清零switch (cfg->dbit) {case 5:new_cfg.c_cflag |= CS5;break;case 6:new_cfg.c_cflag |= CS6;break;case 7:new_cfg.c_cflag |= CS7;break;case 8:new_cfg.c_cflag |= CS8;break;default: //默认数据位大小为 8new_cfg.c_cflag |= CS8;printf("default data bit size: 8\n");break;}/* 设置奇偶校验 */switch (cfg->parity) {case 'N': //无校验new_cfg.c_cflag &= ~PARENB;new_cfg.c_iflag &= ~INPCK;break;case 'O': //奇校验new_cfg.c_cflag |= (PARODD | PARENB);new_cfg.c_iflag |= INPCK;break;case 'E': //偶校验new_cfg.c_cflag |= PARENB;new_cfg.c_cflag &= ~PARODD; /* 清除 PARODD 标志,配置为偶校验 */new_cfg.c_iflag |= INPCK;break;default: //默认配置为无校验new_cfg.c_cflag &= ~PARENB;new_cfg.c_iflag &= ~INPCK;printf("default parity: N\n");break;}/* 设置停止位 */switch (cfg->sbit) {case 1: //1 个停止位new_cfg.c_cflag &= ~CSTOPB;break;case 2: //2 个停止位new_cfg.c_cflag |= CSTOPB;break;default: //默认配置为 1 个停止位new_cfg.c_cflag &= ~CSTOPB;printf("default stop bit size: 1\n");break;}/* 将 MIN 和 TIME 设置为 0 */new_cfg.c_cc[VTIME] = 0;new_cfg.c_cc[VMIN] = 0;/* 清空缓冲区 */if (0 > tcflush(fd, TCIOFLUSH)) {fprintf(stderr, "tcflush error: %s\n", strerror(errno));return -1;}/* 写入配置、使配置生效 */if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));return -1;}/* 配置 OK 退出 */return 0; }/***
--dev=/dev/ttyUSB0
--brate=115200
--dbit=8
--parity=N
--sbit=1
--type=read
***/
/**打印帮助信息**/
static void show_help(const char *app) {printf("Usage: %s [选项]\n""\n 必选选项:\n"" --dev=DEVICE 指定串口终端设备名称, 譬如--dev=/dev/ttyUSB0\n"" --type=TYPE 指定操作类型, 读串口还是写串口, 譬如--type=read(read 表示读、write 表示写、readwrite表示读写、其它值无效)\n""\n 可选选项:\n"" --brate=SPEED 指定串口波特率, 譬如--brate=115200\n"" --dbit=SIZE 指定串口数据位个数, 譬如--dbit=8(可取值为: 5/6/7/8)\n"" --parity=PARITY 指定串口奇偶校验方式, 譬如--parity=N(N 表示无校验、O 表示奇校验、E 表示偶校验)\n"" --sbit=SIZE 指定串口停止位个数, 譬如--sbit=1(可取值为: 1/2)\n"" --help 查看本程序使用帮助信息\n\n", app);
}/**
** 信号处理函数,当串口有数据可读时,会跳转到该函数执行
**/
static void io_handler(int sig, siginfo_t *info, void *context) {unsigned char buf[10] = {0};int ret;int n;if(SIGRTMIN != sig)return;/* 判断串口是否有数据可读 */if (POLL_IN == info->si_code) {ret = read(fd, buf, 8); //一次最多读 8 个字节数据printf("[ ");for (n = 0; n < ret; n++)printf("0x%hhx ", buf[n]);printf("]\n");} 
}/**
** 异步 I/O 初始化函数
**/
static void async_io_init(void) {struct sigaction sigatn;int flag;/* 使能异步 I/O */flag = fcntl(fd, F_GETFL); //使能串口的异步 I/O 功能flag |= O_ASYNC;fcntl(fd, F_SETFL, flag);/* 设置异步 I/O 的所有者 */fcntl(fd, F_SETOWN, getpid());/* 指定实时信号 SIGRTMIN 作为异步 I/O 通知信号 */fcntl(fd, F_SETSIG, SIGRTMIN);/* 为实时信号 SIGRTMIN 注册信号处理函数 */sigatn.sa_sigaction = io_handler; //当串口有数据可读时,会跳转到 io_handler 函数sigatn.sa_flags = SA_SIGINFO;sigemptyset(&sigatn.sa_mask);sigaction(SIGRTMIN, &sigatn, NULL);
}int main(int argc, char *argv[])
{uart_cfg_t cfg = {0};char *device = NULL;int rw_flag = -1;unsigned char w_buf[10] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; //通过串口发送出去的数据int n;printf("解析出参数 \n");/* 解析出参数 */for (n = 1; n < argc; n++) {if (!strncmp("--dev=", argv[n], 6))device = &argv[n][6];else if (!strncmp("--brate=", argv[n], 8))cfg.baudrate = atoi(&argv[n][8]);else if (!strncmp("--dbit=", argv[n], 7))cfg.dbit = atoi(&argv[n][7]);else if (!strncmp("--parity=", argv[n], 9))cfg.parity = argv[n][9];else if (!strncmp("--sbit=", argv[n], 7))cfg.sbit = atoi(&argv[n][7]);else if (!strncmp("--type=", argv[n], 7)) {if (!strcmp("read", &argv[n][7]))rw_flag = 0; //读if (!strcmp("write", &argv[n][7]))rw_flag = 1; //写if (!strcmp("readwrite", &argv[n][7]))rw_flag = 2; //读写}else if (!strcmp("--help", argv[n])) {show_help(argv[0]); //打印帮助信息exit(EXIT_SUCCESS);}}if (NULL == device || -1 == rw_flag) {fprintf(stderr, "Error: the device and read|write type must be set!\n");show_help(argv[0]);exit(EXIT_FAILURE);}printf("串口初始化 \n");/* 串口初始化 */if (uart_init(device))exit(EXIT_FAILURE);printf("串口配置 \n");/* 串口配置 */if (uart_cfg(&cfg)) {tcsetattr(fd, TCSANOW, &old_cfg); //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}printf("读|写串口  \n");/* 读|写串口 */switch (rw_flag) {case 0: //读串口数据async_io_init(); //我们使用异步 I/O 方式读取串口的数据,调用该函数去初始化串口的异步 I/Ofor ( ; ; )sleep(1); //进入休眠、等待有数据可读,有数据可读之后就会跳转到 io_handler()函数break;case 1: //向串口写入数据for ( ; ; ) { //循环向串口写入数据write(fd, w_buf, 8); //一次向串口写入 8 个字节sleep(1); //间隔 1 秒钟}break;case 2: //向串口写入数据async_io_init(); //我们使用异步 I/O 方式读取串口的数据,调用该函数去初始化串口的异步 I/Ofor ( ; ; ) { //循环向串口写入数据write(fd, w_buf, 8); //一次向串口写入 8 个字节sleep(2); //间隔 1 秒钟}break;}/* 退出 */tcsetattr(fd, TCSANOW, &old_cfg); //恢复到之前的配置close(fd);exit(EXIT_SUCCESS);
}

上述是uart收发源码。

在这次例程中我们使用cmake来编译,如果有不熟悉cmake的可自行查阅相关资料,或作者的另一篇文章:linux开发工具-之-CMake简单例程[初见]

在uart.c文件的同级目录下创建文件“CMakeLists.txt”
编写cmake,在“CMakeLists.txt”中编写一下内容:

project(UART)
add_executable(uart ./uart.c)

四、编译

首先:cmake编译:

cmake ./

运行cmake后,在同级目录下会得到一个Makefile文件
在这里插入图片描述
其次再进行Makefile编译,Makefile编译直接运行make即可

make

在这里插入图片描述
即可得到执行文件uart

五、运行验证

查看文件类型
file uart

在这里插入图片描述
作者想串口模块插入ubuntu PC后得到的设备是 “/dev/ttyUSB0”
可通过“ls -a /dev/tty*” 命令查看,如下:
在这里插入图片描述
在这里插入图片描述
运行可执行程序uart;

查看uart的帮助信息:

./uart --help

查看帮助如下:
在这里插入图片描述

进行读写运行测试命令:

sudo ./uart --dev=/dev/ttyUSB0 --type=readwrite

效果如下图:
在这里插入图片描述
至此,uart的读写测试完成。
此例程只是简单实现读写,在实际运用中会比这复杂很多。

cmake相关文章,作者后续会持续更新,尽请关注。

相关内容

热门资讯

感激作文600字 感激作文600字三篇  无论是身处学校还是步入社会,大家对作文都再熟悉不过了吧,作文一定要做到主题集...
团圆饭作文500字 团圆饭作文500字8篇  新年到,祝福满满,笑口常开,团圆温暖,共享繁华!以下是小编整理的团圆饭作文...
夏日里的春天优秀作文 夏日里的春天优秀作文  德意志的夏天充满着激情,跌宕的风,狂热的雨充塞整个安静的德国,风起云涌的土地...
不能说的秘密歌词 不能说的秘密歌词  在日常生活或是工作学习中,大家一定都或多或少地了解过一些关于歌曲的知识,下面是小...
妈妈的爱作文 妈妈的爱作文200字(通用11篇)  在日常的学习、工作、生活中,大家一定都接触过作文吧,作文是人们...
单项奖学金申请书 单项奖学金申请书(精选13篇)  现今社会公众的追求意识不断提升,申请书与我们的关系越来越密切,申请...
不要轻易说不高中作文800字 不要轻易说不高中作文800字  什么是“不”?那是对事物可能性的一种否定。当你说出“不”字的时候,你...
作文 蝴蝶,飞呀 作文 蝴蝶,飞呀蝴蝶,飞呀    ‘梦是蝴蝶的翅膀,年轻是飞翔的天堂、放开风筝和长线,把爱画在岁月的...
爱心煎蛋作文 爱心煎蛋作文6篇  在日常学习、工作抑或是生活中,大家或多或少都会接触过作文吧,作文要求篇章结构完整...
夸夸同学的闪光点 夸夸同学的闪光点700字  我的同学梁鹤迎长着一头乌黑的秀发,两条淡淡的眉毛,一双眼睛炯炯有神。她文...
难忘的青春作文 难忘的青春作文(精选20篇)  无论在学习、工作或是生活中,大家都经常看到作文的身影吧,作文根据写作...
我学会了自强的作文300字优... 我学会了自强的作文300字 第一篇我独自走在河边,望着天空。不知怎么的我想哭。是啊!一转眼十几年过去...
书山有路勤为径,学海无涯苦作... 书山有路勤为径,学海无涯苦作舟作文500字  “书山有路勤为径,学海无涯苦作舟。”这是爸爸经常拿来鼓...
白杨树的作文 关于白杨树的作文10篇  在我们平凡的日常里,大家都经常接触到作文吧,作文是人们以书面形式表情达意的...
我爱读书作文500字 我爱读书作文500字4篇  在日常生活或是工作学习中,大家都写过作文吧,借助作文人们可以实现文化交流...
冬日里的阳光作文 冬日里的阳光作文【精选3篇】  在平日的学习、工作和生活里,大家都经常看到作文的身影吧,通过作文可以...
我心目中的龙小学生作文 我心目中的龙小学生作文五篇  1我心目中的龙小学生作文  我小时候看过《西游记》,在里面有一个东海龙...
点赞的话题作文 关于点赞的话题作文  在日常学习、工作和生活中,大家对作文都不陌生吧,作文是一种言语活动,具有高度的...
我眼中的缤纷世界作文 我眼中的缤纷世界作文(精选34篇)  在平时的学习、工作或生活中,大家都不可避免地会接触到作文吧,作...
我的亲弟弟作文600字 我的亲弟弟作文600字  他,有一双水汪汪的大眼睛,一个翘翘的小皱鼻子,一张小嘴巴总是被那洁白的牙齿...