我有一个通过 USB 连接的 Arduino Uno,使用cdc_acm
驱动程序。可在/dev/ttyACM0
.
Arduino 串行接口的约定是DTR
用于复位信号的信号——当使用集成串行到 USB 适配器时,DTR/RTS/DSR/CTS 信号;或者,当使用 RS-232 电缆时,引脚 4 或 5(可能还有 6 或 8)连接到RESET
引脚。
这种重置途径具有重要的优势,即使不是真正的带外,至少非常接近故障安全(由于通过始终带外串行控制器与非正常用户一起实现- 可控看门狗电路),虽然它可以被物理禁用(通过将电容器或电阻器(取决于型号)连接到RESET
引脚),这样做完全破坏了这个重要的终止开关和所有相关的实用程序。
不幸的是,目前看来,当任何程序出于任何原因连接到 ACM 设备时,Linux 绝对总是发送此信号,并且(与 Windows 不同,)没有提供甚至模糊已知的可靠方法来防止这种情况发生。
(目前-hupcl
,“在最后一个进程关闭 tty 时发送挂断信号”和-clocal
“禁用调制解调器控制信号”都不会阻止每次打开设备时发送此信号。)
当用户态进程打开类似
/dev/ttyS0
or的串行设备/dev/ttyACM0
时,linuxDTR/RTS
默认会提升这些行,并在关闭它时丢弃它们。它通过调用
dtr_rts
由驱动程序定义的回调来实现。不幸的是,还没有任何 sysctl 或类似的东西可以禁用这种烦人的行为(现在很少使用),所以唯一有效的方法是从驱动程序的
tty_port_operations
结构中删除该回调,并重新编译驱动程序模块。您可以
cdc-acm
通过注释掉这一行来为驱动程序执行此操作:这不会阻止您
DTR/RTS
通过串行 ioctl 使用这些行,如TIOCMSET
,TIOCMBIC
,TIOCMBIS
,这将像往常一样acm_tty_tiocmset()
由结构中的 , 等回调处理。acm_ops
其他驱动程序也可以使用类似的技巧;我个人已经将它与
PL2303
usb -> 串行驱动程序一起使用。[差异信息丰富;它不会直接应用,因为这个网站会破坏标签和空格]
我认为有一个很好的解决方法可以解决这个问题。不是从设备 /dev/ttyUSB0 或 /dev/ttyACM0 读取数据,而是从命名管道读取数据,例如 /tmp/arduino 和简单的程序(如下)将数据从设备复制到管道并保存设备打开(从而避免设置 DTR 高)。这也避免了处理从设备读取的所有困难。使用命名管道,可以使用 cat、less -f 之类的工具,或者可以使用任何具有标准 open + read 的程序,而无需发出 ioctl 命令来控制 tty。将设备复制到管道的程序将作为新贵服务运行并将数据从设备复制到管道(并且可能会产生一些日志)。该程序必须处理 SIGPIPE 信号,以避免在关闭任何管道读取器进程时被关闭。由于有意通过 fcntl 对输入设置阻塞,服务器上的负载可以忽略不计。我已经对其进行了测试,它似乎运行良好。我正在尝试解决与 Arduino 接口相同的问题。好的副作用是可以通过在需要时重新启动 upstart 服务来重新启动 Arduino,因为它将 DTR 设置为高。
启动它的 shell 脚本将是(需要重新实现为 upstart 服务):
我认为 logrotate 文件需要使用 copytruncate,因为文件仍然是打开的(我没有机会测试这个):
附加说明:同时我意识到,为了避免管道损坏,可能可以通过使用 cat 或 dd 结合带有参数 PIPE 的 trap 命令来实现,这也将过滤 SIGPIPE 信号。上面的解决方案对我有用,所以我没有尝试使用 trap 命令来获得可比较的结果。