Linux环境下使用线程方式操作UART读写功能

目录

 

概述

1 Linux环境下UART设备

2 轮询方式操作UART功能实现

2.1 打开串口函数:usr_serial_open

2.2 关闭串口函数: usr_serial_close

2.3 发送数据函数: usr_serial_sendbytes

2.4 接收数据函数: thread_uart_readbytes

3 完整代码

3.1 usr_serial.c 文件内容

3.1 usr_serial.h 文件内容

4 编写应用代码

4.1 使用接口

4.2 编写应用程序Makefile

5 测试使用信号量实现串口数据的发送和接收功能


源代码下载地址: Linux环境下使用线程方式操作UART读写功能资源-CSDN文库

概述

本文介绍Linux环境下使用线程方式操作UART的方法,实现了串口打开,关闭,发送数据,接收数据功能,还编写测试代码,验证该功能。

1 Linux环境下UART设备

在linux环境下,UART作为一个终端设备存在,可使用命令, 系统会罗列出该目录下所有的device,其中以tty开头的设备为终端设备。串口也是这些设备之一。

ls /dev/ -l

执行该命令后,可以看见许多以tty开头的设备:

user根据板卡的信息,找到对应的端口,然后才能使用这些串口,笔者使用是基于iMX6ull芯片的板卡,板卡上COM1被用于调试终端,COM3可作为用户终端。

2 轮询方式操作UART功能实现

2.1 打开串口函数:usr_serial_open

函数参数

参数描述
port终端设备: /dev/tty0
baudrate波特率: 1200/2400/4800 ... /115200
databit数据bit位: /5/6/7/8
stopbit停止位:"1" / "1.5" / "2"
parity奇偶位使能: 'N' / 'E' / 'O'

函数实现方法:

代码 43行: 打开端口

代码 49行: 保存termios数据结构中,旧的参数

代码 51行:设置当前用户参数

源代码:

int usr_serial_open( char *port, unsigned int baudrate, unsigned int databit, const char *stopbit, char parity)
{int err;fd = open (port, O_RDWR | O_NOCTTY | O_NDELAY);if (-1 == fd) {fprintf(stderr, "cannot open port %s\n", port);return (-1);}tcgetattr (fd, &termios_old);       /* save the form termios value */err = set_portattr (baudrate, databit, stopbit, parity);if ( err ) {fprintf ( stderr, "\nport %s cannot set baudrate at %d\n",port, baudrate);}usr_baudrate = baudrate;return fd;
}

2.2 关闭串口函数: usr_serial_close

函数实现方法:

代码 64行: 恢复termios default参数

代码 65行:关闭fd端口

void usr_serial_close( void )
{/* flush output data before close and restore old attribute */tcsetattr(fd, TCSADRAIN, &termios_old);close(fd);
}

2.3 发送数据函数: usr_serial_sendbytes

函数参数

参数描述
*data存贮数据的数组
datalength发送的数据长度

函数实现方法:

代码 72行: 使用write函数发送数据

源代码:

unsigned int usr_serial_sendbytes (void * data, unsigned int datalength)
{unsigned int total_len = 0;total_len = write(fd, data, datalength);return (total_len);
}

2.4 接收数据函数: thread_uart_readbytes

函数参数

参数描述
*arg线程函数传入的参数

函数实现方法:

代码 39行: 使用read函数写数据

源代码

void *thread_uart_readbytes(void *arg) 
{int fd = *(int *)arg;char buf[128];int n;while (1) {// 读取串口数据n = read(fd, buf, sizeof(buf));if (n > 0) {printf("Received data: %.*s\n", n, buf);}}
}

3 完整代码

代码文件命名为usr_serial, 包含两个文件

usr_serial.c
usr_serial.h

3.1 usr_serial.c 文件内容

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : 01_usr_serial.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : linux 串口应用程序接口
其他       : 无
日志       : 初版V1.0 2024/03/01***************************************************************/
#include "usr_serial.h"/* Private define ------------------------------------------------------------*/
#define TIMEOUT_SEC(buflen,baud)    (buflen*20/baud+2)
#define TIMEOUT_USEC                 0#define CH_TO_WAIT 5
#define CH_BITS 11/* Private variables ---------------------------------------------------------*/
static unsigned int fd;  static struct timeval tv_timeout;
static struct termios termios_old;
static struct termios termios_new;static fd_set fs_read;
static unsigned int usr_baudrate;/* Private function prototypes -----------------------------------------------*/
static speed_t baudrate_to_Bxx (unsigned int baudrate);
static void set_data_bit (unsigned int databit);
static unsigned int set_portattr ( unsigned int baudrate,unsigned int databit, const char *stopbit,char parity);int usr_serial_open( char *port, unsigned int baudrate, unsigned int databit, const char *stopbit, char parity)
{int err;fd = open (port, O_RDWR | O_NOCTTY | O_NDELAY);if (-1 == fd) {fprintf(stderr, "cannot open port %s\n", port);return (-1);}tcgetattr (fd, &termios_old);       /* save the form termios value */err = set_portattr (baudrate, databit, stopbit, parity);if ( err ) {fprintf ( stderr, "\nport %s cannot set baudrate at %d\n",port, baudrate);}usr_baudrate = baudrate;return fd;
}void usr_serial_close( void )
{/* flush output data before close and restore old attribute */tcsetattr(fd, TCSADRAIN, &termios_old);close(fd);
}unsigned int usr_serial_sendbytes (void * data, unsigned int datalength)
{unsigned int total_len = 0;total_len = write(fd, data, datalength);return (total_len);
}int usr_serial_readbytes (void *data, unsigned int datalength)
{unsigned int total_len = 0;total_len = read(fd, data, datalength);if (total_len > 0) {printf("Receive %d bytes: %.*s\n", total_len, (char*)data);}return (total_len);
}unsigned int usr_serial_readinterrupt (void *data, unsigned int datalength)
{int total_len = 0;/*** caculate the time of 5 characters and get the maxim* with 3ms and 5 ch's time*/tv_timeout.tv_sec = 0;tv_timeout.tv_usec = ( (CH_TO_WAIT * CH_BITS) * (1000000/usr_baudrate));while(1){FD_ZERO (&fs_read);FD_SET (fd, &fs_read);select (fd + 1, &fs_read, NULL, NULL, &tv_timeout);total_len = read(fd, data, datalength);if (total_len > 0) {printf("Receive %d bytes: %.*s\n", total_len, (char*)data);return total_len;}}return total_len;}static void set_data_bit (unsigned int databit)
{termios_new.c_cflag &= ~CSIZE;switch (databit) {default:case 8:termios_new.c_cflag |= CS8;break;case 7:termios_new.c_cflag |= CS7;break;case 6:termios_new.c_cflag |= CS6;break;case 5:termios_new.c_cflag |= CS5;break;}
}static void set_stopbit (const char *stopbit)
{if (0 == strcmp (stopbit, "1")) {termios_new.c_cflag &= ~CSTOPB;    /* 1 stop bit */}else if (0 == strcmp (stopbit, "1.5")) {termios_new.c_cflag &= ~CSTOPB;     /* 1.5 stop bits */}else if (0 == strcmp (stopbit, "2")) {termios_new.c_cflag |= CSTOPB;       /* 2 stop bits */}else {termios_new.c_cflag &= ~CSTOPB;     /* 1 stop bit */}
}static void set_parity (char parity)
{switch (parity) {case 'N':                  /* no parity check */termios_new.c_cflag &= ~PARENB;break;case 'E':                  /* even */termios_new.c_cflag |= PARENB;termios_new.c_cflag &= ~PARODD;break;case 'O':                  /* odd */termios_new.c_cflag |= PARENB;termios_new.c_cflag |= ~PARODD;break;default:                   /* no parity check */termios_new.c_cflag &= ~PARENB;break;}
}static speed_t baudrate_to_Bxx (unsigned int baudrate)
{switch (baudrate) {case 0:return (B0);case 50:return (B50);case 75:return (B75);case 110:return (B110);case 134:return (B134);case 150:return (B150);case 200:return (B200);case 300:return (B300);case 600:return (B600);case 1200:return (B1200);case 2400:return (B2400);case 9600:return (B9600);case 19200:return (B19200);case 38400:return (B38400);case 57600:return (B57600);case 115200:return (B115200);default:return (B9600);}
}static void set_baudrate (unsigned int baudrate)
{speed_t speed;speed = baudrate_to_Bxx (baudrate);  /* set baudrate */cfsetispeed(&termios_new, speed);    // set input speedcfsetospeed(&termios_new, speed);    // set output speed
}static unsigned int set_portattr ( unsigned int baudrate,  // 1200 2400 4800 9600 .. 115200unsigned int databit,   // 5, 6, 7, 8const char *stopbit,    //  "1", "1.5", "2"char parity)            // N(o), O(dd), E(ven)
{bzero(&termios_new, sizeof (termios_new));cfmakeraw (&termios_new);set_baudrate (baudrate);termios_new.c_cflag |= CLOCAL | CREAD;   /* | CRTSCTS */set_data_bit (databit);set_parity (parity);set_stopbit (stopbit);termios_new.c_cc[VTIME] = 1;            /* unit: 1/10 second. */termios_new.c_cc[VMIN]  = 255;          /* minimal characters for reading */return (tcsetattr (fd, TCSANOW, &termios_new));
}/* End of this file */

3.1 usr_serial.h 文件内容

#ifndef __USR_SERIAL_H
#define __USR_SERIAL_H#include <termios.h>            /* tcgetattr, tcsetattr */
#include <stdio.h>              /* perror, printf, puts, fprintf, fputs */
#include <unistd.h>             /* read, write, close */
#include <fcntl.h>              /* open */
#include <sys/signal.h>
#include <sys/types.h>
#include <string.h>             /* bzero, memcpy */
#include <limits.h>             /* CHAR_MAX */#ifdef __cplusplus
extern "C" {
#endifint usr_serial_open( char *port, unsigned int baudrate, unsigned int databit, const char *stopbit, char parity);
void usr_serial_close( void );unsigned int usr_serial_sendbytes (void * data, unsigned int datalength);
int usr_serial_readbytes (void *data, unsigned int datalength);
unsigned int usr_serial_readinterrupt (void *data, unsigned int datalength);#ifdef __cplusplus
}
#endif#endif /* __USR_SERIAL_H */

4 编写应用代码

4.1 使用接口

代码实现功能介绍:

代码 56行:初始化串口设备,设置baud,数据位,停止位等参数

代码 63行:创建线程

代码 72行:向串口写数据

源代码如下:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     :  test_serial.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : 使用线程方式读取串口数据
其他       : 无
日志       : 初版V1.0 2024/03/04***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "usr_serial.h"#define SERILA_PORT   "/dev/ttymxc2"void *thread_uart_readbytes(void *arg) 
{int fd = *(int *)arg;char buf[128];int n;while (1) {// 读取串口数据n = read(fd, buf, sizeof(buf));if (n > 0) {printf("Received data: %.*s\n", n, buf);}}
}int main()
{pthread_t tid;char buf[255];int n;int fd,err;void *tret;// 打开串口设备fd = usr_serial_open(SERILA_PORT, 115200, 8, "1", 'N');if (fd == -1) {printf("open err\n");exit(1);}// 创建读数据线程if (pthread_create(&tid, NULL, thread_uart_readbytes, &fd) != 0) {perror("pthread_create error \n");return -1;}while (1){// 向串口发送数据strcpy(buf, "I am from iMX.6ULL board, hello world! \r\n");n = usr_serial_sendbytes(buf, strlen(buf));if (n < 0) {perror("write failed\n");}sleep(1);}err = pthread_join(tid, &tret);if (err) {fprintf(stderr, "pthread_join error: %s\n", strerror(err));exit(-1);}// 关闭串口设备usr_serial_close();printf("close uart\n");return 0;
}

4.2 编写应用程序Makefile

代码实现功能介绍:

代码 2行:编译器地址

代码 3行:linux内核地址

代码 3行:链接的.o文件名

代码 6行:生成可执行型文件

源代码

CFLAGS= -Wall -lpthread -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-striptest_serial: test_serial.o usr_serial.o$(CC) $(CFLAGS) -o test_serial test_serial.o usr_serial.o$(STRIP) -s test_serialclean:rm -f test_serial test_serial.o usr_serial.o

5 测试使用信号量实现串口数据的发送和接收功能

       使用Make命令编译代码,然后将生成的可执行性文件copy到NFS的共享目录下,然后在板卡中执行。

在代码中,定义要发送的数据如下:

 strcpy(buf, "I am from iMX.6ULL board, hello world! \r\n");

PC端,使用串口调试助手接收数据,详细信息如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/731132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

问题解决 | vscode无法连接服务器而ssh和sftp可以

解决步骤 进入家目录删除.vscode-server rm -rf .vscode-server 然后再次用vscode连接服务器时&#xff0c;会重新安装&#xff0c;这时可能报出一些缺少依赖的错 需要联系管理员安装相关依赖&#xff0c;比如 sudo apt-get install libstdc6 至此问题解决

Go语言框架路由Controller控制器设计思路gin路由根据控制器目录分层生成路由地址

Controller设计好处 框架设计用controller分请求路由层级&#xff0c;应用从app目录开始对应请求url路由地址&#xff0c;这样设计师方便开发时候通过请求地址层级快速定位接口方法对应的代码位置。 例如api接口请求路径为&#xff1a;​​http://localhost:8110/​​busines…

部署 LVS(nginx)+keepalived高可用负载均衡集群

目录 一、集群的概述 1、什么是集群 2、普通集群与负载均衡集群 2.1 普通集群&#xff08;Regular Cluster&#xff09; 2.2 负载均衡集群&#xff08;Load Balancing Cluster&#xff09; 2.3 高可用集群&#xff08;High Availability Cluster&#xff09; 2.4 区别 …

【LeetCode 算法专题突破】---二分查找(⭐⭐⭐)

前言 我在算法题目的海洋中畅游已久&#xff0c;也曾在算法竞赛中荣获佳绩。然而&#xff0c;我发现自己对于算法的学习&#xff0c;还缺乏一个系统性的总结和归类。尽管我已经涉猎过不少算法类型&#xff0c;但心中仍旧觉得有所欠缺&#xff0c;未能形成完整的算法体系。 因…

服务器又被挖矿记录

写在前面 23年11月的时候我写过一篇记录服务器被挖矿的情况&#xff0c;点我查看。当时是在桌面看到了bash进程CPU占用异常发现了服务器被挖矿。 而过了几个月没想到又被攻击&#xff0c;这次比上次攻击手段要更高明点&#xff0c;在这记录下吧。 发现过程 服务器用的是4090…

贪心算法(greedy algorithm,又称贪婪算法)详解(附例题)

目录 基本思想一&#xff09;概念二&#xff09;找出全局最优解的要求三&#xff09;求解时应考虑的问题四&#xff09;基本步骤五&#xff09;贪心策略选择六&#xff09;实际应用 1.零钱找回问题2.背包问题3.哈夫曼编码4.单源路径中的Djikstra算法5.最小生成树Prim算法 基本…

TCP包头

TCP包头: 1.序号:发送端发送数据包的编号 2.确认号:已经确认接收到的数据的编号(只有当ACK为1时,确认号才有用) TCP为什么安全可靠: 1.在通信前建立三次握手连接 SYN SYNACK ACK SYN是TCP包头的一个字段 tcp.port 端口号 抓包数据 2.在通信过程中通过序…

使用Apache Kafka的Golang实践指南

您是否在寻找构建可扩展、高性能应用程序的方法&#xff0c;这些应用程序可以实时处理流数据&#xff1f;如果是的话&#xff0c;结合使用Apache Kafka和Golang是一个很好的选择。Golang的轻量级线程非常适合编写类似Kafka生产者和消费者的并发网络应用程序。它的内置并发原语&…

探索HTTP协议:网络通信的基石

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【CSP试题回顾】202109-2-非零段划分

CSP-202109-2-非零段划分 关键点&#xff1a;差分数组 详见&#xff1a;【CSP考点回顾】差分数组 时间复杂度分析 使用差分数组的优势在于&#xff0c;它将问题转化为了在一次遍历中识别并利用关键变化点&#xff08;波峰和波谷&#xff09;&#xff0c;从而避免了对每个可能…

Mysql中的MVCC

”真正学会&#xff0c;如你般自由~“ MVCC机制简介 MVCC(Multi-Version-Concurrency-Control)多版本并发控制&#xff0c;MVCC 是一种并发控制的方法&#xff0c;一般在数据库管理系统中&#xff0c;实现对数据库的并发访问&#xff1b;在编程中实现事务内存。 取自 MVCC存在被…

某准网招聘接口逆向之WebPack扣取

​​​​​逆向网址 aHR0cHM6Ly93d3cua2Fuemh1bi5jb20v 逆向链接 aHR0cHM6Ly93d3cua2Fuemh1bi5jb20vc2VhcmNoP3BhZ2VOdW09MSZxdWVyeT1weXRob24mdHlwZT01 逆向接口 aHR0cHM6Ly93d3cua2Fuemh1bi5jb20vYXBpX3RvL3NlYXJjaC9qb2IuanNvbg 逆向过程 请求方式&#xff1a;GET 参数构成…

Clickhouse表引擎介绍

作者&#xff1a;俊达 1 引擎分类 ClickHouse表引擎一共分为四个系列&#xff0c;分别是Log、MergeTree、Integration、Special。其中包含了两种特殊的表引擎Replicated、Distributed&#xff0c;功能上与其他表引擎正交&#xff0c;根据场景组合使用。 2 Log系列 Log系列…

k8s-生产级的k8s高可用(1) 24

高可用集群 实验至少需要三个master&#xff08;控制节点&#xff09;&#xff0c;一个可以使外部可以访问到master的load balancer&#xff08;负载均衡&#xff09;以及一个或多个外部节点worker&#xff08;也要部署高可用&#xff09;。 再克隆三台主机 清理并重启 配置两…

LayerNorm的图是不是画错了

这是网上一张很流行的说明几个 Normalization 区别的图 这图出自Kaiming的文章 Group Norm 但是他这个 Layer Norm 的图是不是画错了? 我大四写毕设的时候就想问&#x1f923;&#x1f923;&#x1f923; 这都几年过去了 我觉得图应该是这样画的&#xff0c;相同颜色的区域…

VSCode搭建ARM开发环境

为了构建Cortex M系列单片机免费开源的开发环境&#xff0c;网络上了解来看VSCODEGCCJLINK是一套比较高效的组合方式&#xff0c;下面记录环境搭建的流程。 我这边的PC环境为 WIN7专业版64bit。 需要用到的工具 Visual Studio CodeSTM32CubemxARM GCC 交叉编译工具链&#x…

cadence 之 Allegro PCB封装 3D模型

Allegro PCB封装怎样赋3D模型 1、方式一 —— 设置器件高度 2、方式二 —— 指定STEP模型 2.1、Step 3D模型库 2.2、软件环境的设置和 STEP 模型库路径设置 D:\Cadence\Cadence_SPB_17.4-2019\share\local\pcb\step 2.3、指定STEP模型 即可打开 STEP 模型指定的对话框&…

【理解】STM32一键下载电路

1.MCUISP 串口软件一键下载设置&#xff1a; DTR 低电平复位&#xff0c;RTS 高电平进入boot load 串口下载 在ch340 芯片对应DTR 和RTS 输出电平与电脑软件设置的电平相反。 一键下载电路根据ch340 芯片对应引脚的控制信号完成对应功能 具体实现过程如下&#xff1a; 2.单…

VR数字化线上展馆降低企业投入成本和周期

VR云展会是一种全新的展览形式&#xff0c;它利用虚拟现实技术&#xff0c;将实体展览搬到线上&#xff0c;让观众可以在家中就能参观各种展览。这种新型的展览方式有许多亮点&#xff0c;下面就来详细介绍一下。 首先&#xff0c;VR云展会打破了地域限制。传统的实体展览通常只…

WPF 消息提示 类似toast方式

WPF里面的消息提示一般都是MessageBox.Show()&#xff0c;这种样式不是很好看&#xff0c;所以就想办法重新搞了一个类似弹出消息的功能。原理很简单&#xff0c;就是弹出一个新窗体&#xff0c;然后等几秒窗体自动关闭。 先上效果图&#xff1a; 新建一个MsgHelper.cs类&…