RaspberryPi RS-232麻烦
我在Pi上使用RS-232线与激光测距仪进行通信。 我已经使用minicom以19200的波特率测试了两者之间的通信(因为这是LRF的波特率并且无法更改),并且它工作正常。 虽然写下LRF任何命令(由单个字符组成并按’enter’)可能需要多次尝试才能生效,但双向通信效果很好。
但是,当我开始使用C代码编程以使用RS-232进行读写时,它的一半工作。 这是我正在使用的代码:
#include #include #include #include //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 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:COUNTS: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:COUNTS: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:COUNTS:0:0000 %% $ 2 %% $ COUN $$$$
rx_lentgh = 16:142 %% $ COUNTS:0002 %% $ COUN $$$$
rx_lentgh = 8:0 %% $ COUNUNTS:0002 %% $ COUN $$$$
rx_lentgh = 16:TS:0142 %% $ COUNTS2 %% $ COUN $$$$
rx_lentgh = 8 :: 0000 %% $%$ COUNTS2 %% $ COUN $$$$
rx_lentgh = 16:COUNTS: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]一些[/ i]数据吗?
写作有同样的问题。 一次写入什么都不做。 将编写代码循环100次可能最终将代码下载到LRF,但是在运行该代码之后LRF几乎完全不起作用而且我没有削减function以使其工作并在minicom中查看数据再次。
LRF可以发送200Hz或10Hz的数据包,具体取决于模式。 上面检索的所有数据都是使用LRF以200Hz发送数据包完成的。
任何帮助都将非常感谢! 我在其他课程和工作之间已经工作了几个星期。
您的代码有几个问题。
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指南 。
您已禁用规范输入处理,这可能不是您想要做的。
您尝试读取的输入是带有行终止的ASCII文本,因此请使用规范模式让系统解析每行的结尾。
您现在已将端口配置为原始模式,该模式用于二进制数据或忽略ASCII控制字符。
有关配置规范模式的示例代码,请参阅此答案 。
rx_length = read(uart0_filestream, (void*)rx_buffer, 255);
同样,当系统调用返回-1时,您需要检查errno的值。
当read()
为您的代码返回-1时, errno可能是EAGAIN,表示没有可用的数据。
如果指定了阻塞I / O和规范I / O而不是原始I / O,那么每个read()
将返回一个完整的输入行。
建议OP错误地解释数据 – 看起来相当不错。
建议重写内循环
// for(loop=0; loop<30; loop++) for(loop=0; 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()
选项呢?
其他小问题
-
options.c_cflag
更多注释来解释选项。 -
“rx_length”vs“rx_lentgh”。
-
rx_buffer[loop] == NULL
是不好的forms。rx_buffer[loop]
是一个char
。NULL
是一个指针。 使用rx_buffer[loop] == '\0'
。 -
出于调试目的,请考虑
。
// printf("%c", rx_buffer[loop]); if (isprint(rx_buffer[loop])) { printf("%c", rx_buffer[loop]); } else { printf("(%02hhX)", rx_buffer[loop]); }