C/C++读取UART口并显示结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23907084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:34:50  来源:igfitidea点击:

C/C++ read UART port and display results

c++signalsposixuart

提问by Anders

I have written the C program that should read UART's RxD port and display the results as soon as there is any information. To achieve this I'm using signal_handler SIGIO signal

我已经编写了应该读取 UART 的 RxD 端口并在有任何信息时立即显示结果的 C 程序。为了实现这一点,我正在使用signal_handler SIGIO 信号

Read program c code

读取程序c代码

#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include <string.h>
#define BAUDRATE B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1

int fd;
void signal_handler_IO(int status);
void set_port_settings();
char buff[255]; 
sig_atomic_t flag=0;
using namespace std;

int main ()
{
set_port_settings();

for(;;){

if(flag !=0)
  {    
         //printf( "sync : 0x%X\n", buff[1]);
         //printf ( "PID: 0x%X\n", buff[2]);
         printf ( "D0: 0x%X\n",  buff[4]);
         printf ( "D1: 0x%X\n",  buff[5]);
         printf ( "D2: 0x%X\n",  buff[6]);
         printf ( "D3: 0x%X\n",  buff[7]);
         printf ( "D4: 0x%X\n",  buff[8]);
         printf ( "D5: 0x%X\n",  buff[9]);
         printf ( "D6: 0x%X\n",  buff[10]);
         printf ( "D7: 0x%X\n",  buff[11]);
         printf ( "CHK: 0x%X\n", buff[12]);
    flag = 0;
    }

   }
}


void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
    }
}

void set_port_settings()
{
    struct termios oldtio, newtio;
    struct sigaction saio;
    fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd<0) {perror(PORT); exit(-1);}

    saio.sa_handler=signal_handler_IO;
    sigemptyset(&saio.sa_mask);
    saio.sa_flags=SA_RESTART;
    sigaction(SIGIO, &saio,NULL);

    fcntl (fd, F_SETOWN, getgid());
    fcntl(fd, F_SETFL, FASYNC);

    tcgetattr(fd, &oldtio); perror("tsgetattr");


  newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
  newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");
  newtio.c_oflag = 0; perror("c_oflag");
 newtio.c_lflag = ICANON | ISIG ; perror("c_lflag"); 
  newtio.c_cc[VMIN]=8;perror("c_cc[VMIN]");
  newtio.c_cc[VTIME]=1; perror("c_cc[VTIME]");
newtio.c_cc[VSTART]=0x55;

  tcflush(fd, TCIFLUSH); perror("TCFLUSH");

   tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");
}  

Problem that I have, is that when the program reads data and starts printing out the results, the information printed out is somehow correct just printed (or read in) in a wrong place.

我遇到的问题是,当程序读取数据并开始打印结果时,打印出来的信息在某种程度上是正确的,只是在错误的地方打印(或读入)了。

I'm writing to the port using another C program. I've tried to do it from the same C program but was unsuccessful to write and read from the same C program. So I keep 2 shells open: on one shell I'm running write program, on another shell I'm running read program to display what it was able to read in.

我正在使用另一个 C 程序写入端口。我试图从同一个 C 程序中做到这一点,但是从同一个 C 程序中写入和读取没有成功。所以我保持打开 2 个 shell:在一个 shell 上我正在运行写程序,在另一个 shell 上我正在运行 read 程序以显示它能够读入的内容。

Write program C code

编写程序 C 代码

#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define BAUDRATE9600 B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1

using namespace std;

int main() {
int fd;
char buffer[255];
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}

tcgetattr(fd, &oldtio);

newtio.c_cflag = BAUDRATE9600 | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);

char SYNC  [] = {0x55};
char PID [] = {0x97};
char data0 [] = {0x25};
char data1 [] = {0xFF};
char data2 [] = {0x00};
char data3 [] = {0x64};
char data4 [] = {0x01};
char data5 [] = {0xFF};
char data6 [] = {0xFF};
char data7 [] = {0xFC};
char checksum [] ={0xE0};

for (;;) {

ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us

ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits

write(fd, SYNC, sizeof(SYNC));
write(fd, PID, sizeof(PID));
write(fd, data0, sizeof(data0));
write(fd, data1, sizeof(data1));
write(fd, data2, sizeof(data2));
write(fd, data3, sizeof(data3));
write(fd, data4, sizeof(data4));
write(fd, data5, sizeof(data5));
write(fd, data6, sizeof(data6));
write(fd, data7, sizeof(data7));
write(fd, checksum, sizeof(checksum)); 

usleep(10000);
close (fd); }
#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define BAUDRATE9600 B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1

using namespace std;

int main() {
int fd;
char buffer[255];
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}

tcgetattr(fd, &oldtio);

newtio.c_cflag = BAUDRATE9600 | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);

char SYNC  [] = {0x55};
char PID [] = {0x97};
char data0 [] = {0x25};
char data1 [] = {0xFF};
char data2 [] = {0x00};
char data3 [] = {0x64};
char data4 [] = {0x01};
char data5 [] = {0xFF};
char data6 [] = {0xFF};
char data7 [] = {0xFC};
char checksum [] ={0xE0};

为了 (;;) {

ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us

ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits

write(fd, SYNC, sizeof(SYNC));
write(fd, PID, sizeof(PID));
write(fd, data0, sizeof(data0));
write(fd, data1, sizeof(data1));
write(fd, data2, sizeof(data2));
write(fd, data3, sizeof(data3));
write(fd, data4, sizeof(data4));
write(fd, data5, sizeof(data5));
write(fd, data6, sizeof(data6));
write(fd, data7, sizeof(data7));
write(fd, checksum, sizeof(checksum)); 

usleep(10000);
close (fd); }

When I run both programs to check if the READprograms is working as it should, I can see that the data is read in, but is not exactly as it it's written to the port.

当我运行这两个程序以检查READ程序是否正常工作时,我可以看到数据已读入,但与写入端口的情况不完全一样。

example of the data read in

读入数据的例子

d0: 0x7C
d1: 0x66
d2: 0x1
d3: 0xE0
d4: 0x4C
d5: 0x7C
d6: 0x8
d7: 0x60
CHK: 0x60

d0: 0x1
d1: 0xE0
d2: 0x4C
d3: 0x7C
d4: 0x8
d5: 0x60
d6: 0xFC
d7: 0x60
CHK: 060

I hope that somebody will be able to point where I have made a mistake and what should I do to be able to read from the UART port without the problem.

我希望有人能够指出我犯了错误的地方,以及我应该怎么做才能从 UART 端口读取数据而不会出现问题。

回答by sawdust

Problem that I have, is that when the program reads data and starts printing out the results, the information printed out is somehow correct just printed (or read in) in a wrong place.

我遇到的问题是,当程序读取数据并开始打印结果时,打印出来的信息在某种程度上是正确的,只是在错误的地方打印(或读入)了。

Unfortunately, because your read program does output something, you mistakenly think there is only one problem relating to data data alignment. Data or message alignment is just one of many problems in your programs.

不幸的是,因为你的读取程序确实输出了一些东西,你错误地认为只有一个与数据数据对齐相关的问题。数据或消息对齐只是程序中的众多问题之一。

The read and write programs improperly initialize the serial port to an incomplete (and therefore unknown) state. As commented by @Swanand the write program prematurely closes its file descriptor.

读写程序不正确地将串行端口初始化为不完整(因此未知)状态。正如@Swanand 所评论的,写程序过早地关闭了它的文件描述符。

The read program is needlessly using async I/O events to perform read()syscalls and not checking the return code. The read program then unconditionally prints out the buffer regardless of whether there is actual read data.

读取程序不必要地使用异步 I/O 事件来执行read()系统调用而不检查返回代码。读取程序然后无条件地打印出缓冲区,而不管是否有实际读取数据。

Using a break condition on the serial link is an unorthodox method of message separation. Since the message seems to be framed with a starting "sync" byte and a terminating "checksum" byte, this message frame should be validated by the read program to ensure that message alignment is intact.

在串行链路上使用中断条件是一种非正统的消息分离方法。由于消息似乎是用起始“同步”字节和终止“校验和”字节构成的,因此读取程序应验证此消息帧以确保消息对齐完好无损。





Some of the specific errors in your code:

代码中的一些特定错误:



Lack of proper and consistent formatting.

缺乏适当和一致的格式。



void set_port_settings()
{
struct termios oldtio, newtio;

newtiois an automatic variable, and therefore is not initialized.
The program selectively assigns values to a few structure elements, and leaves others undefined.
These undefined elements in the termios structure passed on through the tcsetattr()syscall can lead to unpredictable and/or unreliable operation of the serial port.

newtio是一个自动变量,因此不会被初始化。
该程序有选择地为一些结构元素赋值,而其他的则未定义。
通过tcsetattr()系统调用传递的 termios 结构中的这些未定义元素可能导致串行端口的不可预测和/或不可靠的操作。

You should study Setting Terminal Modes Properlyand Serial Programming Guide for POSIX Operating Systems.

您应该学习“正确设置终端模式”“POSIX 操作系统的串行编程指南”



newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");

This is an improper method of setting termios attributes.
Refer to the mentioned guides for proper technique.
You claimed you corrected these mistakes in this question, yet here they are still.

这是设置 termios 属性的不正确方法。
有关正确的技术,请参阅上述指南。
你声称你纠正了这个问题中的这些错误,但它们仍然存在。

The unconditional perror()after each assignment is wrong since this isn't a syscall.

每次赋值后的无条件perror()都是错误的,因为这不是系统调用。

Enabling IXONseems irrelevant since this is the read program that isn't doing any output. (IXONenables soft flow-control for output.)

启用IXON似乎无关紧要,因为这是不执行任何输出的读取程序。(IXON为输出启用软流控制。)



newtio.c_lflag = ICANON | ISIG ; perror("c_lflag"); 

Enabling ICANONseems incorrect since the data from the write program is not line-formatted text but binary data. This program should be setting up raw mode instead of canonical mode.

启用ICANON似乎不正确,因为来自写入程序的数据不是行格式文本而是二进制数据。这个程序应该设置原始模式而不是规范模式。

Enabling ISIGseems incorrect since the triggering character values in VINTR, VQUIT, VSUSP, or VDSUSPare all undefined in this uninitialized termios structure.
Since the write program is creating break conditions, a signal from a break, could be generated but has unwanted side effects, i.e. flushing the queues.

启用ISIG似乎不正确,因为在触发字符值VINTRVQUITVSUSP,或VDSUSP都在这个初始化termios结构不确定的。
由于写入程序正在创建中断条件,因此可能会生成中断信号,但会产生不需要的副作用,即刷新队列。

The unconditional perror()after each assignment is wrong since this isn't a syscall.

每次赋值后的无条件perror()都是错误的,因为这不是系统调用。



tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");

The return code from this (and all other) system call needs to be checked.
The unconditional perror()call is wrong.

需要检查这个(和所有其他)系统调用的返回码。
无条件的perror()调用是错误的。



I have written the C program that should read UART's RxD port and display the results as soon as there is any information. To achieve this I'm using signal_handler SIGIO signal

我已经编写了应该读取 UART 的 RxD 端口并在有任何信息时立即显示结果的 C 程序。为了实现这一点,我正在使用 signal_handler SIGIO 信号

Your program has no direct access to the UART's RxD port.
It's the serial port driver that reads the actual data from the UART RxD register.
Since you've setup the serial port for canonical input, the data is stored in the line discipline buffer, and then copied to a system buffer.
The read()syscall in your program retrieves data from the system buffer.
The SIGIO is not speeding up the read by your program.

您的程序无法直接访问 UART 的 RxD 端口。
它是从 UART RxD 寄存器读取实际数据的串行端口驱动程序。
由于您已经为规范输入设置了串行端口,因此数据存储在线路规则缓冲区中,然后复制到系统缓冲区中。
程序中的read()系统调用从系统缓冲区中检索数据。
SIGIO 没有加快程序的读取速度。

void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
    }
}

This signal handler is not necessary if your program performed an ordinary blocking read(). Your premise for using nonblocking I/O and SIGIO seem to be incorrect. The program is not utilizing asynchronous I/O effectively, so you might as well simplify the program.

如果您的程序执行了普通的阻塞read() ,则不需要此信号处理程序。您使用非阻塞 I/O 和 SIGIO 的前提似乎不正确。该程序没有有效地利用异步 I/O,因此您不妨简化该程序。

The return code from the read()needs to be checked. Besides checking for any error condition, the return code will indicate the number of bytes that have been read.
Your read program incorrectly assumes that a complete message of at least 11 bytes is received every time, and could cause display of stale values in the main loop.

需要检查read()的返回码。除了检查任何错误条件外,返回码还将指示已读取的字节数。
您的读取程序错误地假设每次收到至少 11 个字节的完整消息,并且可能导致在主循环中显示陈旧值。

Since the read program enables neither IGNBRKor BRKINT, a break condition is received by your program as a null byte, 0x00, along with the actual message data.

由于读程序使得既不IGNBRKBRKINT,中断条件是由你的程序收到一个空字节,0×00,与实际的消息数据一起。



The read program needs to search for and maintain message alignment using the "sync" and "checksum" bytes. Your program currently has no method at all to test for or to achieve message alignment.
A basic algorithm for performing this are in this answer

读取程序需要使用“同步”和“校验和”字节来搜索和维护消息对齐。您的程序目前根本没有任何方法来测试或实现消息对齐。
执行此操作的基本算法在此答案中

回答by Klaus

If you read/write non ascii data to files you should open with 'O_BINARY'.

如果您将非 ascii 数据读/写到文件中,您应该使用“O_BINARY”打开。

As next hint: Use your console to read or write data to see which program is faulty. Simply use 'echo' for that.

作为下一个提示:使用您的控制台读取或写入数据以查看哪个程序有问题。只需使用“回声”即可。

To see what your program internally reads or writes use 'strace -xfo dump your_prog' and look into the file 'dump' to see which characters was send/read from your uart.

要查看您的程序在内部读取或写入的内容,请使用“strace -xfo dump your_prog”并查看文件“dump”以查看从您的 uart 发送/读取的字符。

The following instructions seems to be really senseless for me! There is no chance to get a sleep exactly to any kind of bit rates until you have a hard real time kernel and hardware. On x86 you will have deviations of more then 100ms! for a usleep if an other process is running. Every disk io will kill your timing for example.

下面的说明对我来说似乎真的毫无意义!在您拥有硬实时内核和硬件之前,没有机会完全适应任何类型的比特率。在 x86 上,您将有超过 100 毫秒的偏差!如果其他进程正在运行,则用于 usleep。例如,每个磁盘 io 都会扼杀您的时间。

ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us

ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits

For sending a break use

用于发送休息使用

  tcsendbreak()