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

news2024/12/26 11:30:05

目录

 

概述

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

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

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

相关文章

Humanoid-Gym 开源人形机器人端到端强化学习训练框架!星动纪元联合清华大学、上海期智研究院发布!

系列文章目录 前言 Humanoid-Gym: Reinforcement Learning for Humanoid Robot with Zero-Shot Sim2Real Transfer GitHub Repository: GitHub - roboterax/humanoid-gym: Humanoid-Gym: Reinforcement Learning for Humanoid Robot with Zero-Shot Sim2Real Transfer 一、介…

挑战杯 基于深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

unity学习(51)——服务器三次注册限制以及数据库化角色信息6--完结

同一账号只写第一次&#xff0c;不同账号第一次爆炸 &#xff0c;就因为下面部分得到逻辑有问题 修改后的代码如下&#xff1a;1.成功完成角色注册信息的数据库化记录。2.每个账号上限3个角色。3.角色是可以重名的&#xff0c;但是角色的id不会重名。 internal class UserCach…

最简k8s部署(AWS Load Balancer Controller使用)

问题 我需要在k8s集群里面部署springboot服务&#xff0c;通过k8s ingress访问集群内部的springboot服务&#xff0c;应该怎么做&#xff1f; 这里假设已经准备好k8s集群&#xff0c;而且也准备好springboot服务的运行镜像了。这里我们将精力放在k8s服务编排上面。 一图胜千言…

原生JavaScript,根据后端返回JSON动态【动态列头、动态数据】生成表格数据

前期准备&#xff1a; JQ下载地址&#xff1a; https://jquery.com/ <!DOCTYPE html> <html><head><meta charset"utf-8"><title>JSON动态生成表格数据,动态列头拼接</title><style>table {width: 800px;text-align: cen…

MySQL安装与卸载

安装 1). 双击官方下来的安装包文件 2). 根据安装提示进行安装(全部默认就可以) 安装MySQL的相关组件&#xff0c;这个过程可能需要耗时几分钟&#xff0c;耐心等待。 输入MySQL中root用户的密码,一定记得记住该密码 配置 安装好MySQL之后&#xff0c;还需要配置环境变量&am…

释机器学习中的召回率、精确率、准确率

精确率和召回率又被叫做查准率和查全率&#xff0c;可以通过P-R图进行表示 如何理解P-R(精确率-召回率)曲线呢&#xff1f;或者说这些曲线是根据什么变化呢&#xff1f; 以逻辑回归举例&#xff0c;其输出值是0-1之间的数字。因此&#xff0c;如果我们想要判断用户的好坏&…

Qt 自绘进度条 QProgressBar使用

文章目录 效果图控件介绍绘制函数总结 效果图 控件介绍 QProgressBar是Qt框架中提供的一个控件&#xff0c;用于在界面上显示任务的进度。它通常用于向用户展示一个操作完成的百分比&#xff0c;比如文件复制、数据加载等操作的进度。 QProgressBar的主要特性&#xff1a; 范…

linux应用程序需要编写的脚本

每一个程序都按照下面的要求进行脚本编写 多个应用之间联合安装采用编写外围脚本&#xff0c;依次调用多个应用的脚本的方式实现

【elasticsearch】ES的JAVA工具类完整版(待完成...)

springboot 的 elasticsearch 版本: 7.15.2 前情提要: 1.首先要理解 elasticsearch 对于【数据类型】很严格,如果字段类型不规范,在 检索/排序/聚合 时候类型不正确就会出现报错或者查不到数据的问题。所以在一般String类型插入结构如下: 这样的结构,不仅可以支持分词查…

css 背景图片居中显示

background 简写 background: #ffffff url(https://profile-avatar.csdnimg.cn/b9abdd57de464582860bf8ade52373b6_misnice.jpg) center center / 100% no-repeat;效果如图&#xff1a;

ubuntu 卸载miniconda3

一开始安装路径错了&#xff0c;需要重新安一次&#xff0c;就一起记录了。 前提是这种方式安装&#xff1a; ubuntu安装miniconda3管理python版本-CSDN博客 删除Miniconda的安装目录 这目录就是你选择安装的时候指定的&#xff0c;如果记不得了,可以这样查看 which conda 这…

docker拉取镜像失败的解决方案大全

更换国内源 创建或修改 /etc/docker/daemon.json 文件&#xff0c;修改&#xff1a; {"registry-mirrors" : ["https://registry.docker-cn.com","http://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn","https:…

【生态适配】亚信安慧AntDB数据库与OpenCloudOS、TencentOS Server五款产品完成兼容互认

日前&#xff0c;亚信安慧AntDB数据库与OpenCloudOS8、OpenCloudOS9、TencentOS Server 2、TencentOS Server 3、TencentOS Server 4五款操作系统完成兼容互认。经过严格测试&#xff0c;亚信安慧AntDB数据库与这五款操作系统兼容良好&#xff0c;整体运行稳定。 图1&#xff1…

全面认识计算机

目录 一、计算机的发展史 1. 电子管计算机时代 2. 晶体管计算机时代 3. 小、中规模集成电路计算机时代 4. 大、超大规模集成电路计算机时代 二、计算机硬件组成 1. 输入设备 2. 输出设备 3. 存储器 4. 运算器 5. 控制器 三、计算机硬件间的连接 四、计算机系统的结…

0201安装报错-hbase-大数据学习

1 基础环境简介 linux系统&#xff1a;centos&#xff0c;前置安装&#xff1a;jdk、hadoop、zookeeper&#xff0c;版本如下 软件版本描述centos7linux系统发行版jdk1.8java开发工具集hadoop2.10.0大数据生态基础组件zookeeper3.5.7分布式应用程序协调服务hbase2.4.11分布式…

Pytorch学习 day07(神经网络基本骨架的搭建、2D卷积操作、2D卷积层)

神经网络基本骨架的搭建 Module&#xff1a;给所有的神经网络提供一个基本的骨架&#xff0c;所有神经网络都需要继承Module&#xff0c;并定义_ _ init _ _方法、 forward() 方法在_ _ init _ _方法中定义&#xff0c;卷积层的具体变换&#xff0c;在forward() 方法中定义&am…

Java设计模式:适配器模式的三种形式(五)

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 适配器模式用于将一个类的接口转换为客户端所期望的另一个接口&#xff0c;以实现不兼容接口之间的协作。它像电器插头转换器一样…

单调栈(例题+解析)

1、应用场景 找出一个数的左面离概述最近的且小于该数的数&#xff08;同理右面也可以&#xff09; 例如&#xff1a; 数组a[i] 3 4 2 7 5 答案&#xff1a; -1 3 -1 2 2 2、如何实现找到规律 暴力写法&#xff1a; for(int i0;i<n;i) {for(int ji-1;j>0;j--){i…

金融数据采集与风险管理:Open-Spider工具的应用与实践

一、项目介绍 在当今快速发展的金融行业中&#xff0c;新的金融产品和服务层出不穷&#xff0c;为银行业务带来了巨大的机遇和挑战。为了帮助银行员工更好地应对这些挑战&#xff0c;我们曾成功实施了一个创新的项目&#xff0c;该项目采用了先进的爬虫技术&#xff0c;通过ope…