C++串口通信读数据有效但写入失败
C++ Serial communication reading data works but writing fails
我正在为 Linux 环境中的嵌入式系统和 C++ 应用程序 运行 之间的串行通信创建 class。因此,我将术语 API 用于 Linux,描述为 here。
构造函数将打开设备的串口。在我的例子中,我使用的 arduino 微控制器是 'ttyUSB0'。接下来它将设置波特率和其他端口选项。
我还添加了在串口上读取或写入数据的功能。因为读取是一个阻塞函数(直到接收到数据或超时时才会 return)我添加了一个函数来检查是否有可用的字节,你可以在调用 'Read()' 之前检查。
做了一个测试用例后,阅读似乎工作正常。函数 'Available()' 确实 return 可用的字节数。阅读后将它们打印到控制台。
然而,由于某些未知原因,我的写入功能无法正常工作,尽管我 'believe' 我已正确按照指南中的步骤进行操作。我为写入功能做了一个测试用例:一旦收到正确的消息,arduino 应该闪烁它的内置 LED。以开始标记“#”开始并以结束标记“$”结束的消息是正确的。
当我用测试工具putty或arduino的串口监视器发送正确的消息时,led会闪烁。但是当我通过我自己的写函数发送消息时,这不会发生。
arduino 还有其他内置 LED,用于指示 RX 和 TX 引脚上的数据。一旦我从我自己的写入函数发送数据,这些 LED 实际上会亮起,但我的测试用例中的闪烁函数从未被调用。然后我检查是否读取了任何字节,但是当数据从我自己的写函数发送时,arduino 的 'Serial.available()' 永远不会 return 的值高于 0。
我认为错误要么在写入函数本身,要么在串行端口的配置中。到目前为止,我无法弄清楚这一点。有没有人对此有任何经验或知识,或者对我应该如何解决这个问题有任何提示?
提前致谢,
德克
Linux代码:
main.cpp
#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 = '[=10=]';
}
std::cout << "received: " << message << std::endl;
ptr = NULL;
break;
default:
if (ptr != NULL)
{
*ptr = c;
ptr++;
}
break;
}
}
}*/
return EXIT_SUCCESS;
}
Serial.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
Serial.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 = '[=13=]';
}
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);
}
已解决。
'open' 函数在串行端口上发送信号,arduino 将其解释为重启信号。我通过 disabling auto-reset.
解决了这个问题
或者,您可以在保存配置后添加两秒的延迟。
此问题专门针对 arduino 微控制器。
我正在为 Linux 环境中的嵌入式系统和 C++ 应用程序 运行 之间的串行通信创建 class。因此,我将术语 API 用于 Linux,描述为 here。
构造函数将打开设备的串口。在我的例子中,我使用的 arduino 微控制器是 'ttyUSB0'。接下来它将设置波特率和其他端口选项。
我还添加了在串口上读取或写入数据的功能。因为读取是一个阻塞函数(直到接收到数据或超时时才会 return)我添加了一个函数来检查是否有可用的字节,你可以在调用 'Read()' 之前检查。
做了一个测试用例后,阅读似乎工作正常。函数 'Available()' 确实 return 可用的字节数。阅读后将它们打印到控制台。
然而,由于某些未知原因,我的写入功能无法正常工作,尽管我 'believe' 我已正确按照指南中的步骤进行操作。我为写入功能做了一个测试用例:一旦收到正确的消息,arduino 应该闪烁它的内置 LED。以开始标记“#”开始并以结束标记“$”结束的消息是正确的。
当我用测试工具putty或arduino的串口监视器发送正确的消息时,led会闪烁。但是当我通过我自己的写函数发送消息时,这不会发生。
arduino 还有其他内置 LED,用于指示 RX 和 TX 引脚上的数据。一旦我从我自己的写入函数发送数据,这些 LED 实际上会亮起,但我的测试用例中的闪烁函数从未被调用。然后我检查是否读取了任何字节,但是当数据从我自己的写函数发送时,arduino 的 'Serial.available()' 永远不会 return 的值高于 0。
我认为错误要么在写入函数本身,要么在串行端口的配置中。到目前为止,我无法弄清楚这一点。有没有人对此有任何经验或知识,或者对我应该如何解决这个问题有任何提示?
提前致谢,
德克
Linux代码:
main.cpp
#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 = '[=10=]';
}
std::cout << "received: " << message << std::endl;
ptr = NULL;
break;
default:
if (ptr != NULL)
{
*ptr = c;
ptr++;
}
break;
}
}
}*/
return EXIT_SUCCESS;
}
Serial.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
Serial.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 = '[=13=]';
}
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);
}
已解决。 'open' 函数在串行端口上发送信号,arduino 将其解释为重启信号。我通过 disabling auto-reset.
解决了这个问题或者,您可以在保存配置后添加两秒的延迟。
此问题专门针对 arduino 微控制器。