【问题标题】:RaspberryPi RS-232 trouble树莓派 RS232 故障
【发布时间】:2013-10-03 13:22:40
【问题描述】:

我正在使用我的 Pi 上的 RS-232 线路与激光测距仪进行通信。我已经使用minicom以19200的波特率测试了两者之间的通信(因为这是LRF的波特率并且无法更改),并且工作正常。尽管向 LRF 写下任何命令(由单个字符组成并按“回车”)可能需要多次尝试才能生效,但双向通信效果很好。

但是,当我开始用 C 代码编程以使用 RS-232 进行读写时,它只工作了一半。这是我正在使用的代码:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

//SETUP UART0

int main(){
    int uart0_filestream = -1;
    int loop;
    int i;
    int isError=1, rx_length;
    unsigned char rx_buffer[256];
    useconds_t micro=3000;

    uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);

    if(uart0_filestream == -1)
        printf("ERROR: Unable to open UART\n\n");
    else
        printf("UART open\n\n");

    struct termios options;

    tcgetattr(uart0_filestream, &options);
    options.c_cflag = B19200 | CS8 | CLOCAL | CREAD;
    options.c_iflag = IGNPAR | ICRNL;
    options.c_oflag = 0;
    options.c_lflag = 0;

    tcflush(uart0_filestream, TCIFLUSH);
    tcsetattr(uart0_filestream, TCSANOW, &options);

    unsigned char tx_buffer[20];
    unsigned char *p_tx_buffer;

    p_tx_buffer = &tx_buffer[0];
    *p_tx_buffer++ = 'o';
    *p_tx_buffer++ = '\n';

    /*
    if(uart0_filestream != -1){
        for(i = 0; i<100; i++){
            int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));
            if(count < 0)
                printf("\n\nERROR: No bytes written\n\n");
            else
                printf("%i bytes written: %s\n", (p_tx_buffer - &tx_buffer[0]), tx_buffer);
        }
    }
    */
    if(uart0_filestream != -1){
        for(i=0; i<50; ){
            rx_length = read(uart0_filestream, (void*)rx_buffer, 255);

            if(rx_length > 0){
                printf("rx_lentgh = %i:\t ", rx_length);
                for(loop=0; loop<30; loop++){
                    //check for NULL and new line for easier readability
                    if(rx_buffer[loop] == NULL)
                        rx_buffer[loop] = '$';
                    if(rx_buffer[loop] == '\n')
                        rx_buffer[loop] = '%';
                    printf("%c", rx_buffer[loop]);
                }
                printf("\n");
                i++;
            }
        }
    }

    close(uart0_filestream);
}

当我尝试从设备读取数据时,它总是返回错误。我开始循环查看是否不断阅读会产生不同的结果。在 100 次尝试中,通常是 4-5 次返回数据,其余所有 [i]rx_length[/i] 都返回 -1。返回的数据应如下所示:

计数:0000

其中数字取决于 LRF 测量的距离。但相反,我得到这样的输出:

rx_lentgh = 16: jRþ$COUNTS:0000%$$$$$$$$$$$$
rx_lentgh = 8: %$COUNTSTS:0000%$$$$$$$$$$$$
rx_lentgh = 16: :0142%%$COUNTS:0$$$$$$$$$$$$
rx_lentgh = 8: 000%%$COCOUNTS:0$$$$$$$$$$$$
rx_lentgh = 16: UNTS:0142%%$COUN$$$$$$$$$$$$
rx_lentgh = 24: TS:0142%%$COUNTS:0000%%$$$$$
rx_lentgh = 8:计数:0%$COUNTS:0000%%$$$$$
rx_lentgh = 16: 142%%$COUNTS:000:0000%%$$$$$
rx_lentgh = 16: 0%%$COUNTS:0142%:0000%%$$$$$
rx_lentgh = 8: %$COUNTSTS:0142%:0000%%$$$$$
rx_lentgh = 8: :0000%%$TS:0142%:0000%%$$$$$
rx_lentgh = 8: 计数:0TS:0142%:0000%%$$$$$
rx_lentgh = 24: 142%%$COUNTS:0142%%$COUN$$$$
rx_lentgh = 8: TS:0000%UNTS:0142%%$COUN$$$$
rx_lentgh = 16: %$COUNTS:0000%%$2%%$COUN$$$$
rx_lentgh = 8:计数:0:0000%%$2%%$COUN$$$$
rx_lentgh = 16: 142%%$COUNTS:0002%%$COUN$$$$
rx_lentgh = 8: 0%%$COUNT:0002%%$COUN$$$$
rx_lentgh = 16: TS:0142%%$COUNTS2%%$COUN$$$$
rx_lentgh = 8: :0000%%$%$COUNTS2%%$COUN$$$$
rx_lentgh = 16:计数:0142%%$CO2%%$COUN$$$$
rx_lentgh = 8: UNTS:000142%%$CO2%%$COUN$$$$
rx_lentgh = 24: 0%%$COUNTS:0142%%$COUNTS$$$$
rx_lentgh = 16: :0000%%$COUNTS:0%$COUNTS$$$$
rx_lentgh = 24: 142%%$COUNTS:0142%%$COUN$$$$
rx_lentgh = 8: TS:0000%UNTS:0142%%$COUN$$$$
rx_lentgh = 16: %$COUNTS:0142%%$2%%$COUN$$$$

**The above is edited in my code for readability. A NULL character is replaced with '$' and a '\n' is replaced with '%'

您可以看到,每次它获取数据时,它至少会获得部分好读,有时甚至是整件事。但是里面有很多垃圾。您可以在我的代码中看到,我过滤掉了所有返回错误的读取。可能需要超过 1000 次读取才能获得这么多“好”读取。我真的认为这与时间有关,但即使是时间,我不应该仍然得到 [i]some[/i] 数据吗?

写作也有同样的问题。一次写入什么也不做。循环写入代码 100 次可能最终会将代码下载到 LRF,但在运行该代码后 LRF 几乎根本不起作用,我没有切断电源让它工作并在 minicom 中查看数据再次。

LRF 可以以 200Hz 或 10Hz 的频率发送数据包,具体取决于模式。上面检索到的所有数据都是通过 LRF 以 200Hz 发送数据包完成的。

任何帮助都将不胜感激!在我的其他课程和工作之间,我已经为此工作了几个星期。

【问题讨论】:

  • 在开始怀疑软件之前,您检查过物理连接吗?可能是某个地方的连接不好,从而扰乱了您的通信。
  • 嗯,我一直使用终端应用程序 minicom 进行良好的沟通。我可以运行 minicom,查看良好的数据,运行我的好程序并获得垃圾。然后再次运行 minicom 并获得良好的数据。所以我真的不认为这是联系。

标签: c serial-port hardware raspberry-pi


【解决方案1】:

您的代码存在几个问题。


uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);

您已为非阻塞 I/O 设置端口。
这可能不是你想要的。在返回码检查后添加以下内容:

fcntl(uart0_filestream, F_SETFL, 0);

为了配置阻塞 I/O。


if(uart0_filestream == -1)
    printf("ERROR: Unable to open UART\n\n");

当出现致命错误时,程序应该退出,而不是继续。
当系统调用返回 -1 时,您还需要检查 errno 的值。


tcgetattr(uart0_filestream, &options);
  ...
tcsetattr(uart0_filestream, TCSANOW, &options);

应始终检查来自系统调用的返回代码。


options.c_cflag = B19200 | CS8 | CLOCAL | CREAD;
options.c_iflag = IGNPAR | ICRNL;
options.c_oflag = 0;
options.c_lflag = 0;

这是对 termios 成员的不当修改。
只应执行正确的宏和按位操作。 参考Posix Guide of Serial Programing
您已禁用规范输入处理,这可能不是您想要做的。
您尝试读取的输入是带有行终止的 ASCII 文本,因此请使用规范模式让系统解析每行的末尾。
您现在已将端口配置为原始模式,该模式用于二进制数据或忽略 ASCII 控制字符。

有关配置规范模式的示例代码,请参阅this answer


        rx_length = read(uart0_filestream, (void*)rx_buffer, 255);

同样,当系统调用返回 -1 时,您需要检查 errno 的值。
read() 为您的代码返回 -1 时,errno 可能是 EAGAIN,表示没有可用数据。


如果指定了阻塞 I/O 并且指定了规范 I/O 而不是原始 I/O,则每个 read() 将返回完整的输入行。

【讨论】:

  • 非阻塞和非规范输入做到了!我想我误解了规范和非规范输入是什么。除了现在,一旦我在设备上完成了 5-6 次读取,它就会进入一个奇怪的状态并停止输出,只有电源循环才能修复它。我觉得这很奇怪,因为我可以随心所欲地运行 minicom,而且没有问题。
  • 这可能值得一个新问题来显示您的清理代码和更好的症状描述,例如“它进入一个奇怪的状态”中的“它”是什么。有关规范与原始模式的简短说明,请参阅编辑后的答案。
  • 谢谢,我在这里创建了一个新问题,根据您的建议使用更简洁的代码:New issues
【解决方案2】:

建议 OP 误解了数据 - 它看起来相当不错。

建议重写内循环

// for(loop=0; loop<30; loop++)
for(loop=0; loop<rx_length; loop++)

这样做之后,结果应该会好很多。诀窍在于read() 样本与到达的数据异步发生,因此仅读取消息的部分read() 不知道数据包何时结束。它会在其已满、错误或当时没有更多可用数据时返回。在read() 和数据包端到达之间没有同步。需要重新整合消息。

同步伪代码

i = 0;
length = 0;
forever() {   
  do {
    i += length;
    length = read(&buffer[i])
    if (length means bad read) handle error;
    search buffer from i to (i+length) for End-of-packet
  } while (end-of-packet not found and buffer not too full)
  Use buffer from start to end-of-packet.
  length = i+length-end-of-packet-index
  memmove(&buffer[0], &buffer[end-of-packet-index+1], length);
  i = 0;
}

您可以检查其他类似read() 的函数,这些函数会一直读取到超时。 (也许还有一个不同的open() 选项?

其他小问题

  1. 更多 cmets 在options.c_cflag 中解释选项。

  2. “rx_length”与“rx_lentgh”。

  3. rx_buffer[loop] == NULL 是错误的形式。 rx_buffer[loop]charNULL 是一个指针。使用rx_buffer[loop] == '\0'

  4. 出于调试目的,考虑

.

// printf("%c", rx_buffer[loop]);
if (isprint(rx_buffer[loop])) {
   printf("%c", rx_buffer[loop]);
}
else {
   printf("(%02hhX)", rx_buffer[loop]);
}

【讨论】:

    猜你喜欢
    • 2023-04-05
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 2014-09-14
    • 2019-07-16
    • 2014-12-09
    • 2013-11-05
    相关资源
    最近更新 更多