在Linux中使用C

时间:2018-12-24 14:07:49

标签: c linux raspberry-pi serial-port termios

我一直在尝试使用USB到串行转换器读取与树莓派接口的串行温度传感器的响应。

我可以看到对传感器设备的写入似乎有效。但是,当我尝试从串行芯片回读时,读取失败,结果为-1。

我确实尝试使用Realterm程序使用相同的波特率9600 8位无奇偶校验设置,并且能够按预期方式读取和写入十六进制值,请为我指明正确的方向。

void serial_write(char parameter,char value){
int fd;
uint8_t bytes_wr;
char wr_buffer[3];
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY); 
if(fd == -1)
    ERROR("Error! in Opening ttyUSB0 \n");
else
    DEBUG("ttyUSB0 Opened Successfully \n");

struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);

cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);

SerialPortSettings.c_cflag &= ~PARENB;
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |=  CS8; 
SerialPortSettings.c_cflag &= ~CRTSCTS;
SerialPortSettings.c_cflag |= CREAD | CLOCAL;
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);  
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
SerialPortSettings.c_oflag &= ~OPOST;

if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) 
    ERROR("ERROR ! in Setting attributes \n");
else
    DEBUG("BaudRate=9600\tStopBits=1\tParity=none \n");

wr_buffer[0] = write;
wr_buffer[1] = parameter;
wr_buffer[2] = value;

bytes_wr = write(fd, wr_buffer,sizeof(wr_buffer));
DEBUG("Total Bytes written: %d \n", sizeof(wr_buffer));
close(fd);}

上述功能似乎按预期方式写入了串行端口,但是当我尝试读取读取内容时却以-1失败

char serial_read(char parameter){
int fd,read_length,i;
uint8_t bytes_wr;
char wr_buffer[2];
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY); 
if(fd == -1)
    ERROR("Error! in Opening ttyUSB0 \n");
else
    DEBUG("ttyUSB0 Opened Successfully \n");

struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);

cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);

SerialPortSettings.c_cflag &= ~PARENB;
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |=  CS8; 
SerialPortSettings.c_cflag &= ~CRTSCTS;
SerialPortSettings.c_cflag |= CREAD | CLOCAL;
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);  
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
SerialPortSettings.c_oflag &= ~OPOST;

if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) 
    ERROR("ERROR ! in Setting attributes \n");
else
    DEBUG("BaudRate=9600\tStopBits=1\tParity= none\n");

wr_buffer[0] = read;
wr_buffer[1] = parameter;

bytes_wr = write(fd, wr_buffer,sizeof(wr_buffer));
DEBUG("Total Bytes written: %d \n", sizeof(wr_buffer));
usleep(8000);
tcflush(fd,TCIFLUSH);
char rd_buffer[4];
read_length = read(fd, rd_buffer,sizeof(rd_buffer));
DEBUG("Total bytes read = %d \n",read_length);
for(i==0;i<read_length;i++){
    DEBUG("rd_buffer[%d]=%x \n",i,rd_buffer[i]);
}
close(fd);
return rd_buffer[0];}

使用realterm Windows应用程序,所有读写操作似乎都可以正常工作

1 个答案:

答案 0 :(得分:1)

在open(2)联机帮助页中:

   O_NONBLOCK or O_NDELAY
          When possible, the file is opened in nonblocking mode.
          Neither the open() nor any subsequent operations on the file
          descriptor which is returned will cause the calling process to
          wait.

对于串行连接,最终结果将是:如果您要求从串行端口读取一定数量的字节,并且没有等待字符,则读取将返回-1并且'errno'可能为EAGAIN或EWOULDBLOCK。

因此,您的usleep(8000)可能是试图等待足够长的时间以使设备响应,但设备可能没有适合您的数据;尤其是在进行ADC操作的过程中,可能需要8毫秒以上的时间。

您可以做一些事情:

您可以(使用伪代码):

int retries=10;
while(retries--) {
    read_length = read(fd, rd_buffer,sizeof(rd_buffer));
    if(read_length > 0)
        break;
    usleep(1000);
}

不幸的是,这样做的一个副作用是,如果温度传感器向您发送一个长字符串,而在温度传感器仍在写入时程序read()s,则会得到部分字符串。因此,如果知道等待接收的字符串的长度,则可以使用ioctl()来查找等待等待的字符数:

ioctl(fd, FIONREAD, &bytes_avail);

所以伪代码看起来更像:

int retries=10;
int bytes_avail=0;
while(retries--) {
    if (ioctl(fd, FIONREAD, &bytes_avail) < 0) {
        fprintf(stderr, "ioctl failed\n");
        return;   // Do something here
    }
    if (bytes_avail >= sizeof(rd_buffer)) {
        read_length = read(fd, rd_buffer,sizeof(rd_buffer));
        if(read_length > 0)
            break;
    }
    usleep(1000);
}

如果温度传感器发送以换行符或回车符结尾的ascii字符串,则代码看起来会有所不同。