使用 select 进行 UART 通信的注意事项

文章目录

    • 引言
    • UART 通信中的 `select` 函数
    • `select` 函数的工作原理
    • 使用 `select` 进行 UART 通信的注意事项
    • 示例代码

引言

UART(Universal Asynchronous Receiver/Transmitter)是一种用于异步串行通信的硬件协议,常用于计算机和外设之间的数据交换。在嵌入式系统中,UART 通信非常常见,用于连接传感器、微控制器、调制解调器等设备。为了实现高效的 UART 通信,通常需要使用非阻塞式 I/O 操作,这时候 select 函数就派上用场了。

UART 通信中的 select 函数

select 函数允许我们同时监控多个文件描述符,并在这些文件描述符变为可读、可写或发生错误时通知程序。这样可以避免程序在等待 I/O 操作时被阻塞,提高系统的响应速度和效率。

select 函数的工作原理

  • 监控文件描述符select 接受一组文件描述符,并监控这些文件描述符的状态。
  • 阻塞等待:可以设置阻塞等待或非阻塞等待,直到文件描述符变为可读、可写或发生错误,或者达到超时时间。
  • 返回事件select 返回后,程序可以检查哪些文件描述符变为可读、可写或发生错误,并进行相应的处理。

使用 select 进行 UART 通信的注意事项

在使用 select 进行 UART 通信时,有几个关键点需要注意:

  1. 正确初始化文件描述符集

每次调用 select 之前,需要重新初始化文件描述符集(fd_set),因为 select 调用会修改这个集合:

FD_ZERO(&rfds);
FD_SET(fd, &rfds);
  1. 设置超时值

在每次调用 select 之前,需要设置超时值(struct timeval)。同样,select 会修改这个结构体,因此每次调用前都需要重新设置:

struct timeval tv;
tv.tv_sec = timeout_us / 1000000;
tv.tv_usec = timeout_us % 1000000;
  1. 处理 select 的返回值

检查 select 的返回值以确定是否有文件描述符变为可读、可写或出错:

int ret = select(fd + 1, &rfds, NULL, NULL, &tv);
if (ret == -1) {// Handle error
} else if (ret == 0) {// Handle timeout
} else {if (FD_ISSET(fd, &rfds)) {// Handle readable data}
}
  1. 避免文件描述符泄漏

确保在程序结束或不再需要文件描述符时关闭它们,防止资源泄漏:

close(fd);
  1. 多线程环境下的文件描述符使用

如果在多线程环境下使用文件描述符,需要确保对文件描述符的操作是线程安全的。使用互斥锁(mutex)保护对文件描述符的操作,避免竞态条件。

  1. 检查文件描述符的有效性

在调用 select 之前检查文件描述符是否有效,确保文件描述符在整个生命周期中是有效的:

int check_fd_valid(int fd) {return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
  1. 处理边界情况

处理 read 返回 0 的情况,这通常表示文件结束或没有数据可用:

ssize_t bytesRead = read(fd, buff, len);
if (bytesRead == -1) {// Handle read error
} else if (bytesRead == 0) {// Handle EOF or no data
}

示例代码

以下是综合了上述注意事项的改进代码示例:

#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdarg.h>
#include <fcntl.h>
#include <pthread.h>void ota_info(const char *fmt, ...) {va_list args;va_start(args, fmt);vprintf(fmt, args);printf("\n");va_end(args);
}int check_fd_valid(int fd) {return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}int uart_read(int fd, uint8_t *buff, int len, int timeout_us)
{int ret;struct timeval tv;fd_set rfds;memset(buff, 0, len);// Debug log to print fd value before selectota_info("uart_read: fd=%d, timeout_us=%d, thread_id=%ld", fd, timeout_us, pthread_self());// Check if fd is validif (!check_fd_valid(fd)) {ota_info("Invalid file descriptor: %d", fd);return -1;}FD_ZERO(&rfds);FD_SET(fd, &rfds);memset(&tv, 0, sizeof(tv));tv.tv_sec = timeout_us / 1000000;tv.tv_usec = timeout_us % 1000000;ret = select(fd + 1, &rfds, NULL, NULL, &tv);if (ret == -1) {// Select itself failedota_info("select() failed. ret %d, errno %d, %m", ret, errno);return -1;} else if (ret == 0) {// Timeout occurred without any file descriptor becoming readyota_info("select() timed out.");return 0; // Indicate timeout}if (FD_ISSET(fd, &rfds)) {ssize_t bytesRead = read(fd, buff, len);if (bytesRead == -1) {// Error during readota_info("read() failed. errno %d, %m", errno);return -1;} else if (bytesRead == 0) {// EOF reached or no data availableota_info("read() returned 0, no data available or EOF reached.");return 0;}// Return actual bytes readota_info("read() succeeded. bytesRead=%ld, thread_id=%ld", bytesRead, pthread_self());return bytesRead;} else {// This branch should not be reachable given the checks aboveota_info("select() returned with no file descriptor ready.");return 0;}
}int main() {// Example usageint fd = 0; // Example file descriptor, should be initialized properlyuint8_t buffer[256];int len = 256;int timeout_us = 5000000; // 5 secondsint result = uart_read(fd, buffer, len, timeout_us);if (result > 0) {printf("Read %d bytes\n", result);} else if (result == 0) {printf("Timeout or no data available\n");} else {printf("Error occurred\n");}return 0;
}

通过遵循这些注意事项,可以确保在使用 select 函数进行 UART 通信时,代码更加健壮和可靠,减少出现难以复现的错误的可能性。希望这篇博客能帮助你在项目中更好地使用 select 函数进行 UART 通信。

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

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

相关文章

Apple创始人斯蒂夫乔布斯2005年在斯坦福大学的毕业典礼演讲:Steve Jobs‘ 2005 Stanford Commencement Address

Steve Jobs’ 2005 Stanford Commencement Address Link: https://www.youtube.com/watch?vUF8uR6Z6KLc and https://www.youtube.com/watch?vHd_ptbiPoXM 文章目录 Steve Jobs 2005 Stanford Commencement AddressSummaryVocabularyTranscriptConnecting the dotsLove and …

从函数逼近角度理解神经网络、残差连接与激活函数

概述 最近思考激活函数的时候&#xff0c;突然想到神经网络中残差连接是不是和函数的泰勒展开很像&#xff0c;尤其是在激活函数 f ( x ) x 2 f(x)x^2 f(x)x2时(这个激活函数想法来源于 f ( x ) R e L U 2 ( x ) [ 3 ] f(x)ReLU^2(x)[3] f(x)ReLU2(x)[3])&#xff0c;所以验…

VC++支持断点续下或续传的功能

VC使用多线程和Socket实现断点续下 一、断点续下的基本原理&#xff1a; 1.断点续传的理解可以分为两部分&#xff1a;一部分是断点&#xff0c;一部分是续传。断点的由来是在下载过程中&#xff0c;将一个下载文件分成了多个部分&#xff0c;同时进行多个部分一起的下载&…

Adaboost集成学习 | Adaboost集成学习特征重要性分析(Python)

目录 效果一览基本介绍模型设计程序设计参考资料效果一览 基本介绍 Adaboost集成学习特征重要性分析(Python)Adaboost(自适应增强)是一种常用的集成学习方法,用于提高机器学习算法的准确性。它通过组合多个弱分类器来构建一个强分类器。在Adaboost中,每个弱分类器都被赋予…

基于LangChain框架搭建知识库

基于LangChain框架搭建知识库 说明流程1.数据加载2.数据清洗3.数据切分4.获取向量5.向量库保存到本地6.向量搜索7.汇总调用 说明 本文使用openai提供的embedding模型作为框架基础模型&#xff0c;知识库的搭建目的就是为了让大模型减少幻觉出现&#xff0c;实现起来也很简单&a…

Ocam:高效录屏,屏幕录制最佳?

名人说&#xff1a;&#xff1a;一点浩然气&#xff0c;千里快哉风。 ——苏轼 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍1、Ocam2、核心特点 二、下载安装1、下载2、安装 三、使用方法 很高兴你…

【5】apollo编写python节点步骤及实例

在workspace/modules下新建包buildtool create --template component modules/test_one 编译包 buildtool build -p modules/test_two/ 增加自己的proto消息 在刚才自动生成的proto文件里面添加自己定义的消息,记得重新编译. syntax "proto2";package apollo;…

【python】美妆类商品跨境电商数据分析(源码+课程论文+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

使用RedisShake迁移自建Redis数据至阿里云Redis

一、前言 最近有个需求&#xff0c;需要把自建的Redis数据迁移至阿里云的Redis RDS。阿里云有官方的数据传输服务DTS&#xff08;Data Transmission Service&#xff09;。全量迁移是免费的&#xff0c;但增量迁移需要按量收费&#xff0c;遂放弃。经过一番搜索&#xff0c;发…

数据库的概念-数据库、数据库管理系统、数据库系统、数据库管理员、数据库设计人员、开发管理使用数据库系统的人员

一、数据库&#xff08;DB&#xff09; 1、数据库就是存储数据的仓库&#xff0c;只不过这个仓库是在计算机存储设备上 2、严格的说&#xff0c;数据库是长期存储在计算机内、有组织的、统一管理的、可共享的相关数据的集合 3、数据库应是为一个特定目标而设计、构建并装入数…

ClickHouse备份方案

ClickHouse备份方案主要包括以下几种方法&#xff1a; 一、使用clickhouse-backup工具&#xff1a; &#xff08;参考地址&#xff1a;https://blog.csdn.net/qq_43510111/article/details/136570850&#xff09; **安装与配置&#xff1a;**首先从GitHub获取clickhouse-bac…

利用MSSQL模拟提权

点击星标&#xff0c;即时接收最新推文 本文选自《内网安全攻防&#xff1a;红队之路》 扫描二维码五折购书 利用MSSQL模拟提权 在MS SQL数据库&#xff0c;可以使用EXECUTE AS语句&#xff0c;以其他用户的上下文执行SQL查询。需要注意的是只有明确授予模拟&#xff08;Impers…

38.MessageToMessageCodec线程安全可被共享Handler

handler被注解@Sharable修饰的。 这样的handler,创建一个实例就够了。例如: ByteToMessageCodec的子类不能被@Sharable修饰 如果自定义类是MessageToMessageCodec的子类就是线程共享的,可以被@Sharable修饰的 package com.xkj.protocol;import com.xkj.message.Message; i…

Go日常分享 - error类型是指针类型吗?

背景 这个问题的产生来源于小泉在开发rpc接口时返回error遇到的问题&#xff0c;开发时想在defer里对err进行最终的统一处理赋值&#xff0c;发现外层接收一直都未生效。问题可以简化为成下面的小demo。 func returnError() error {var err errordefer func() {//err errors…

在 Oracle Linux 8.9 上安装中文和日文字体的完整指南

在 Oracle Linux 8.9 上安装中文和日文字体的完整指南 在 Oracle Linux 8.9 上安装中文和日文字体的完整指南前提条件安装步骤1. 更新系统2. 安装字体包安装中文字体安装日文字体 3. 安装字体配置工具4. 更新字体缓存5. 验证安装 可能遇到的问题及解决方案结语 在 Oracle Linux…

(一)SvelteKit教程:hello world

&#xff08;一&#xff09;SvelteKit教程&#xff1a;hello world sveltekit 的官方教程&#xff0c;在这里&#xff1a;Creating a project • Docs • SvelteKitCreating a project • Docs • SvelteKit 我们可以按照如下的步骤来创建一个项目&#xff1a; npm create s…

CentOs7 安装单机版redis

1.安装依赖 redis是由C语言开发&#xff0c;因此安装之前必须要确保服务器已经安装了gcc&#xff0c;可以通过如下命令查看机器是否安装&#xff1a; gcc -v如果没有安装则通过以下命令安装&#xff1a; yum install -y gcc如果安装gcc依赖报错则执行yum升级命令 # 先执行升…

NSIS 入门教程 (三)

引言 在教程的第二部分中&#xff0c;我们为安装程序增加了一个卸载程序&#xff0c;并查看了一些其他的向导页面以及安装部分的选择。第三部分的目标是使安装程序的外观更加现代化。 更现代的外观 为了给安装程序一个更现代的外观&#xff0c;我们要启用现代用户界面。要提…

Shell编辑之条件语句

一&#xff0c;条件测试操作 1&#xff1a;文件测试 文件测试操作用来检查文件的各种属性&#xff0c;如文件是否存在、是否可读、是否为空等。常用的文件测试操作符包括&#xff1a; -e 文件存在性测试-f 是否为普通文件-d 是否为目录-r 是否可读-w 是否可写-x 是否可执行-s…

学懂C#编程:常用高级技术——委托(Delegate)的概念及详细使用讲解

目录 委托的概念 常用应用场景 优势 C#中的委托&#xff08;Delegate&#xff09;是一种引用类型&#xff0c;它允许你封装一个方法的引用。委托类似于函数指针&#xff0c;但提供了更强大和类型安全的功能。委托在C#中扮演着多重角色&#xff0c;常用于实现回调方法、事件…