我如何在 C++ 中为 Raspberry PI USB UART 连接设置 termios 参数?
How do i set up termios parameters in C++ for a Raspberry PI USB UART connection?
我有一个 RPI 3 和一个 LoRa USB 模块,我正在用 C++ 编写一些代码用于设备的端口连接。我能够连接到端口(在 udev 规则中指定为 ttyUSBPort1)。但是,当我向端口发送数据时,出现错误。我只是不太了解 termios 和端口通信来确定这是否是问题所在(是的,我已经阅读了联机帮助页)。
LoRa模块是RN2903设备,下面是UART接口说明参考sheet:
All of the RN2903 module’s settings and commands are transmitted over UART using
the ASCII interface.
All commands need to be terminated with < CR >< LF > (spaces added for formatting) and any replies they generate will
also be terminated by the same sequence.
The default settings for the UART interface are 57600 bps, 8 bits, no parity, 1 Stop bit,
no flow control.
发送命令时,通过监听
端口可以看到设备正在响应"invalid_parameter"
sudo cat /dev/ttyUSBPort1
我假设我有一些 termios 标志设置不正确,或者写命令设置不正确。这是我设置端口的代码:
int openPort(void) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if ((usb_port = open(device, O_RDWR))>=0) {// | O_NOCTTY | O_SYNC
std::cout << "DEVICE OPENED: " << device << " handle number: " << usb_port << std::endl;
} else {
fprintf(stderr, "unable to open serial device");
return -1;
}
if(tcgetattr(usb_port, &tty) != 0) {
printf("Error %i \n", errno);
}
cfsetispeed(&tty, B57600);
cfsetospeed(&tty, B57600);
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] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tcflush( usb_port, TCIFLUSH );
if (tcsetattr(usb_port, TCSANOW, &tty) != 0) {
printf("Error %i\n", errno);
}
return usb_port;
}
下面是从设备获取版本信息的调用命令:
void radioCMD(string tmp) {
string tmp2 = tmp + "\r\n";
int n = tmp2.length();
char cmd[n];
strcpy(cmd, tmp2.c_str());
std::cout << write(usb_port, cmd, sizeof(cmd)) << " " << cmd << "Writing to " << usb_port << " Delay: " << delay << " Command Size: " << sizeof(cmd) << std::endl;
}
void setupRadio() {
radioCMD("sys get ver");
usleep(delay);
}
写入控制台时 std::cout,我看到了:
13 sys get ver
Writing to 3 Delay: 200000 Command Size: 13
显示消息确实被正确写入。
设备的 cat 输出应响应如下(来自数据sheet):
2.3.6.1
sys get ver
Response: RN2903 X.Y.Z MMM DD YYYY HH:MM:SS, where X.Y.Z is the firmware
version, MMM is month, DD is day, HH:MM:SS is hour, minutes, seconds (format: [HW]
[FW] [Date] [Time]). [Date] and [Time] refer to the release of the firmware.
This command returns the information related to the hardware platform, firmware
version, release date and time-stamp on firmware creation.
Example: sys get ver
我实际得到的是 "invalid_param\r\n",如果呼叫中的某些内容不正确,这是设备的适当响应。
我有什么地方可能出错的想法吗?
编辑
感谢 Ted 为我指明了正确的方向并简化了我的代码。有两个丢失的 termios 标志。一旦我设置了这些(最后两个),它就可以正常工作。
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] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
***ADDITIONAL TWO FLAGS THAT FIXED IT****
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_oflag &= ~OCRNL; // Prevent conversion of newline to carriage return/line feed
新写调用函数:
void radioCMD(string cmd) {
cmd += "\r\n";
write(usb_port, cmd.c_str(), cmd.size());
}
这将创建一个 VLA,它 char
太短而无法容纳 C 字符串的空终止符:
void radioCMD(string tmp) {
string tmp2 = tmp + "\r\n";
int n = tmp2.length();
char cmd[n]; // should be n+1 for strcpy to work
strcpy(cmd, tmp2.c_str()); // undefined behavior [=10=] is written out of bounds
write(usb_port, cmd, sizeof(cmd)); // sizeof(cmd) should be replaced by n
}
更好的替代方法是使用 std::memcpy
而不是 std::strcpy
来复制 C 字符串 而没有 空终止符 - 并避免 VLA
:s.
更好的替代方法是直接使用您获得的 std::string
作为参数:
void radioCMD(string cmd) {
cmd += "\r\n";
write(usb_port, cmd.c_str(), cmd.size());
}
这可能不是唯一的问题,但由于 std::strcpy
当前使您的程序具有未定义的行为,因此这是一个很好的起点。
我有一个 RPI 3 和一个 LoRa USB 模块,我正在用 C++ 编写一些代码用于设备的端口连接。我能够连接到端口(在 udev 规则中指定为 ttyUSBPort1)。但是,当我向端口发送数据时,出现错误。我只是不太了解 termios 和端口通信来确定这是否是问题所在(是的,我已经阅读了联机帮助页)。
LoRa模块是RN2903设备,下面是UART接口说明参考sheet:
All of the RN2903 module’s settings and commands are transmitted over UART using the ASCII interface. All commands need to be terminated with < CR >< LF > (spaces added for formatting) and any replies they generate will also be terminated by the same sequence. The default settings for the UART interface are 57600 bps, 8 bits, no parity, 1 Stop bit, no flow control.
发送命令时,通过监听
端口可以看到设备正在响应"invalid_parameter"sudo cat /dev/ttyUSBPort1
我假设我有一些 termios 标志设置不正确,或者写命令设置不正确。这是我设置端口的代码:
int openPort(void) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if ((usb_port = open(device, O_RDWR))>=0) {// | O_NOCTTY | O_SYNC
std::cout << "DEVICE OPENED: " << device << " handle number: " << usb_port << std::endl;
} else {
fprintf(stderr, "unable to open serial device");
return -1;
}
if(tcgetattr(usb_port, &tty) != 0) {
printf("Error %i \n", errno);
}
cfsetispeed(&tty, B57600);
cfsetospeed(&tty, B57600);
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] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tcflush( usb_port, TCIFLUSH );
if (tcsetattr(usb_port, TCSANOW, &tty) != 0) {
printf("Error %i\n", errno);
}
return usb_port;
}
下面是从设备获取版本信息的调用命令:
void radioCMD(string tmp) {
string tmp2 = tmp + "\r\n";
int n = tmp2.length();
char cmd[n];
strcpy(cmd, tmp2.c_str());
std::cout << write(usb_port, cmd, sizeof(cmd)) << " " << cmd << "Writing to " << usb_port << " Delay: " << delay << " Command Size: " << sizeof(cmd) << std::endl;
}
void setupRadio() {
radioCMD("sys get ver");
usleep(delay);
}
写入控制台时 std::cout,我看到了:
13 sys get ver
Writing to 3 Delay: 200000 Command Size: 13
显示消息确实被正确写入。
设备的 cat 输出应响应如下(来自数据sheet):
2.3.6.1 sys get ver Response: RN2903 X.Y.Z MMM DD YYYY HH:MM:SS, where X.Y.Z is the firmware version, MMM is month, DD is day, HH:MM:SS is hour, minutes, seconds (format: [HW] [FW] [Date] [Time]). [Date] and [Time] refer to the release of the firmware. This command returns the information related to the hardware platform, firmware version, release date and time-stamp on firmware creation. Example: sys get ver
我实际得到的是 "invalid_param\r\n",如果呼叫中的某些内容不正确,这是设备的适当响应。
我有什么地方可能出错的想法吗?
编辑
感谢 Ted 为我指明了正确的方向并简化了我的代码。有两个丢失的 termios 标志。一旦我设置了这些(最后两个),它就可以正常工作。
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] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
***ADDITIONAL TWO FLAGS THAT FIXED IT****
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_oflag &= ~OCRNL; // Prevent conversion of newline to carriage return/line feed
新写调用函数:
void radioCMD(string cmd) {
cmd += "\r\n";
write(usb_port, cmd.c_str(), cmd.size());
}
这将创建一个 VLA,它 char
太短而无法容纳 C 字符串的空终止符:
void radioCMD(string tmp) {
string tmp2 = tmp + "\r\n";
int n = tmp2.length();
char cmd[n]; // should be n+1 for strcpy to work
strcpy(cmd, tmp2.c_str()); // undefined behavior [=10=] is written out of bounds
write(usb_port, cmd, sizeof(cmd)); // sizeof(cmd) should be replaced by n
}
更好的替代方法是使用 std::memcpy
而不是 std::strcpy
来复制 C 字符串 而没有 空终止符 - 并避免 VLA
:s.
更好的替代方法是直接使用您获得的 std::string
作为参数:
void radioCMD(string cmd) {
cmd += "\r\n";
write(usb_port, cmd.c_str(), cmd.size());
}
这可能不是唯一的问题,但由于 std::strcpy
当前使您的程序具有未定义的行为,因此这是一个很好的起点。