为我的条码阅读器读取和写入数据到缓冲区的最佳方法是什么? [英] What is the best way to read and write data to a buffer for my bar code reader?

查看:65
本文介绍了为我的条码阅读器读取和写入数据到缓冲区的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

所以问题是:我应该先读取缓冲区中的数据然后再使用它吗?甚至可以通过这种方式配置的端口将数据写入缓冲区吗?当设备回复我时,根据那个回复数据,我需要向设备发送另一个命令.另外,我可以使用 write()编写消息吗?如果我不能使用该命令,应该使用什么命令?您能用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 #include< sys/signal.h>#include< errno.h>#include< termios.h>无效signal_handler_IO(int状态);/*信号处理程序的定义*/int n;int fd;内部连接;字符缓冲区[14];整数字节;struct termios termAttr;结构签名int main(int argc,char * argv []){fd = open("/dev/ttyUSB1",O_RDWR | O_NOCTTY | O_NDELAY);如果(fd == -1){perror("open_port:无法打开/dev/ttyO1 \ n");出口(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已配置....\ n");已连接= 1;while(connected == 1){//编写函数并读取数据分析(处理中)}关闭(FD);退出(0);}无效signal_handler_IO(int状态){字节=读(fd,& buffer,sizeof(buffer));printf(%s \ n",buffer);} 

解决方案

再一次,这个想法很简单:我向设备发送命令,然后设备中断我并重放.我读了发送的内容,使用了重播数据,然后根据该数据,我发送了另一个命令.

您所描述的是master& Master的常见且简单的安排.从设备:主设备发送命令或请求消息,而从设备必须回复响应消息.

您正在使用正确的POSIX方法修改标志.但是,系统调用 fcntl() tcgetattr() tcsetattr()时,应检查其返回码以确保没有错误./p>

您正在配置用于非规范(又称原始)输入和输出的串行端口.输出.(因此,您的接收缓冲区应为 unsigned char ,而不是带符号的 char .)此模式提供了一种基于字节数和/或串行链接上的静音的读取方法.没有理由使用涉及信号处理程序的异步读取.(您的信号处理程序与 VMIN和VTIME值.如果响应消息的长度为7到32个字节,则

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

应该可以解决问题.您可能希望响应消息的长度不超过32个字节,或者当字节在半秒后停止到达时,则假定该消息已完成.

  #define CMDLEN 7无符号字符缓冲区[32];未签名的字符cmd [CMDLEN];而(已连接== 1){/*构造一个新命令*//* 发送请求 */nbytes = write(fd,cmd,CMDLEN);if(nbytes!= CMDLEN){/* 问题!*/}/*获得响应,如有必要,请等待*/nbytes = read(fd,buffer,sizeof(buffer));如果(nbytes< 7){/* 问题!*/}/*处理响应数(以字节为单位)*/} 

附录:回答评论中的问题

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

您尚未描述异步读取串行端口的任何要求.(顺便说一句,这不是串行总线" ; USB是串行总线; EIA/RS-232是串行链接.)如果这不是主/从配置,请这样说..否则,用于读取的信号处理程序是不必要的复杂性,而不必要的复杂性(通常)是设计不佳的迹象.(如果该代码是完全由事件驱动的系统的一部分,则为例外.)

请注意,串行端口已完全缓冲以供读取和写入.当数据到达串行端口时,您的程序不必具有挂起或活动的 read()请求.当接收到数据时,内核将串行数据存储在内部缓冲区中.即使您的程序不使用异步读取和信号处理程序,它也不会丢失或丢失任何数据.

最后一个问题是关于fcntl(),tcgetattr()和tcsetattr()系统调用的.您能给我一些例子,说明我应该检查哪些错误标志吗?

此类信息记录在Linux man 页面中.
如果使用Linux开发系统(应该这样做),请确保已安装 man 页面.否则,您可以在Google上搜索手册页,例如
检查功能原型.
返回的值通常是syscall状态.如果指示错误(通常为负值,例如-1),则应访问全局变量 errno 以获得更多详细信息.

  rc = tcgetattr(fd,& termAttr);如果(rc< 0){printf(无法获得attr:%d,%s",rc,strerror(errno));关闭(FD);出口(-2);} 

P.S我正在向设备发送正在发送的消息的示例和响应,以便您可以查看我在说什么.
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())来安排周期性计时器.
但是,您必须检查可用的时钟分辨率,以确定它是否可以让您执行1毫秒的操作.

但是IMO,您走错了路.
您已经忽略了我关于不使用异步I/O的建议.
您正在信号处理程序中调用 read(),如果它试图进入睡眠状态,则可能会出现问题.
您用于异步读取的信号处理程序与主线程没有同步或缓冲区管理,因此该程序有丢失已读取数据的风险.
您已经忽略了我关于检查系统调用返回码的建议( write()s 除外).

如果我正在编写此程序,则可能会使用状态机,并且使用串行端口只有一个 write()和一个 read().

附录3 :回答评论中的第三组问题

能否请给我一个简单的示例,说明如何使用timer_create()函数创建计时器?

显然,您没有听从我先前有关使用手册页的建议.
timer_create()手册页

中有一个代码示例.

关于此内容:您用于异步读取的信号处理程序与主线程之间没有同步或缓冲区管理,因此该程序有丢失读取数据的风险."我想我可以解决这个问题.如果我在信号处理程序中的每个read()函数之后调用clearRXbuffer函数,那么在这种情况下,缓冲区将仅包含最后一个消息数据,该消息数据的范围为7到32个字节.因此,如果有新消息到达,它将被写入缓冲区的开头.您怎么办,这个主意好吗,我朝正确的方向走吗?如果没有,您能给我一些其他的缓冲区管理主意吗?

那不是一个可靠的解决方案.

信号处理程序相对于主线程异步执行.
因此,所有共享变量和缓冲区都是 关键区域 .
必须使用同步结构(例如互斥锁(aka 互斥体)或信号灯或条件变量)来保护关键区域.
使关键区域(如接收缓冲区)不受保护似乎在某些时候还可以,但是将是不可靠的,并会导致(未检测到)数据丢失或奇怪"的情况.程序行为.
您可以尝试各种创可贴"就像清除缓冲区一样,但最终只有正确的同步结构(由原子执行的OS提供)才能可靠地工作.

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

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

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

否.

...我的方向正确吗?

奇怪的是,当您选择完全忽略我对使用异步I/O的(错误)主要主题的建议时,您会不愿意要求我对次要项目发表意见.

附录4 :回答评论中的第四组问题

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

哇,在我提出2项要求(原始答复和第1篇附录)之后,您终于在2天后提到为什么您认为需要使用异步I/O.

因为当我尝试打开设备电源或重置设备时,我应该发送N条命令,并在N条命令后设备响应我.(打开设备顺序).

不需要异步I/O".

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

这不需要异步 I/O";也不描述异步I/O.

因为它写在数据表中

此数据表可以用英语在线获得吗?

您是否对异步通信"感到困惑?与异步I/O"一起使用?

如果此设备仅响应命令发送数据,而从未主动自发发送数据(即客户端/服务器模型),则意味着您的程序可以期望或知道何时可以期望来自该设备的消息.
这些将是消息被请求".或从设备请求.
该设备(根据您到目前为止的描述)永远不会发送不请自来的消息.

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

但是同样,该设备(根据您到目前为止的描述)仅在收到命令时发送请求消息或不发送消息.

设备不应指示主机程序的结构.它只能指定协议.

I need to write a driver for a bar code reader for Linux in C. The bar code reader works through the serial bus. When I send a set of commands to the bar code reader, the bar code reader should return status messages to me. I managed to configure the port and to create a signal handler. In the signal handler, I read the data which the serial bus receives.

So the question is: Should I read the data in buffer and then work with it? And can I even write the data in the buffer with the port configured in this way? When the device replies to me, according to that reply data, I need to send another command to the device. Also, can I use write() to write the messages? If I can’t use that, what command should I use? And can you help me a little with the write command?

The command that I send to the device is always 7 bytes, but the reply data varies between 7-32 bytes. If the read function sends different number of bytes that are read, how can I be sure that I received all of the data, so I can work with it?

Here is some code that I've written. Am I going in the right direction? Once more, the idea is very simple: I am sending a command to a device, and the device interupt me and replies. I read what was send, I work with the reply data, and according to that data, I am send another command. Thanks in forward.

#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);
}

解决方案

Once more, the idea is very simple: I am sending a command to a device, and the device interupt me and replays. I read what was send, I work with the replay data, and according to that data, I am sending another command.

You are describing a common and simple arrangement of master & slave devices: the master device sends a command or request message, and the slave device has to reply with a response message.

You are using the proper POSIX method of modifying the flags. However the system calls fcntl(), tcgetattr() and tcsetattr() should have their return codes checked to ensure there were no errors.

You are configuring the serial port for non-canonical (aka raw) input & output. (So your receiving buffer should be unsigned char rather than signed char.) This mode offers a method of reading based on byte count and/or silence on the serial link. There is no reason to use async read involving a signal handler. (Your signal handler has the same bug as this question.) The master/slave device relationship dictates a request/response message exchange.

To properly read the complete response message, you need to define the VMIN and VTIME values. If the response message is 7 to 32 bytes in length, then

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

should do the trick. You would be expecting a response message of no more than 32 bytes, or when bytes stop arriving after a half second, then assume the message is complete.

#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 */
}

Addendum: response to questions in comments

First, can I leave the filling of the buffer in the signal handler with defined values for VMIN and VTIME?

You have not described any requirement for asynchronous reading of the serial port. (BTW this is not a "serial bus"; USB is a serial bus; EIA/RS-232 is a serial link.) If this is not a master/slave configuration, then please say so. Otherwise a signal handler for reading is an unnecessary complication, and unnecessary complexity is (usually) an indication of poor design. (Exception would be if this code is part of a system that is exclusively event driven.)

Be aware that the serial port is fully buffered for both reading and writing. Your program does not have to have a pending or active read() request when data arrives at the serial port. The kernel stores the serial data in an internal buffer as the data is received. Your program will not lose or miss any data even if it does not use async reading and a signal handler.

The last question is about fcntl(), tcgetattr() and tcsetattr() sistem call. Can you please give me some examples about what error flag I should check?

Such information is documented in the Linux man pages.
If you are using a Linux development system (and you should), then make sure that the man pages are installed. Otherwise you can google for the man page, e.g. "fcntl linux man page"
Check the function prototype.
The returned value is typically the syscall status. If an error is indicated (often by a negative value such as -1), then the global variable errno should be accessed to get more detail.

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

P.S I am sending an example of the message that I am sending to the device, and an response, so that you can see for what I am talking about.
tx_buffer = 0xFC 0x05 0x11 0x27 0x56 rx_buffer = 0xFC 0x05 0x40 0x27 0x56 0xFC always same 0x05 number of bytes 0x11 command 0x40 response code 0x27 crc_low 0x56 crc_high
example of one command packet and response packet

That is a typical message format for a binary protocol.
As part of the read processing, each received message must be validated before any message processing is performed.
The 0xFC header byte must be checked to ensure that you have message frame synchronization. If the first byte is not equal to 0xFC, then the program has to go into a "hunt for synchonization" mode to find the start of a message (starting with the second byte in this buffer).
The message length should be checked for reasonableness and then used to locate the two CRC bytes, and the message contents can be validated. If the CRC check fails, then the "message" should be ignored and a "hunt for sync" should commence (starting with the second or third byte of this "message").

Addendum 2: response to question on "final code

So the question is now how to create a timer?

Userland can schedule a periodic timer using a POSIX timer and a signal, i.e. timer_create().
But you'll have to check the available clock resolutions to determine if it will let you do 1ms.

But IMO you are going down the wrong path.
You have ignored my advice on not using async I/O.
You are calling read() in a signal handler, which is a potential problem if it tries to sleep.
Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.
You have ignored my advice on checking return codes of syscalls (except for write()s).

If I was writing this program, I would probably use a state machine and there would be only one write() and a read() using the serial port.

Addendum 3: response to 3rd set of questions in comments

Can you please give me a simple example of how can I create a timer, with the timer_create() function?

Apparently you didn't heed my previous advice on using man pages.
There's a code example in the timer_create() man page

About this: "Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read." I think I can solve this If I call clearRXbuffer function after every read() function in the signal handler, so in this case the buffer will only contain the last message data which can be from 7 to 32 bytes. So if new message arrives it will be written in the beginning of the buffer. What do you thing, is this idea good, and am I going in right direction?If not, can you please give me some other buffer management idea.

That is not a reliable solution.

The signal handler executes asynchronously with respect to the main thread.
All shared variables and buffers are therefore critical regions.
Critical regions have to be protected with synchronizing constructs such as mutual exclusion locks (aka mutexes) or semaphores or conditional variables.
Leaving a critical region (like the receive buffer) unprotected might seem to work okay some of the time, but will be unreliable and cause (undetected) loss of data or "strange" program behavior.
You can try various "band-aids" like clearing the buffer, but in the end only proper synchronizing constructs (provided by the OS that execute atomically) will work reliably.

The system maintains a FIFO buffer for the data as it arrives from the serial port, so that no data is lost. Your signal handler counteracts that FIFO by using a single destination buffer that overwrites its buffer everytime the signal handler is executed. The only way this program won't lose any data is to somehow ensure that every write(), read() and CheckRXbuffer() are perfectly coordinated, and the remote device behaves perfectly and responds in a timely manner all the time.
But those are not conditions that a program can depend on when you have asynchronous reads and no synchronizing mechanisms in place!

Tutorials on multithreading usually cover these concepts.

Can I use one write()function, and then another one write() function, because I tried to do that, but it only is written once. Do you know why is that happening?

No.

... am I going in right direction?

Strange that you bother to ask for my opinion on a minor item, when you choose to completely ignore my advice on the major topic of (mis)using async I/O.

Addendum 4: response to 4th set of questions in comments

I want to tell you that in the datasheet of the device is mentioned that I need to use async I/O because ...

Wow, after 2 requests by me (original response & 1st addendum), you finally mention 2 days later why you think you need to use async I/O.

because when I try to power up the device, or reset the device, I should sent N number of commands, and after N number of commands the device response to me.(power up device sequence).

That does not require "async I/O".

Something like this also can happen when I am sending a status to the device, and the device for some reason won’t response to me, in amount period of time, so I should resend the command (async I/O).

That does not require "async I/O" either, nor does that describe async I/O.

because it’s written in the datasheet

Is this datasheet available in English online?

Are you confusing "asynchronous communication" with "asynchronous I/O"?

If this device only sends data in response to a command, and never sends data spontaneously on its own accord (i.e. client/server model), then that means that your program can expect or knows when it can expect messages from the device.
These would be messages that were "solicited" or requested from the device.
The device (from what you have described so far) never sends unsolicited messages.

If this device did send unsolicited messages, then yes, your program might need to have async I/O capability to process these message in a timely manner.

But again, the device (from what you have described so far) only sends solicited messages or no messages when it receives a command.

A device should not be dictating how the host program should be structured. It can only specify the protocol.

这篇关于为我的条码阅读器读取和写入数据到缓冲区的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆