Linux下串口通信,很多坑,例如你要读取50字节,很可能对方发送没那么快,结果只读取到30个字节,又有可能对方没有正确响应,导致这边读取吊死。
下面的代码是最好的方式,即可以保证读取到给定长度的字节,又可以保证在超时后一定返回:
/**
* 从文件设备中读取数据,按给定的长度读取,并在指定的时间内超时返回
* 本函数适合从串口读取到给定长度的数据,一般情况下串口读取不一定读到完整数据
* @param fd 文件设备句柄
* @param buf 缓冲区
* @param size 读取的字节数
* @param useconds 超时返回微妙数
* @return
*/
bool readex(int fd, void *buf, size_t size, int useconds) {
int left = size;
int readsize = 0;
long long interval = 0;
struct timeval tvBegin, tvNow;
gettimeofday(&tvBegin, NULL);
do {
readsize = read(fd, buf, left);
if (readsize > 0) {
buf += readsize;
left -= readsize;
}
gettimeofday(&tvNow, NULL);
interval = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
// LOGD("READEX", "读取到 %d 字节,超时: %d", readsize, interval);
if (interval > useconds) break;
usleep(8*100); // 115200波特率 = 1000000(1s) / 115200 = 8 微秒, 100字节一次提高效率
} while (left > 0);
return size - left;
}
下面是调用代码:
char buf[100]={0x00};
fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) return -1;
tcgetattr(fd, &mode);
mode.c_cc[VMIN] = 1;
mode.c_cc[VTIME] = 0;
mode.c_cflag |= CS8;
mode.c_cflag &= ~CRTSCTS;
mode.c_cflag &= ~CSTOPB;
mode.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入模式
mode.c_cflag &= ~PARENB;
mode.c_iflag &= ~INPCK;
if (cfsetispeed(&mode, B115200) < 0 || cfsetospeed(&mode, B115200) < 0)
return -2;
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSANOW, &mode);
len = readex(fd, &buf, sizeof(buf), 1000*1000);
if (len != sizeof(buf))
return false; // 读取不足
else
读取到了所有字节,该干嘛干嘛!