读取和写入数据到我的条形码阅读器的缓冲区的最佳方法是什么?

我需要在C语言中为Linux编写一个条形码阅读器驱动程序。条形码阅读器通过串行总线工作。 当我向条形码阅读器发送一组命令时,条形码阅读器应该向我返回状态消息。 我设法configuration端口,并创build一个信号处理程序。 在信号处理程序中,我读取了串行总线接收的数据。

所以问题是:我应该读取缓冲区中的数据,然后使用它? 我甚至可以用这种方式configuration的端口在缓冲区中写入数据? 当设备回复我时,根据该回复数据,我需要发送另一个命令给设备。 另外,我可以使用write()来写消息吗? 如果我不能使用,我应该使用什么命令? 你能用写命令帮我一下吗?

我发送给设备的命令总是7个字节,但是回复数据在7-32个字节之间变化。 如果读取函数发送不同数量的字节被读取,我怎么能确定我收到了所有的数据,所以我可以使用它?

这是我写的一些代码。 我正朝着正确的方向走吗? 再一次,这个想法非常简单:我正在向设备发送一个命令,设备中断我的回复。 我读了发送的内容,回复数据,根据这些数据,我发送了另一个命令。 感谢前进。

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <fcntl.h> #include <sys/signal.h> #include <errno.h> #include <termios.h> void signal_handler_IO (int status); /* definition of signal handler */ int n; int fd; int connected; char buffer[14]; int bytes; struct termios termAttr; struct sigaction saio; int main(int argc, char *argv[]) { fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror("open_port: Unable to open /dev/ttyO1\n"); exit(1); } saio.sa_handler = signal_handler_IO; saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO,&saio,NULL); fcntl(fd, F_SETFL, FNDELAY); fcntl(fd, F_SETOWN, getpid()); fcntl(fd, F_SETFL, O_ASYNC ); tcgetattr(fd,&termAttr); //baudRate = B115200; cfsetispeed(&termAttr,B115200); cfsetospeed(&termAttr,B115200); termAttr.c_cflag &= ~PARENB; termAttr.c_cflag &= ~CSTOPB; termAttr.c_cflag &= ~CSIZE; termAttr.c_cflag |= CS8; termAttr.c_cflag |= (CLOCAL | CREAD); termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); termAttr.c_iflag &= ~(IXON | IXOFF | IXANY); termAttr.c_oflag &= ~OPOST; tcsetattr(fd,TCSANOW,&termAttr); printf("UART1 configured....\n"); connected = 1; while(connected == 1){ //write function and read data analyze(Processing) } close(fd); exit(0); } void signal_handler_IO (int status) { bytes = read(fd, &buffer, sizeof(buffer)); printf("%s\n", buffer); } 

再一次,这个想法是非常简单的:我发送一个命令到设备,设备插入我并重播。 我读了什么是发送,我使用重播数据,根据这些数据,我发送另一个命令。

您正在描述主从设备的通用简单布置:主设备发送命令或请求消息,从设备必须回复响应消息。

您正在使用正确的POSIX方法修改标志。 然而系统调用fcntl()tcgetattr()tcsetattr()应该检查它们的返回代码以确保没有错误。

您正在配置非规范(又名原始)输入和输出的串行端口。 (所以你的接收缓冲区应该是unsigned char而不是有符号char 。)这种模式提供了一种基于字节计数和/或在串行链路上静音的方法。 没有理由使用涉及信号处理程序的异步读取。 (您的信号处理程序与此问题具有相同的错误。)主/从设备关系指示请求/响应消息交换。

要正确读取完整的响应消息,您需要定义VMIN和VTIME值 。 如果响应消息的长度是7到32个字节,那么

 termAttr.c_cc[VMIN] = 7; termAttr.c_cc[VTIME] = 5; 

应该做的伎俩。 您将需要至少7个字节的响应消息,并且当半秒钟后字节停止时,则假定消息已完成。

 #define CMDLEN 7 unsigned char buffer[32]; unsigned char cmd[CMDLEN]; while (connected == 1) { /* construct a new command */ /* send request */ nbytes = write(fd, cmd, CMDLEN); if (nbytes != CMDLEN) { /* problem! */ } /* get response, wait if necessary */ nbytes = read(fd, buffer, sizeof(buffer)); if (nbytes < 7) { /* problem! */ } /* process response of nbytes */ } 

增编 :回应评论中的问题

首先,我可以使用定义的VMIN和VTIME值来填充信号处理程序中的缓冲区填充吗?

您没有描述任何要求异步读取串行端口的要求。 (顺便提一句,这不是一个“串行总线” ; USB是一个串行总线; EIA / RS-232是一个串行链路。)如果这不是主/从配置,那么请说出来。 否则,用于读取的信号处理程序是不必要的复杂性,不必要的复杂性(通常)表示设计不佳。 (如果这个代码是完全由事件驱动的系统的一部分,则是例外。)

请注意,串口是完全缓冲读写的。 当数据到达串口时,你的程序不需要有一个挂起或活动的read()请求。 当数据被接收时,内核将串行数据存储在内部缓冲区中。 即使不使用异步读取和信号处理程序,程序也不会丢失或丢失任何数据。

最后一个问题是关于fcntl(),tcgetattr()和tcsetattr()系统调用。 你能给我举一些关于我应该检查什么错误标志的例子吗?

这些信息记录在Linux 手册页中。
如果您使用的是Linux开发系统(您应该),那么请确保已经安装了手册页。 否则,你可以谷歌手册页,例如“fcntl linux手册页”
检查函数原型。
返回的值通常是系统调用状态。 如果出现错误(通常是负值,例如-1),则应该访问全局变量errno以获取更多细节。

 rc = tcgetattr(fd, &termAttr); if (rc < 0) { printf("failed to get attr: %d, %s", rc, strerror(errno)); close(fd); exit (-2); } 

PS我正在发送一个消息,我正在发送到设备的示例,并作出回应,以便您可以看到我在说什么。
tx_buffer = 0xFC 0x05 0x11 0x27 0x56 rx_buffer = 0xFC 0x05 0x40 0x27 0x56 0xFC始终相同0x05字节数0x11命令0x40响应代码0x27 crc_low 0x56 crc_high
一个命令包和响应包的例子

这是二进制协议的典型消息格式。
作为读取处理的一部分,在执行任何消息处理之前,必须验证每个接收到的消息。
必须检查0xFC标头字节以确保消息帧同步。 如果第一个字节不等于0xFC ,那么程序必须进入“寻找同步”模式来查找消息的开始(从该缓冲区的第二个字节开始)。
应检查消息长度是否合理,然后用于定位两个CRC字节,并验证消息内容。 如果CRC检查失败,那么应该忽略“消息”,应该开始“寻找同步”(从这个“消息”的第二或第三个字节开始)。

附录2 :对“最终代码”问题的回答

所以现在的问题是如何创建一个计时器?

Userland可以使用POSIX定时器和一个信号(例如timer_create()来调度定时器。
但是你必须检查可用的时钟分辨率,以确定它是否会让你做1ms。

但海事组织你走错了路。
你忽略了我的建议,不使用异步I / O。
你在一个信号处理程序中调用read() ,如果它试图睡觉,这是一个潜在的问题。
您的异步读取信号处理程序与主线程没有同步或缓冲区管理,因此程序面临丢失读取数据的风险。
你已经忽略了我的关于检查系统调用返回码的建议( write()s除外)。

如果我正在编写这个程序,我可能会使用一个状态机,只有一个write()和一个read()使用串口。

附录3 :对第三组问题的回应

你能给我一个简单的例子,我怎么可以用timer_create()函数创建一个计时器?

显然你没有听从我以前的建议,使用手册页。
在timer_create()手册页中有一个代码示例

关于这个:“你的异步读取信号处理程序没有与主线程同步或缓冲区管理,所以程序有丢失被读取的数据的风​​险。”我想我可以解决这个问题如果我在每次读取后调用clearRXbuffer函数)函数,所以在这种情况下,缓冲区将只包含最后一个可以从7到32字节的消息数据。 所以如果新消息到达,它将被写入缓冲区的开始。 你的东西是什么,这个主意不错,我正朝着正确的方向走吗?如果没有,请给我一些其他的缓冲区管理的想法。

这不是一个可靠的解决方案。

信号处理程序相对于主线程异步执行。
所有的共享变量和缓冲区都是关键区域
关键区域必须用同步构造(如互斥锁(aka mutex )或信号量或条件变量)来保护。
让一个关键区域(如接收缓冲区)不受保护可能在某些时候可能正常工作,但会不可靠,导致(未检测到)数据丢失或“奇怪”的程序行为。
你可以尝试像清除缓冲区一样的各种“band-aids”,但最终只有适当的同步构造(由原子执行的OS提供)才能可靠地工作。

当数据从串口到达时,系统会维护一个FIFO缓冲区,这样就不会丢失任何数据。 您的信号处理程序通过使用一个单一的目标缓冲区抵消该FIFO,每次执行信号处理程序时都会覆盖其缓冲区。 这个程序不会丢失任何数据的唯一方法是确保每个write()read()CheckRXbuffer()都完美协调,并且远程设备的表现完美并且始终及时响应。
但是这些不是程序可以依赖的条件,当你有异步读取和没有同步机制!

多线程教程通常涵盖这些概念。

我可以使用一个write()函数,然后使用另一个write()函数,因为我试图这样做,但它只写了一次。 你知道为什么会这样吗?

没有。

我正朝着正确的方向走吗?

奇怪的是,当你选择完全忽略关于(错误)使用异步I / O的主要话题的建议时,你烦请问我关于一个小项目的意见。

附录4 :回应第四组问题的意见

我想告诉你,在设备的数据表中提到,我需要使用异步I / O,因为…

哇,经过我的两个请求(原始响应和第一个附录),你最后提到2天后为什么你认为你需要使用异步I / O。

因为当我尝试给设备上电或重置设备时,我应该发送N个命令,并且在N个命令之后,设备对我做出响应(上电设备顺序)。

这不需要“异步I / O”。

类似这样的事情也可能发生在我向设备发送状态时,由于某种原因,设备在一段时间内不会响应我,所以我应该重新发送命令(异步I / O)。

这也不需要“异步I / O”,也不需要描述异步I / O。

因为它是在数据表中写的

此数据表是否可以在线获得英文版本?

您是否将“异步通信”与“异步I / O”混淆?

如果这个设备只响应一个命令发送数据,并且从不自发地发送数据(即客户机/服务器模式),那么这意味着你的程序可以期望或知道什么时候可以期待来自设备的消息。
这些将是“请求”或从设备请求的消息。
该设备(从目前为止所描述的)不会发送未经请求的消息。

如果此设备确实发送了未经请求的消息,那么是的,您的程序可能需要具有异步I / O功能来及时处理这些消息。

但是,设备(从目前为止所描述的)只能在收到命令时才发送请求消息或不发送消息。

一个设备不应该指定如何构建主机程序。 它只能指定协议。