广东建设注册中心网站,邵阳网站制作,怎样在百度上做网站,游戏排行榜2023文章目录 引言UART 通信中的 select 函数select 函数的工作原理使用 select 进行 UART 通信的注意事项示例代码 引言
UART#xff08;Universal Asynchronous Receiver/Transmitter#xff09;是一种用于异步串行通信的硬件协议#xff0c;常用于计算机和外设之间的数据交换… 文章目录 引言UART 通信中的 select 函数select 函数的工作原理使用 select 进行 UART 通信的注意事项示例代码 引言
UARTUniversal Asynchronous Receiver/Transmitter是一种用于异步串行通信的硬件协议常用于计算机和外设之间的数据交换。在嵌入式系统中UART 通信非常常见用于连接传感器、微控制器、调制解调器等设备。为了实现高效的 UART 通信通常需要使用非阻塞式 I/O 操作这时候 select 函数就派上用场了。
UART 通信中的 select 函数
select 函数允许我们同时监控多个文件描述符并在这些文件描述符变为可读、可写或发生错误时通知程序。这样可以避免程序在等待 I/O 操作时被阻塞提高系统的响应速度和效率。
select 函数的工作原理
监控文件描述符select 接受一组文件描述符并监控这些文件描述符的状态。阻塞等待可以设置阻塞等待或非阻塞等待直到文件描述符变为可读、可写或发生错误或者达到超时时间。返回事件select 返回后程序可以检查哪些文件描述符变为可读、可写或发生错误并进行相应的处理。
使用 select 进行 UART 通信的注意事项
在使用 select 进行 UART 通信时有几个关键点需要注意
正确初始化文件描述符集
每次调用 select 之前需要重新初始化文件描述符集fd_set因为 select 调用会修改这个集合
FD_ZERO(rfds);
FD_SET(fd, rfds);设置超时值
在每次调用 select 之前需要设置超时值struct timeval。同样select 会修改这个结构体因此每次调用前都需要重新设置
struct timeval tv;
tv.tv_sec timeout_us / 1000000;
tv.tv_usec timeout_us % 1000000;处理 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}
}避免文件描述符泄漏
确保在程序结束或不再需要文件描述符时关闭它们防止资源泄漏
close(fd);多线程环境下的文件描述符使用
如果在多线程环境下使用文件描述符需要确保对文件描述符的操作是线程安全的。使用互斥锁mutex保护对文件描述符的操作避免竞态条件。
检查文件描述符的有效性
在调用 select 之前检查文件描述符是否有效确保文件描述符在整个生命周期中是有效的
int check_fd_valid(int fd) {return fcntl(fd, F_GETFD) ! -1 || errno ! EBADF;
}处理边界情况
处理 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.hvoid 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 通信。