San*_*Kim 5 c c++ linux serial-port
我在 Linux 中使用串行端口进行编码。
并且通信的要求是5ms inter-byte time。
它要求我在write()调用之前根据字节的值更改每个字节的奇偶校验模式(偶数和奇数)。
所以我编码如下(我简单描述代码)
void setWakeupMode(int fd, bool mode) {
struct termios tio;
bzero(&tio, sizeof(tio));
tcgetattr(fd, &tio);
if (mode == false) {
tio.c_cflag &= ~PARODD;
} else if (mode == true) {
tio.c_cflag |= PARODD;
}
if(tcsetattr(fd, TCSADRAIN, &tio) < 0){
perror("tcsetattr Error");
}
}
int main(){
unsigned char a[2] = {0x01, 0x0F};
write(fd, a, 1);
setWakeupMode(fd, true);
write(fd, a+1, 1);
}
Run Code Online (Sandbox Code Playgroud)
但是代码不满足字节间时间,导致近 20 毫秒。
所以我尝试打印每个系统调用之间的确切时间,如下所示。
int main(){
unsigned char a[2] = {0x01, 0x0F};
printf("write1 start : %s", /*time*/);
write(fd, a, 1);
printf("write1 end : %s", /*time*/);
setWakeupMode(fd, true);
printf("write2 start : %s", /*time*/);
write(fd, a+1, 1);
printf("write2 end : %s, /*time*/);
}
Run Code Online (Sandbox Code Playgroud)
这是结果
write1 start : 34.755201
write1 end : 34.756046
write2 start : 34.756587
write2 end : 34.757349
Run Code Online (Sandbox Code Playgroud)
这个结果突然满足了 5ms 的字节间时间,导致1ms inter-byte time.
所以我尝试了几种方法。
最后我认识到只有当我在 tcsetattr() 之前打印一些东西时,才能满足字节间时间。
例如,如果我删除printf("write1 end : %s, /*time*/);如下
int main(){
unsigned char a[2] = {0x01, 0x0F};
printf("write1 start : %s", /*time*/);
write(fd, a, 1);
// printf("write1 end : %s", /*time*/); //remove this
setWakeupMode(fd, true);
printf("write2 start : %s", /*time*/);
write(fd, a+1, 1);
printf("write2 end : %s", /*time*/);
}
Run Code Online (Sandbox Code Playgroud)
其结果是令人惊讶的不同,请参阅之间的间隔write1 start和write2 start,这是18ms。
write1 start : 40.210111
write2 start : 40.228332
write2 end : 40.229187
Run Code Online (Sandbox Code Playgroud)
如果我使用 std::cout 而不是 printf,结果是一样的。
为什么会出现这种奇怪的情况?
- - - - - - - - - - - - - - - -编辑 - - - - - - - - - --------------
因为我看到了一些答案,所以有些人误解了我的问题。
我不担心printf()开销。
简单地说。
write()用 1byte调用s 但是write()s之间的间隔必须在 5ms 以内write(),我必须使用更改奇偶校验模式tcsetattr()18ms,tcsetattr()几乎在时间被阻塞。printf()或,间隔减少到.std::couttcsetattr()1~2ms也就是说,不知何故,在调用之前的printftcsetattr()化妆tcsetattr()回报堵塞得更快。
- - - - - - - - - - - - - 更新 - - - - - - - - - - - - -----
我在这个问题上取得了进展。
我说我必须缩短printf()或std::cout缩短阻塞时间tcsetattr()。
但它并没有打印影响该问题的内容。
它只需要一些延迟,也就是说,如果我usleep(500)在调用之前放置tcsetattr(),它也会影响字节间时间减少1~2ms,从tcsetattr()更快返回。
我假设,如果我tcsetattr()用TCSADRAIN标志调用,它会等到串行缓冲区中的所有数据都传输完毕,然后更改为我想要的设置。
它可能会延迟一些。
但是如果我专门调用delay,在我调用之前tcsetattr(),buffer状态已经是空的(因为在delay时间内,串行缓冲区中的数据被传输了),所以没有阻塞。
这是我假设的场景,可能吗?
\n\n\n这是我假设的场景,可能吗?
\n
你一定是对的。锯末写道:
\n\n\n\n\nuart_wait_until_sent()中
\ndrivers/tty/serial/serial_core.c了为什么在涉及“耗尽”时对字符之间的间隔进行量化:使用仅毫秒分辨率的延迟来轮询串行端口驱动程序以获取 TIOCSER_TEMP(发送器空)状态。
这几乎是正确的,只是轮询周期分辨率不是毫秒,而是 jiffies:
\n\n while (!port->ops->tx_empty(port)) {\n msleep_interruptible(jiffies_to_msecs(char_time));\n \xe2\x80\xa6\nRun Code Online (Sandbox Code Playgroud)\n\n所以如果 HZ 是 100 并且char_time为 1(最小值),则每 10 毫秒检查一次状态。正如你所写:
\n\n\n但如果我打电话特意延迟,在我打电话之前
\ntcsetattr(),缓冲区状态已经为空(因为在延迟时间内,串行缓冲区中的数据被传输),因此不存在阻塞。
换句话说,如果您将过程延迟足够长的时间,那么当它到达发射器空测试时!port->ops->tx_empty(port)发射器已经是空的,则不会发生睡眠;否则它会休眠至少 1 jiffy。