簡體   English   中英

如何在 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 接口說明:

RN2903 模塊的所有設置和命令都使用 ASCII 接口通過 UART 傳輸。 所有命令都需要以 < CR >< LF > (為格式化添加空格)終止,並且它們生成的任何回復也將以相同的順序終止。 UART 接口的默認設置為 57600 bps、8 位、無奇偶校驗、1 個停止位、無流量控制。

發送命令時,我可以通過監視端口看到設備以“invalid_parameter”響應

sudo cat /dev/ttyUSBPort1

我假設我的某些 termios 標志設置不正確,或者 write 命令設置不正確。 這是我設置端口的代碼:

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 輸出應以如下方式響應(來自數據表):

2.3.6.1 sys get ver 響應:RN2903 XYZ MMM DD YYYY HH:MM:SS,其中XYZ為固件版本,MMM為月,DD為日,HH:MM:SS為時、分、秒(格式:[HW] ] [FW] [日期] [時間])。 [Date] 和 [Time] 是指固件的發布時間。 此命令返回與硬件平台、固件版本、發布日期和固件創建時間戳相關的信息。 示例: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 \0 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當前使您的程序具有未定義的行為,因此這是一個很好的起點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM