我在 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);
}
但是代码不满足字节间时间导致将近20ms。
所以我尝试打印每个系统调用之间的准确时间,如下所示。
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*/);
}
这是结果
write1 start : 34.755201
write1 end : 34.756046
write2 start : 34.756587
write2 end : 34.757349
这个结果突然满足5ms字节间时间,导致1ms字节间时间
。
所以我尝试了几种方法。
最后我认识到,只有当我在 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*/);
}
结果出奇的不同,看write1 start
和write2 start
的间隔,
它是 18ms
。
write1 start : 40.210111
write2 start : 40.228332
write2 end : 40.229187
如果我使用 std::cout 而不是 printf,结果是一样的。
为什么会出现这种奇怪的情况?
----------------------------编辑------------ --------------------
因为我看到了一些答案,所以有些人误解了我的问题。
我不担心 printf()
开销。
简单的说。
- 我想用1byte调用
write()
但是write()
的间隔必须在5ms以内 - 并且在调用
write()
之前,我必须使用tcsetattr()
更改奇偶校验模式
- 但是间隔结果是
18ms
,几乎在tcsetattr()
处被阻塞。 - 但是如果我在
tcsetattr()
之前调用printf()
或std::cout
,间隔会减少到1 ~2 毫秒
。
也就是说,不知何故,在 tcsetattr()
之前调用 printf 会使 tcsetattr()
更快地从阻塞中返回。
------------------------更新---------------- ----------
我在这个问题上取得了进展。
我说过我必须放置 printf()
或 std::cout
来缩短 tcsetattr()
上的阻塞时间。
但它并没有打印出影响那个问题的东西。
它只是需要一些延迟,也就是说,如果我在调用 tcsetattr()
之前放置 usleep(500)
,它也会影响字节间时间减少 1~2ms
,从 tcsetattr()
返回更快。
我假设,如果我用 TCSADRAIN
标志调用 tcsetattr()
,它会等到串行缓冲区中的所有数据都已传输,然后更改为我想要的设置。
这可能会造成一些延迟。
但是如果我专门调用delay,在我调用tcsetattr()
之前,buffer状态已经为空(因为在delay期间,串口buffer中的数据被传输),所以有没有阻塞。
这是我假设的情况,可能吗?
最佳答案
this is the scenario i assume, is it possible?
你一定是对的。木屑写道:
uart_wait_until_sent() in
drivers/tty/serial/serial_core.c
explains why the interval between characters is quantized when a "drain" is involved: the serial port driver is polled for the TIOCSER_TEMP (transmitter empty) status using a delay with only millisecond resolution.
这几乎是正确的,除了轮询周期分辨率不是毫秒,而是jiffies:
while (!port->ops->tx_empty(port)) {
msleep_interruptible(jiffies_to_msecs(char_time));
…
因此,如果 HZ 为 100 且 char_time
为 1(最小值),则每 10 毫秒检查一次状态。如您所写:
but if i calling specifically delay, before i call
tcsetattr()
, the buffer state is already empty(because during the delay time, the data in serial buffer is transmitted), so that there is no blocking.
换句话说,如果你延迟这个过程足够长的时间,当它到达发射器空测试时 !port->ops->tx_empty(port)
发射器已经是空的,不 sleep 发生;否则它至少会休眠 1 分钟。
关于c++ - 带有 TCSADRAIN 标志的 tcsetattr() 阻塞时间很奇怪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27499501/