linux - C ++串行通信读取数据有效但写入失败
问题描述
我正在为嵌入式系统和在 Linux 环境中运行的 C++ 应用程序之间的串行通信创建一个类。因此,我使用了适用于 Linux 的 termios API,在此处进行了描述。
构造函数将打开设备的串口。在我的情况下,我使用的 arduino 微控制器是“ttyUSB0”。接下来它将设置波特率和其他端口选项。
我还添加了在串行端口上读取或写入数据的功能。因为 read 是一个阻塞函数(在接收到数据或超时后才返回),我添加了一个函数来检查是否有可用的字节,您可以在调用“Read()”之前应该这样做。
制作测试用例后,阅读似乎工作正常。函数“Available()”确实返回了可用的字节数。阅读后将它们打印到控制台。
但是,由于某些未知原因,我的写入功能不起作用,即使我“相信”我正确地遵循了指南中的步骤。我为 write 功能做了一个测试用例:一旦收到正确的消息,arduino 应该闪烁它的内置 LED。当一条消息以开始标记“#”开始并以结束标记“$”结束时,它是正确的。
当我使用测试工具 putty 或 arduino 的串行监视器发送正确的消息时,LED 会闪烁。但是当我通过自己的 write 函数发送消息时,这不会发生。
arduino 有其他内置 LED,用于指示 RX 和 TX 引脚上的数据。一旦我从自己的写入函数发送数据,这些 LED 确实会亮起,但我的测试用例中的闪烁函数从未被调用。然后我检查是否读取了任何字节,但是当从我自己的写入函数发送数据时,arduino 的“Serial.available()”永远不会返回高于 0 的值。
我认为这个错误要么在写函数本身,要么在串口的配置中。到目前为止,我无法弄清楚这一点。有没有人对此有任何经验或知识,或者对我应该如何解决这个问题有任何提示?
提前致谢,
短剑
Linux 代码:
主文件
#include "serial.h"
#include <iostream>
using namespace std;
int main()
{
//TEST CASE FOR WRITING DATA
Serial serial("/dev/ttyUSB0");
serial.Write("#TEST$");
//TEST CASE FOR READING DATA
/*while (true)
{
char message[100];
char * ptr = NULL;
while (serial.Available() > 0)
{
char c;
serial.Read(&c);
switch(c)
{
case '#':
ptr = message;
break;
case '$':
if (ptr != NULL)
{
*ptr = '\0';
}
std::cout << "received: " << message << std::endl;
ptr = NULL;
break;
default:
if (ptr != NULL)
{
*ptr = c;
ptr++;
}
break;
}
}
}*/
return EXIT_SUCCESS;
}
串行.h
#ifndef SERIAL_H
#define SERIAL_H
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <string>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
class Serial
{
private:
int fd;
public:
Serial(std::string device);
~Serial()
{
close(fd);
};
int Available();
void Read(char * buffer, int amountOfBytes);
void Read(char * bytePtr);
int Write(std::string message);
};
#endif
串口.cpp
#include "serial.h"
#include <stdexcept>
#include <string.h>
Serial::Serial(std::string device)
{
// Open port
fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0)
{
throw std::runtime_error("Failed to open port!");
}
// Config
struct termios config;
tcgetattr(fd, &config);
// Set baudrate
cfsetispeed(&config, B9600);
cfsetospeed(&config, B9600);
// 9600 8N1
config.c_cflag &= ~PARENB;
config.c_cflag &= ~CSTOPB;
config.c_cflag &= ~CSIZE;
config.c_cflag |= CS8;
// Disable hardware based flow control
config.c_cflag &= ~CRTSCTS;
// Enable receiver
config.c_cflag |= CREAD | CLOCAL;
// Disable software based flow control
config.c_iflag &= ~(IXON | IXOFF | IXANY);
// Termois Non Cannoincal Mode
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// Minimum number of characters for non cannoincal read
config.c_cc[VMIN] = 1;
// Timeout in deciseconds for read
config.c_cc[VTIME] = 0;
// Save config
if (tcsetattr(fd, TCSANOW, &config) < 0)
{
close(fd);
throw std::runtime_error("Failed to configure port!");
}
// Flush RX Buffer
if (tcflush(fd, TCIFLUSH) < 0)
{
close(fd);
throw std::runtime_error("Failed to flush buffer!");
}
}
int Serial::Available()
{
int bytes = 0;
if (ioctl(fd, TIOCINQ, &bytes) < 0)
{
close(fd);
throw std::runtime_error("Failed to check buffer!");
}
return bytes;
}
void Serial::Read(char * buffer, int amountOfBytes)
{
if (read(fd, buffer, amountOfBytes) < 0)
{
close(fd);
throw std::runtime_error("Failed to read bytes!");
}
}
void Serial::Read(char * bytePtr)
{
return Serial::Read(bytePtr, 1);
}
int Serial::Write(std::string message)
{
int length = message.size();
if (length > 100)
{
throw std::invalid_argument("Message may not be longer than 100 bytes!");
}
char msg[101];
strcpy(msg, message.c_str());
int bytesWritten = write(fd, msg, length);
if (bytesWritten < 0)
{
close(fd);
throw std::runtime_error("Failed to write bytes!");
}
return bytesWritten;
}
Arduino代码
void setup()
{
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
//TEST-CASE FOR WRITING DATA
/*Serial.print("#TEST$");
delay(1000);*/
//TEST-CASE FOR READING DATA
char message[100];
char * ptr = NULL;
while (Serial.available() > 0)
{
char c = Serial.read();
switch(c)
{
case '#':
ptr = message;
break;
case '$':
if (ptr != NULL)
{
*ptr = '\0';
}
ptr = NULL;
int messageLength = strlen(message);
Blink();
break;
default:
if (ptr != NULL)
{
*ptr = c;
ptr++;
}
break;
}
}
}
void Blink()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
解决方案
解决了。“打开”功能在串行端口上发送一个信号,arduino 将其解释为重新启动的信号。我通过禁用 auto-reset解决了这个问题。
或者,您可以在保存配置后添加两秒钟的延迟。
这个问题特别适用于 arduino 微控制器。
推荐阅读
- node.js - 在 nodeJS 中处理长时间运行的进程的可能方法
- python - 将列表值分配给 2D 列表的最佳方法
- excel - 用于 VBA 的 Excel 表格列的“代码名称”
- c++ - 如何将小数转换为二进制补码字符串
- javascript - 自定义 WooCommerce 消息删除 HTML 标签上的 onClick 事件
- python - 在 python 3.7.2 中使用请求发送 JSON Post
- c# - 如何制作.NET Web API 的自定义路由?
- python - 使用 Z3 求解多个方程
- java - 如何从 FirestorePagingAdapter 中的方法 onLoadingStateChanged 隐藏/显示进度条?
- postgresql - 在 postgress 中访问 jsonb 对象会引发错误