Using a K8055 I encounter problems reading the digital inputs. My Visual Basic program reads a digital channel and reports a high while I see it is definitely low. Only when I wait for about a second after changing the input, it is read correctly… most of the time. The outputs work fine and I do not use other parts of the board so far. It does not always go wrong. The first changes after starting the board are reported correctly even without long delays. Using 1s delays the it goes wrong after twelve changes or so. Does anyone know the cause of this problem or, even better, knows a solution?
I am in the process of adding legacy K8055 support to the K8055M project. The K8055M client library uses a separate thread (per card) to receive the HID reports as fast as possible. Right now it only works with the open source PIC18F2550/2455 firmware.
Three consecutive reads and a 330ms delay seems to give the right input… most of the times (again). When two digital inputs change shortly after each other, only the first one is seen. The second one is never seen no matter what delays or repeating input readings I use. Up to 34 consecutive reads report a high while the input is definitively low. Changing another inputchannel also makes no difference. So I suppose the micro does not read the inputs but only reports the last changes it found (due to interrupt on change?).
Although I’m not too shy to program a PIC, but I bought the card not to have to go al the way along that route. So I’d be very glad if anyone knows a trick to make the micro do its job.
According to the HID descriptors, the K8055 sends as fast as 10ms apart. The default ring buffer size for HID reports in Windows is 32 messages. A full ring buffer therefore is pretty close to your 330ms. But as you see from your tests, doing multiple reads and delays is basically playing hit and miss for the correct data with the Windows HID ring buffer.
The K8055D.dll uses the Windows API function ReadFile() to receive the actual HID reports one by one. When there is something in the ring buffer, the ReadFile() will return immediately with the (possibly old) data. But if Windows doesn’t have anything in the ring buffer, it will block and wait until the card sent another report (or an error occurs, like disconnect).
This is a classic example for the need of a separate reader thread. In VB I believe it is called a BackgroundWorker (I’m not a VB developer, but I am pretty good at multithreading in C with Posix threads).
The K8055D.dll opens the USB device twice. Once for reading, the second time for writing. So it has two separate handles open in shared mode. There should be no problem with the BackgroundWorker (thread) getting the input data all the time, while the main application is sending output commands whenever it wants to change some output.
I didn’t use the 330ms delay between the consucutive reads, I tried various delays before start reading and then I did the consecutive reads. In one of the tests I did 34 consecutive reads of all didgital input but although the input of the specific channel was kept low, all the reads reported a high. If that channel had not read highs as well as lows before I’d thought it to be stuck at zero. (Remind the inverting buffers.)
BTW, when that USB ringbuffer overflows, does it flush the oldest content or does it block the new stuff?
Usually I’m programming C and C++ but this time I have to use VB as others need to understand and maintain the code. But in both cases I don’t know how to get the handle(s) of the DLL which is needed to use the ReadFile() API.
I’ll have a look at that backgroundworker to see what it can do for me.
Nevertheless, I keep the feeling that the micro fails but not knowing the firmware inside, I may be wrong.
AFAIK it overwrites the oldest message. So with an HID device that sends a report every 10ms (like the k8055) once the ring buffer is full, you will always get a 320ms old report unless you read faster than every 10ms.
You can’t get at them and I didn’t mean to suggest that you operate on the DLL’s file handles directly. I said that the DLL does open the device twice to have separate handles for reading and writing. From the Delphi code that I have seen (K8055D.DLL version 4) it seems to me that the DLL is implicitly thread safe as long as you only have one thread calling input functions and one other thread calling output functions. Doing the input part in the BackgroundWorker and let it signal events as needed to the main application thread.
So you would first open the card, then launch the BackgroundWorker and have it constantly read the inputs. That should keep the HID ring buffer empty.
Then again, the K8055D.DLL in version 4 is rather inefficient. I didn’t look at version 5 yet. The problem is that although the K8055 sends all input data (digital, analog and counter values) in one HID report, the DLL does not have a function to get all that at once into your program. To get all that information you have to make 4 function calls. 1x ReadAllAnalog(), 1x ReadAllDigital() and 2x ReadCounter(). Each of those functions causes the DLL to call ReadFile() to receive another HID report. So all 4 calls will eventually take 40ms, you only get every 4th value sent by the card and each value is from a different HID report.
You see why I think I should add legacy K8055 support to the client library of the K8055 open source project?
I had a look at the backgroundworker but consider it overkill. I suppose a timer set to - let’s say - 9ms will do all I need. If I need something like that.
For the moment I concentrated on the digial inputs and found that there must be a bug in the firmware.
I have two input- and two outputchannels in use, so the channels 1 and 2. I connected the outputs to the corresponding inputs to be able to read back my own output and also to connect an external bi-directional I/O-line to them.
Now when both outputs are low and they are pulled high one at a time, the inputs follow as does the reading of that inputs. But when one of the outputs is pulled low again and as a reaction an external device pulls down the other immediately that last event is missed completely. Athough the input is definitively low, I can read it millions of times but the K8055 reports to have read a high to all eternity… I suppose
As a workaround I added a piece of hardware that passes the falling edge to the external device immediately but delays the passing of that edge sent to the inputchannel. 1ms seems to be enough.
Just to make sure we are on the same page on everything:
The K8055 digital outputs O1…O8 are open collector outputs. They do not output 0V/5V, they provide a high resistance to ground for OFF or a very low resistance to ground for ON.
Likewise the digital inputs I1…I5 expect to be operated by something like a switch or an open collector. You activate them by shorting I1…I5 to ground, not by applying any voltage.
This means that if you simply connect O1 to I1 with a length of cable, the value you read for I1 via ReadDigitalChannel(1) should reflect the last state set to O1 via any of the digital output functions. After a short delay like 10ms or more, of course.
If you don’t have anything else connected to the whole thing, is that what happens?
From this I understand that you have something else connected to those inputs in parallel, and under some circumstance that “something else” also “pulls down” one or more of the K8055 input lines.
“Pull down” in electronics is usually associated with connecting to ground, evtl. through some resistor, in order to get the voltage “low”. That is actually the state, the K8055 interprets as “1” when it occurs on one of the digital input connectors. Does your external “something else” actually conform to this, or is it designed to connect the K8055 input to ground to mean “off”?
Thanks for your reply. As I’m in (digital) electronics for quite some years, I’m sure we’re on that same page. I looked at the datasheet of the ULN2804 to find out about that I/O things. So I added pull up resistors to get voltage outputs. I also found the active low nature of the I/O but that’s easy to solve in software.
However my knowledge of USB is limited and I wanted to find out why my circuit did wrong. So I learned about the 10ms scan time and the circular buffer. With that kowledge I could modify my program to find out what was really going wrong.
As the problem proved to be in the micros firmware, to which I have no access, I had to find a workaround.
I read about replacing the OTP PIC by a corresponding flash type. A great idea. But although I know about PIC programming I suppose my contribution can only be as low as my knowledge of USB.