我在Pi上使用RS-232线与激光测距仪进行通信。我已经使用minicom以19200的波特率测试了两者之间的通信(因为这是LRF的波特率并且无法更改),并且它工作正常。虽然向下写入LRF任何命令(由单个字符组成并按下'enter')可能需要多次尝试才能生效,但双向通信效果很好。
然而,当我开始用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。返回的数据应如下所示:
COUNTS: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]数据回来吗?
写作有同样的问题。单个写入什么都不做。将代码循环100次可能最终将代码下载到LRF,但是在运行该代码之后LRF几乎完全无法工作,我不必削减功能以使其工作并在minicom中查看数据试。
LRF可以发送200Hz或10Hz的数据包,具体取决于模式。上面检索的所有数据都是使用LRF以200Hz发送数据包完成的。
任何帮助都将非常感谢!我在其他课程和工作之间已经工作了几个星期。
答案 0 :(得分: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()
将返回一个完整的输入行。
答案 1 :(得分:0)
建议OP错误地解释数据 - 看起来相当不错。
建议重写内循环
// for(loop=0; loop<30; loop++)
for(loop=0; loop<rx_length; loop++)
执行此操作后,结果应该会更好。诀窍是read()
样本与到达数据异步发生,因此只读取消息的部分。 read()
不知道数据包何时结束。它在完整,错误或当时没有可用数据的情况下返回。 read()
与数据包结束到达之间存在 no 同步。需要重新整合消息。
同步伪代码
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
表格不好。 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]);
}