Linux环境下使用轮询方式操作UART

目录

概述

1 Linux环境下UART设备

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

2.1 打开串口函数:usr_serial_open

2.2 关闭串口函数: usr_serial_close

2.3 发送数据函数: usr_serial_sendbytes

2.4 接收数据函数: usr_serial_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'

函数实现方法:

代码 38行: 打开端口

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

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

源代码:

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);
    }
    
    return fd;
}

2.2 关闭串口函数: usr_serial_close

函数实现方法:

代码 58行: 恢复termios default参数

代码 59行:关闭fd端口

2.3 发送数据函数: usr_serial_sendbytes

函数参数

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

函数实现方法:

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

2.4 接收数据函数: usr_serial_readbytes

函数参数

参数描述
*data存贮数据的数组
datalength接收的数据长度

函数实现方法:

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

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

/* Private variables ---------------------------------------------------------*/
static unsigned int fd;  
static struct termios termios_old;
static struct termios termios_new;


/* 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);
    }
    
    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);
}

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.2 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);

#ifdef __cplusplus
}
#endif

#endif /* __USR_SERIAL_H */

4 编写测试代码

4.1 编写测试代码

代码实现功能介绍:

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

代码 48行:从串口读取数据

代码 55行:向串口写数据

源代码

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : 01_test_serial.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : 使用轮询方式读取串口数据
其他       : 无
日志       : 初版V1.0 2024/02/01

***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.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 "usr_serial.h"

#define SERILA_PORT   "/dev/ttymxc2"


int main()
{
    int fd;
    char buf[255];
    int n;
 
    // 打开串口设备
    fd = usr_serial_open(SERILA_PORT, 115200, 8, "1", 'N');
    if (fd == -1) {
        printf("open err\n");
        exit(1);
    }
    
    while (1)
    {
        // 读取串口数据
        n = usr_serial_readbytes(fd, buf, sizeof(buf));
        if (n > 0) {
            printf("Received: %.*s\n", n, buf);
        }
 
        // 向串口发送数据
        strcpy(buf, "I am from iMX.6ULL board, hello world! \r\n");
        n = usr_serial_sendbytes(fd, buf, strlen(buf));
        if (n < 0) {
            perror("write failed\n");
        }
        sleep(1);
    }
 
    // 关闭串口设备
    usr_serial_close();
    printf("close uart\n");
 
    return 0;
}

4.2 编写测试代码的Makefile

代码实现功能介绍:

代码 2行:编译器地址

代码 3行:linux内核地址

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

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

源代码

CFLAGS= -Wall -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

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

clean:
	rm -f 01_test_serial 01_test_serial.o usr_serial.o

5 测试轮询模式下串口数据的发送和接收功能

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

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

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

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

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

相关文章

【QT C++实践】Qt 项目中一个界面动态处理多张数据库中的表|附源码

一、前言 在之前那篇讲如何使用QT连接数据库时&#xff08;QT C实践|超详细数据库的连接和增删改查操作|附源码)&#xff0c;做了一个简单的对数据库进行增删改查的界面(如下&#xff09;。 但是存在一个问题就是&#xff1a;这个界面只是对一张表进行操作&#xff0c;但是我…

探索HTTP/2

文章目录 http/1.1http/2疑惑 探索1. 连接前言2. 帧结构2.1 帧类型 Type 3. 帧详情3.1 SETTINGS 帧3.2 WINDOW_UPDATE 帧3.3 PRIORITY 帧3.4 HEADERS 帧3.5 DATA 帧3.6 PING3.7 GOAWAY 帧3.8 RST_STREAM 帧3.9 PUSH_PROMISE 帧3.10 CONTINUATION 帧 你对http2了解多少&#xff…

BUUCTF--极客大挑战php

文章目录 1.网站备份文件www.zip2.下载后发现class.phpindex.phpflag.php 3.分析php代码绕过__wakeup方法变量权限为私有或保护python方法url方法 1.网站备份文件www.zip 2.下载后发现 class.php <?php include flag.php; error_reporting(0);class Name{private $usernam…

MYSQL5.7报1205 - Lock wait timeout exceeded; try restarting transaction

简介 今天使用navicate操作添加时&#xff0c;mysql报错误&#xff0c;错误如下 原因 这个问题的原因是在mysql中产生了事务A&#xff0c;执行了修改的语句&#xff0c;比如&#xff1a; update t1 set aget18 where id1;此时事务并未进行提交&#xff0c;事务B开始运行&am…

【C++精简版回顾】19.异常处理

1.throw抛出问题 int print(int a,int b) {if (b 0)throw b;return a / b; } 2.try与catch解决问题 try {print(2, 0); } catch (int b) {cout << "竟然是&#xff1a;"<<b<<endl; } 结果&#xff1a; 补充1&#xff1a;可以抛出字符串等 1.throw…

pytorch配置环境

1.查看cuda版本 nvidia-smi cuda version:12.3 2.下载torch 然后根据版本去查找对应的 torch下载代码 可查看这里&#xff1a;Previous PyTorch Versions | PyTorch 然后执行 conda install pytorch1.10.0 torchvision0.11.0 torchaudio0.10.0 cudatoolkit11.3 -c pytorch…

Python的文件操作

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路在脚下&#xff0c;勇往直前&#x…

使用nvidia-ml-py事实监控GPU状态

平时监控GPU状态最常用的是watch配合nvidia-smi指令&#xff0c;但有时可能不仅仅需要监控&#xff0c;还需要记录状态数据&#xff0c;比如GPU的显存变化以及利用率变化等等。本文提供了一个使用nvidia-ml-py包编写的简易Demo&#xff0c;该Demo能够实现简易版的nvidia-smi功能…

[云原生] K8s之pod控制器详解

Pod 是 Kubernetes 集群中能够被创建和管理的最小部署单元。所以需要有工具去操作和管理它们的生命周期,这里就需要用到控制器了。 Pod 控制器由 master 的 kube-controller-manager 组件提供&#xff0c;常见的此类控制器有 Replication Controller、ReplicaSet、Deployment、…

openssl3.2 - exp - 产生随机数

文章目录 openssl3.2 - exp - 产生随机数概述笔记END openssl3.2 - exp - 产生随机数 概述 要用到openssl产生的随机数, 查了资料. 如果用命令行产生随机数, 如下: openssl rand -hex -num 6 48bfd3a64f54单步跟进去, 看到主要就是调用了一个RAND_bytes(), 没其他了. 官方说…

美国站群服务器使用技巧与注意事项

美国站群服务器使用技巧与注意事项有哪些?RAKsmart小编为您整理发布美国站群服务器使用技巧与注意事项&#xff0c;希望对您有帮助。 美国站群服务器的使用技巧主要包括远程管理、灵活配置和备份还原&#xff0c;具体如下&#xff1a; 1. **远程管理**&#xff1a;用户可以通过…

Promisification、微任务

前提摘要 Promise 对象的构造器&#xff08;constructor&#xff09;语法如下&#xff1a; let promise new Promise(function(resolve, reject) { // executor }); 传递给 new Promise的函数被称为 executor&#xff0c;当 new Promise 被创建&#xff0c;executor 会自动…

本地部署websocket服务端并结合内网穿透实现固定公网地址连接

文章目录 1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功,暴露端口默认99995. 创建隧道映射内网端口6. 查看状态->在线隧道,复制所创建隧道的公网地址加端口号7. 以…

So you think you understand IP fragmentation?

文章目录 前言一、Why care?二、Prevention三、Well-understood?四、Introducing fragquiz五、A novel (?) algorithm六、Reader challenge七、traceroute八、ICMP参考资料 前言 本文来自&#xff1a;https://lwn.net/Articles/960913/ February 7, 2024This article was …

英福康INFICON真空计VGC012-103-401使用说明

英福康INFICON真空计VGC012-103-401使用说明

【C++ Primer Plus学习记录】break和continue语句

break和continue语句都使程序能够跳过部分代码。可以在switch语句或任何循环中使用break语句&#xff0c;使程序跳到switch或循环后面的语句处执行。continue语句用于循环中&#xff0c;让程序跳过循环体中余下的代码&#xff0c;并开始新一轮循环&#xff08;参见图6.4&#x…

Unity性能优化篇(八) 导入的模型网格优化设置

模型导入Unity后&#xff0c;可以选中这个模型&#xff0c;在Inspector窗口设置它的属性。下面说的都是可自定义选择优化的地方 Model选择卡: 1.在Model选项卡&#xff0c;启用Mesh Compression可以压缩模型&#xff0c;压缩程度越高&#xff0c;模型精度越低&#xff0c;但是…

GEE 将裁剪后的Sentinel-2影像 在ArcGIS中去除空值

在ArcGIS中&#xff0c;可以使用栅格计算器&#xff08;Raster Calculator&#xff09;工具来设置NoData值为空。以下是在ArcGIS中将NoData值设置为空的步骤&#xff1a; 打开ArcGIS软件并加载下载的Sentinel-2影像数据。 影像Nodata空值以黑色背景呈现&#xff0c;影响矢量数据…

使用 Docker 部署 Stirling-PDF 多功能 PDF 工具

1&#xff09;Stirling-PDF 介绍 大家应该都有过这样的经历&#xff0c;面对一堆 PDF 文档&#xff0c;或者需要合并几个 PDF&#xff0c;或者需要将一份 PDF 文件拆分&#xff0c;又或者需要调整 PDF 中的页面顺序&#xff0c;找到的线上工具 要么广告满天飞&#xff0c;要么 …

OpenCV与机器学习:OpenCV实现主成分分析

OpenCV实现主成分分析 前言主成分分析&#xff08;PCA&#xff09;数据生成画图cv2.PCACompute绘制主成分分析结果 前言 维数灾难是指出现一定数量的特征&#xff08;或者维度&#xff09;后&#xff0c;分类器的性能将开始下降。特征越多&#xff0c;数据集中的信息就越多。但…
最新文章