当前位置: 首页 > 面试题库 >

在Linux上用C读写串口

宋和颂
2023-03-14
问题内容

我正在尝试 使用FTDI通过USB端口发送/接收数据 ,因此我需要使用C / C ++处理串行通信。我正在使用 Linux
(Ubuntu)。

基本上,我已连接到正在侦听传入命令的设备。我需要发送这些命令并阅读设备的响应。命令和响应都是 ASCII字符

使用GtkTerm一切正常,但是,当我切换到C编程时,遇到了问题。

这是我的代码:

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions

/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );

/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;        // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;       // no flow control
tty.c_lflag     =   0;          // no signaling chars, no echo, no canonical processing
tty.c_oflag     =   0;                  // no remapping, no delays
tty.c_cc[VMIN]      =   0;                  // read doesn't block
tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout

tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
tty.c_iflag     &=  ~(IXON | IXOFF | IXANY);// turn off s/w flow ctrl
tty.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tty.c_oflag     &=  ~OPOST;              // make raw

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );

if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}

/* *** WRITE *** */

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};
int n_written = write( USB, cmd, sizeof(cmd) -1 );

/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '\0', sizeof buf);

/* *** READ *** */
int n = read( USB, &buf , sizeof buf );

/* Error Handling */
if (n < 0)
{
     cout << "Error reading: " << strerror(errno) << endl;
}

/* Print what I read... */
cout << "Read: " << buf << endl;

close(USB);

发生的结果是read()返回0(根本不读取任何字节)或阻塞直到超时(VTIME)。我假设发生这种情况是因为write()没有发送任何东西。在这种情况下,设备将不会接收命令,并且我将无法接收响应。实际上,在我的程序被阻止读取时关闭设备实际上成功地获得了响应(设备在关闭时发送了一些信息)。

奇怪的是,添加这个

cout << "I've written: " << n_written << "bytes" << endl;

write()致电后,我收到:

I've written 6 bytes

这正是我所期望的。只有我的程序无法正常工作,就像 我的设备无法收到我 在端口上 实际写入的 内容一样。

我已经尝试了不同的方法和解决方案,还涉及了数据类型(我尝试使用std :: string,例如cmd = "INIT \r"or const char),但是没有任何实际效果。

有人可以告诉我我错了吗?

先感谢您。

编辑: 以前使用此代码的版本

unsigned char cmd[] = "INIT \n"

还有cmd[] = "INIT \r\n"。我更改了它,因为我的设备的命令sintax报告为

<command><SPACE><CR>

我也尝试过避免O_NONBLOCK读取时出现标志,但是后来我一直阻塞直到永远。我尝试使用,select()但是什么也没有发生。只是尝试一下,我创建了一个等待循环,直到数据可用为止,但是我的代码从未退出循环。顺便说一句,等待还是usleep()我需要避免的事情。报告的只是我的代码的一部分。
完整的代码需要在实时环境 (特别是OROCOS)中工作,所以我真的不想要类似睡眠的功能。


问题答案:

我已经解决了我的问题,所以我在这里发布了正确的代码,以防有人需要类似的东西。

开放港口

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

设定参数

struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}

unsigned char cmd[] = "INIT \r";
int n_written = 0,
    spot = 0;

do {
    n_written = write( USB, &cmd[spot], 1 );
    spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);

绝对没有必要每个字节写一个字节,也可以正常int n_written = write( USB, cmd, sizeof(cmd) -1)工作。

最后, 阅读

int n = 0,
    spot = 0;
char buf = '\0';

/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);

do {
    n = read( USB, &buf, 1 );
    sprintf( &response[spot], "%c", buf );
    spot += n;
} while( buf != '\r' && n > 0);

if (n < 0) {
    std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
    std::cout << "Read nothing!" << std::endl;
}
else {
    std::cout << "Response: " << response << std::endl;
}

这个为我工作。谢谢你们!



 类似资料:
  • 问题内容: 我在Ubuntu上使用LibSerial在串行端口上读写数据。 目前,我可以通过串行端口写入和接收字符串,但是我的代码不能很好地工作:特别是, 我想控制读取功能,以便仅在有需要读取的内容时才能读取当没有信息可读取时退出,以发送另一个命令 而不会阻塞流程。 我想要做: 编写命令 等待答案 然后写另一个命令 等待答案 现在,我可以在while循环中使用读取功能发送第一个命令并读取答案,但是

  • 我读了很多问题和答案,但没有找到任何解决方案。也许我的问题不对,但我需要一些指导。我在Linux中使用串行端口,从我的Arduino设备读取数据。每当我想从Arduino向Linux发送数据时,我首先发送两个字节,这表示将从Arduino发送的总字节数。我将这两个字节转换为整数值,并开始从串行端口读取数据。比如说,我想把300字节从Ardiuno发送到Linux,我只需要先写{1,44},然后用下

  • 问题内容: 我想知道是否有有效的开源工具在Linux(Ubuntu)上开发C#应用程序。特别是,我必须开发WinForm应用程序。 我知道 Mono项目 ,但从未使用过。您能否建议我在Ubuntu上设置.NET开发环境的最佳工具(IDE,编译器等)是什么? 它是在可在Windows上运行的Linux上开发的软件吗?是否有不同的行为或不兼容? 问题答案: MonoDevelop,与Mono Proj

  • 主要内容:C# 中的 I/O 类,FileStream 类,C#中文本文件的读取写入,二进制文件读写文件是存储在磁盘中的具有特定名称和目录路径的数据集合,当我们使用程序对文件进行读取或写入时,程序会将文件以数据流(简称流)的形式读入内存中。我们可以将流看作是通过通信路径传递的字节序列,流主要分为输入流和输出流,输入流主要用于从文件读取数据(读操作),输出流主要用于向文件中写入数据(写操作)。 C# 中的 I/O 类 System.IO 命名空间中包含了各种用于文件操作的类,例如文件的创建、删除、

  • 问题内容: 我正在寻找解决上述问题的解决方案。 这是我的“无效代码”。charsInCurrentBuffer始终返回-1! 问题答案: 当你从FD中,“用户的定位缓冲区和文件偏移都必须是文件系统的逻辑块大小的倍数”(引自手册页在Linux上)。其他环境对此可能有不同的约束,并且实际上是依赖于文件系统的。 通常情况并非如此(除非您很幸运)。 如果平台具有该功能,则应考虑使用该功能,或者只是分配一个

  • 问题内容: 我知道如何在Mac OS上使用Xcode在Swift中访问C库,并且在Linux上也知道,但是如何在Linux上使用Swift这样的C库,如OpenGL? 问题答案: 使用系统模块导入OpenGL头文件:https : //github.com/apple/swift-package- manager/blob/master/Documentation/SystemModules.md