在Linux上使用C语言读写串行端口

2024-10-22 08:28:00
admin
原创
256
摘要:问题描述:我正在尝试使用 FTDI 通过 USB 端口发送/接收数据,因此我需要使用 C/C++ 处理串行通信。我正在使用Linux(Ubuntu)。基本上,我连接到一个正在监听传入命令的设备。我需要发送这些命令并读取设备的响应。命令和响应都是ASCII 字符。使用 GtkTerm 一切都运行正常,但是当我切...

问题描述:

我正在尝试使用 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', ' ', '
', ''};
int n_written = write( USB, cmd, sizeof(cmd) -1 );

/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '', 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
"const char`),但没有什么真正起作用。

有人能告诉我我错在哪里吗?

先感谢您。

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

`unsigned char cmd[] = "INIT
"`

并且`cmd[] = "INIT
"`。我更改了它,因为我的设备的命令语法被报告为

<command><SPACE><CR>

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


解决方案 1:

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

开放端口

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 
";
int n_written = 0,
    spot = 0;

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

绝对没有必要逐字节写入,而且int n_written = write( USB, cmd, sizeof(cmd) -1)工作正常。

最后,请阅读

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

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

do {
    n = read( USB, &buf, 1 );
    sprintf( &response[spot], "%c", buf );
    spot += n;
} while( buf != '
' && 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;
}

这个对我有用。谢谢大家!

解决方案 2:

有些接收器需要 EOL 序列,通常是两个字符`
`,因此请尝试在代码中替换该行

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '
', ''};

unsigned char cmd[] = "INIT
";

顺便说一句,上面的方法可能更有效。不需要引用每个字符。

解决方案 3:

1)我会在 init 后添加一个 /n。即write(USB, "init\n", 5);

2) 仔细检查串行端口配置。很可能其中存在错误。仅仅因为您不使用 ^Q/^S 或硬件流控制并不意味着另一端没有预料到它。

3)最有可能:在write()之后 添加一个“usleep(100000) ;。文件描述符设置为不阻塞或等待,对吗?在调用 read 之前,需要多长时间才能得到响应?(它必须由内核通过系统硬件中断接收和缓冲,然后才能read()它。)您是否考虑过使用select()等待某些内容read()?也许有超时?

编辑并添加:

您需要 DTR/RTS 线路吗?硬件流控制会告诉对方发送计算机数据?例如

int tmp, serialLines;

cout << "Dropping Reading DTR and RTS
";
ioctl ( readFd, TIOCMGET, & serialLines );
serialLines &= ~TIOCM_DTR;
serialLines &= ~TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
usleep(100000);
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
sleep (2);

cout << "Setting Reading DTR and RTS
";
serialLines |= TIOCM_DTR;
serialLines |= TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用