我在 Windows 上使用 FTDI D2xx 驱动程序在 BitBash 模式下运行 tcl8.5/tk8.5 应用程序(问题发生在 XP、7 和 8 上)。还使用 ftd2xx c 扩展来访问 FTDI dll。
我有一个用户的报告,该应用程序最初运行良好,但在一天不使用后,在后台运行,它突然从 5M 增长到 100M 并开始消耗 99% 的 cpu。(情况不妙!)
在此之前我遇到过 USB 问题,特别是如果 USB 是“热拔出”的。它可能会导致应用程序被阻止并且 Windows 无法杀死它。我的应用程序从不需要读取 USB,它只是写入(控制它),但我发现在 BitBash 模式下,FTDI 芯片会发送回连续的数据流。处理读取会清除读取缓冲区,因此如果 USB 被拔出,则没有待阻止的读取,我可以优雅地退出。
但是现在,我认为 readhandler 给我带来了问题。我编写了一小段测试代码,模仿了我认为可以解决问题根源的实际应用程序。无论如何,我不理解 tcl 的行为。
这是代码:
package require -exact ftd2xx 1.2.1
# define a read handler
# gets called by a fileevent readable
#
proc readchan {} {
variable cnt
set len [gets $::handle buf]
# buffer is non zero
if {$len > 0} {
incr cnt
set end [eof $::handle]
puts "$len ($cnt) eof $end";
# if it wasn't an eof output the buffer
if {!$end} {
puts $buf
}
} elseif {$len == 0} {
# was a zero length read
puts -nonewline "0"
set end [eof $::handle]
if { !$end } {
# eof makes the $len invalid?
puts "-0"
#puts "\nlen is 0 and eof is $end - exit!";
#exit
}
} else {
# len was negative (-1) so data in buffer but no end of line (in binary mode)
if { [eof $::handle] } {
puts "EOF w/len 0"
} else {
puts -nonewline "."
}
}
}
# main code
#
# find the usb device and open it
set usb [ftd2xx list]
lassign $usb d
lassign $d d id e loc f serial
puts "found USB $serial"
set handle [ftd2xx open -serial $serial]
puts "Opened USB $handle"
#
# configure it for bitbash mode and for binary, non blocking
#
set bitmode 0xFF01
chan configure $handle -tranlation binary -bitmode $bitmode -blocking 0
fileevent $handle readable readchan
# output something to the usb every 100ms or so required to cause failure
#
while {1} {
set continue 0
after 100 {set continue 1}
# putting:
# "a" made it crash after 245 "timeouts" - no flush
# null made it past 255 - no flush
# "aa" made it crash after 164 "timeouts - no flush
puts $handle "aa"
#flushing $handle makes it crash after first flush
#flush $handle
puts -nonewline "w"
vwait continue
}
以下是上述代码的输出:
$ tclsh85 usbfailtest.tcl
found USB AH009L40
Opened USB ftd2xx0
w...w....w....w....w....w....w...w....w.126976 (1) eof 1 <<< 126K length with eof true
..w....w....w....w...w....w....w....w..126976 (2) eof 1 <<< "w" output for usb write
.w....w....w...w....w....w....w....w...126976 (3) eof 1 <<< this is 3rd non-zero read
w...w....w....w....w....w...w....w....w.126976 (4) eof 1 <<< "." output when len -1
..w....w...w....w....w....w....w...w...126976 (5) eof 1 <<< line takes about 1 sec
w....w....w....w....w...w....w....w....w126976 (6) eof 1
.
. (output skipped)
.
.w....w....w....w...w....w....w....w...126976 (156) eof 1
w....w....w...w....w....w....w....w....w126976 (157) eof 1
..w....w....w....w....w....w...w....w..126976 (158) eof 1
.w....w....w...w....w....w....w....w...126976 (159) eof 1
w....w...w....w....w....w....w....w...w.126976 (160) eof 1
..w....w....w....w....w...w....w....w..126976 (161) eof 1
.w....w....w....w...w....w....w....w...126976 (162) eof 1
w....w...w....w....w....w....w....w...w.126976 (163) eof 1
..w....w....w....w....w...w....w102695 (164) eof 0 << a 102K buffer with EOF false
aa << all but two of the 102K buffer were null?
0-0 << zero length buffer with no eof
2 (165) eof 0
aa << two length buffer, no eof, and we got two chars
3 (166) eof 0
aaa << three length bufffer, no eof, and we got three chars
2 (167) eof 0
aa << etc.
0-0 << another zero zero these scroll out *very* fast.
0-0
0-0
0-0
2 (168) eof 0
aa << an so forth
0-0
0-0
.
. (output skipped)
.
43 (268) eof 0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
..w...22192 (269) eof 1
w...w....w....w....w....w...w....w....w.126976 (270) eof 1 << revert to "slow" mode
..w....w....w....w...w....w....w....w..126976 (271) eof 1
.w....w....w...w....w....w....w....w...126976 (272) eof 1
w....w...w....w....w....w....w....w...w.126976 (273) eof 1
..w....w....w....w....w...w....w....w..126976 (274) eof 1
输出说明:
首先,读取处理程序被多次调用(每个“.”),缓冲区不完整(长度 -1)并且没有 eof 条件。最终,某些事情要么超时,要么达到内部缓冲区限制,读取被迫完成。同时提出了一个EOF。“...126987”的每一行需要大约一秒钟的时间来写。
在一定数量的 EOF 之后(即 ....126987 的行 - 在这种情况下为 164 - 非常可重复),取决于写入通道的数据量以及是否刷新通道,在没有 EOF 的情况下完成读取(第 164线)。
到目前为止,读取事件中断率是可以容忍的,但随后它会猛增并消耗大量处理它的周期。在较慢的机器上,没有时间做任何有用的事情。
我对此有很多难以表达的问题。但首先:
我不明白为什么读取处理程序会在 0 字节未决和非 eof 条件下被调用。缓冲区中是否必须至少有一个字节才能“可读”?
我不明白为什么 EOF 是瞬态的。我希望USB端口上的EOF意味着它被拔掉了,但事实并非如此。
如果我在写入 USB 后刷新通道,我得到的只是 0 长度读取,即使我终止应用程序并重新启动它,这种情况仍然存在。清除此问题(进入慢速模式)的唯一方法是从 USB 中拔出设备并重新开始(不刷新)。我不知道该怎么做。我必须刷新通道才能让写入实际进入 FTDI 芯片。
我应该为此使用固定长度的读取而不是获取吗?