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 speed
    cfsetospeed(&termios_new, speed);    // set output speed
}

static unsigned int set_portattr ( unsigned int baudrate,  // 1200 2400 4800 9600 .. 115200
                                   unsigned int databit,   // 5, 6, 7, 8
                                   const 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" {
#endif

int 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-strip

test_serial: test_serial.o usr_serial.o
	$(CC) $(CFLAGS) -o test_serial test_serial.o usr_serial.o
	$(STRIP) -s test_serial

clean:
	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端,使用串口调试助手接收数据,详细信息如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

波卡 Alpha 计划启动,呼唤先锋创新者重新定义 Web3 开发

原文&#xff1a;https://polkadot.network/blog/the-polkadot-alpha-program-a-new-era-for-decentralized-building-collaboration/ 编译&#xff1a;OneBlock 区块链领域不断发展&#xff0c;随之而来的是发展和创新机会的增加。而最新里程碑是令人振奋的 Polkadot Alpha …

生态,带给用友新机会? | ToB产业观察

ITValue 近年来&#xff0c;不少厂商都将生态调整到战略级别&#xff0c;但做生态往往需要实打实、接地气的生态策略。 作者&#xff5c;杨丽 首发&#xff5c;钛媒体APP ITValue 3月初&#xff0c;用友召开了新一年的首场生态发布会&#xff0c;为接下来一年动作定调&#xff…

17、电源管理入门之Power supply子系统

目录 1. Power supply框架都做些什么 2. 相关数据结构和接口 2.1 数据结构 2.2 接口 3. 充电驱动 3.1 Charger Manager 3.2 Fuel Gauge 3.3 Charger IC 4. 怎样基于power supply class编写PSY driver 参考资料: 对于便携设备来说,电源管理更加的重要,因为电池电量…

数据库系统概念(第一周)

⚽前言 &#x1f3d0;四个基本概念 一、数据 定义 种类 特点 二、数据库 三、数据库管理系统&#xff08;DBMS&#xff09; 四、 数据库系统&#xff08;DBS&#xff09; &#x1f3c0;数据库系统和文件系统对比 文件系统的弊端 &#x1f94e;数据视图 数据抽象 …

jeecgboot 新建子模块 使用@EXCEL实现实现导入导出功能

一&#xff0c;用框架生成增删改查模块 二&#xff0c;在实体类entity 需要导入导出的字段上加上注解Excel 三&#xff0c;在controller类上继承jeecgboot通用controller JeecgController 并且在JeecgController里增加导出模板的方法 /*** 导出excel空模板** param req…

融资项目——网关微服务

1. 网关的路由转发功能 在前后端分离的项目中&#xff0c;网关服务可以将前端的相关请求转发到相应的后端微服务中。 2. 网关微服务的配置 首先需要创建一个网关微服务&#xff0c;并添加依赖。 <!-- 网关 --><dependency><groupId>org.springframework.cl…

相似矩阵及其对角化

目录 相似矩阵 矩阵的相似对角化 对称矩阵的相似对角化 相似矩阵 矩阵的相似对角化 对称矩阵的相似对角化

循序渐进丨MogDB 数据库新特性之SQL PATCH绑定执行计划

1 SQL PATCH 熟悉 Oracle 的DBA都知道&#xff0c;生产系统出现性能问题时&#xff0c;往往是SQL走错了执行计划&#xff0c;紧急情况下&#xff0c;无法及时修改应用代码&#xff0c;DBA可以采用多种方式针对于某类SQL进行执行计划绑定&#xff0c;比如SQL Profile、SPM、SQL …

鸿蒙实战多媒体运用:【音频组件】

音频组件用于实现音频相关的功能&#xff0c;包括音频播放&#xff0c;录制&#xff0c;音量管理和设备管理。 图 1 音频组件架构图 基本概念 采样 采样是指将连续时域上的模拟信号按照一定的时间间隔采样&#xff0c;获取到离散时域上离散信号的过程。 采样率 采样率为每…

macos m1 arm芯片 使用jpype报错 FileNotFoundError: [Errno 2] JVM DLL not found

startJVM(jpype.getDefaultJVMPath()) 报错 Traceback (most recent call last):File "/Users/thomas990p/PycharmProjects/tuya/volcano-biz-scripts/WenKongFa/FinalCode/java2python/CallJavaAPI.py", line 12, in <module>startJVM(jpype.getDefaultJVMPa…

寻找两个正序数组的中位数[困难]

优质博文IT-BLOG-CN 一、题目 给定两个大小分别为m和n的正序&#xff08;从小到大&#xff09;数组nums1和nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&…

【漏洞复现】帮管客 CRM jiliyu SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

用 Axios 提升前端异步请求的效率

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

代码随想录 -- 栈和队列

文章目录 栈实现队列描述题解 用队列实现栈描述题解 有效的括号描述题解 删除字符串中的所有相邻重复项描述题解 逆波兰表达式求值描述题解 滑动窗口最大值描述题解&#xff1a;暴力题解&#xff1a;单调队列 前 K 个高频元素描述题解 栈实现队列 题目链接 描述 使用两个栈实…

WPF开源的一款免费、开箱即用的翻译、OCR工具

前言 今天大姚给大家分享一款由WPF开源的、免费的&#xff08;MIT License&#xff09;、即开即用、即用即走的翻译、OCR工具&#xff1a;STranslate。 WPF介绍 WPF 是一个强大的桌面应用程序框架&#xff0c;用于构建具有丰富用户界面的 Windows 应用。它提供了灵活的布局、…

TCP包头、TCP为什么安全可靠、UDP和TCP的区别、http协议

我要成为嵌入式高手之3月8日Linux高编第十八天&#xff01;&#xff01; __________________________________________________ 学习笔记 TPC包头 1、序号 发送端发送数据包的编号 2、确认号 已经确认接收到的数据的编号&#xff0c;只有当ACK为1时&#xff0c;该位才有用 …

sentinel prometheus指标收集及资源规则正则表达式实现

sentinel 支持 prometheus 收集指标 实现原理 在 sentinel-extension 模块下&#xff0c;新增 sentinel-prometheus-metric-exporter 模块。依赖Prometheus 提供的 simpleclient 和 simpleclient_httpserver 来实现 exporter。 依赖 simpleclient 主要是为了实现自定义Collect…

关于手机是否支持h264的问题的解决方案

目录 现象 原理 修改内容 现象 开始以为是手机不支持h264的编码 。机器人chatgpt一通乱扯。 后来检查了下手机&#xff0c;明显是有h264嘛。 终于搞定&#xff0c;不枉凌晨三点起来思考 原理 WebRTC 默认使用的视频编码器是VP8和VP9&#xff0c;WebRTC内置了这两种编码器…

flink重温笔记(十二): flink 高级特性和新特性(1)——End-to-End Exactly-Once(端到端精确一致性语义)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第 12 天啦&#xff01;学习了 flink 高级特性和新特性之 End-to-End Exactly-Once&#xff08;端到端精确一致性语义&#xff09;&#xff0c;主要是解决大数据领域数据从数据源到数据落点的一致性&#xff0c;不会容易造成…

计算机组成原理之机器:存储器之辅助存储器

计算机组成原理之机器&#xff1a;存储器之辅助存储器 笔记来源&#xff1a;哈尔滨工业大学计算机组成原理&#xff08;哈工大刘宏伟&#xff09; Chapter3&#xff1a;存储器之辅助存储器 3.1 概述 3.2 磁记录原理 通不同方向电流时磁化方向不同&#xff0c;由此区分写入…