Open8055 - K8055/K8055N based Open Source Firmware

After playing with a PIC18F2550 on a K8055 board for several months now, I finally ordered, assembled and (of course) modified a K8055N as well. A few initial comments about that:

[ul]
[li]I have learned a lot about USB since I started working on this. And that is an understatement.[/li]
[li]The original name for the Open Source Firmware was probably very confusing. I hope calling it “Open8055” from now on will be better.[/li]
[li]The K8055N kit is made in the same high quality kit standard as the K8055 was done. It just worked as advertised after assembly. No surprise there. Two thumbs up for Velleman from here.[/li]
[li]Although their optical appearance is very similar, the K8055N has a few differences to the K8055. Nothing that can’t be dealt with in #ifdef/#else/#endif sections if we are talking about the basic K8055(N) functionality. [/li]
[li]The PIC18F24J50, used on the K8055N, is more different to a PIC18F2550 than I initially thought. It has no EEPROM, it uses different registers to configure ADC and other things, some IO pins are shared differently … oh well, we’ll figure the rest out as we go.[/li]
[li]I decided to start over from scratch, so forget anything you read about the K8055 Open Source Project so far.[/li][/ul]

A new approach.

As said, things are to be called Open8055 from now on.

I have started a new software tree from scratch and am incorporating features from the old project bit by bit. What I have so far:

[ul]
[li]A boot loader for the K8055 board converted to PIC18F2550. The application (Open8055 Firmware) is loaded at 0x1200. The boot loader activates if sk5+sk6 are set for card address 3.[/li]
[li]A boot loader for the K8055N board with a PIC18F24J50 (as shipped). The application (Open8055 Firmware) is loaded at 0x1400. The boot loader activates if sk5+sk6 are set for card address 3. This boot loader can probably be modified to load the Firmware at 0x1200.[/li]
[li]A rudimentary Open8055 Firmware that can be compiled for both PICs and loaded with the above boot loaders. It identifies as idProduct 0x55F0…0x55F2 (card address 0, 1 or 2) according to the sk5, sk6 configuration. The firmware is blinking output 8 once per second and is reflecting the 5 input ports on the digital output ports 1…5, so I know that the Timer3 code and digital I/O is working. Well, it also (and most importantly) confirms that the boot loaders as well as the firmware code itself are working PIC and board independent to this point.[/li]
[li]The firmware is already using the low priority interrupt vector for USB communication, so that’s out of the way too.[/li][/ul]

What’s next?

I need input for the Open8055.dll API. I didn’t even start writing that code yet. So you are scribbling on a blank white-board for that one.

Any comments are welcome.

Regards,
Jan

A few thoughts about the new client library and API:

[ul]
[li]Using a separate IO-thread inside the library to avoid stale HID reports in the ring buffer has proven useful. The new library will expand on this concept. *1)[/li]
[li]To support a wide variety of programming languages, the API must avoid using pointers and C structs.[/li]
[li]Avoiding pointers may require to use multiple API calls to retrieve all information. But this must NOT lead to waiting for multiple HID reports to be received.[/li]
[li]At the same time a busy loop style application should not hog a CPU. This all means that a code fragment like this:

while (TRUE) { var1 = Open8055_ReadDigitalAll(cardHandle); var2 = Open8055_ReadAnalog1(cardHandle); var3 = Open8055_ReadAnalog2(cardHandle); // do something with the values }should cause one full loop iteration per HID report received.[/li]
[li]Likewise multiple API calls to change output ports or configuration settings should automatically be folded into one HID command. *2) [/li]
[li]The library and API design must support “remote” cards. An Open8055 card may be connected to a computer running a “service”, that lets other computers access the card over the network.[/li][/ul]

Notes:

[ol]
[li]USB is a rather crappy IO subsystem by definition. The host computer is constantly “polling” all attached devices for messages. The interval it polls them at is fixed in the device’s USB descriptors. A device cannot be polled faster than once per millisecond and the host cannot send messages faster than once per millisecond either. This means that in order to be able to deliver a change on a digital input within no more than 2ms delay to the application, the Open8055 USB descriptor must use 1ms polling interval.

Short of using bulk transfer, there just is no way to “send” anything asynchronously to the host computer. And that would be getting rather messy.

The 2ms latency stems from the following. Imagine a digital input being OFF. The PIC “queue’s” an HID report with that information and one microsecond later, the input comes ON. It can take up to 1,000 microseconds (1ms) for the host to poll, but it then will still receive the old “OFF” state. The PIC can only queue another HID report (containing the correct value) after the first one had been delivered, and that message will not be received by the host computer before 2ms have elapsed. I believe that not queuing HID reports unless 800 or 900 microseconds have elapsed since the last polling can improve the effective response time of the Open8055.

All this of course assumes that the host is actually polling at that rate. My O-Scope tells me that a Windows computer under load doesn’t do that.

[/li]
[li]Since the host cannot send commands faster than once per millisecond either, it doesn’t make sense to queue a command message for each Open8055_SetDigital(cardHandle, port, value) API call either. It makes more sense to collect all those changes and send them all together in one HID command once per millisecond, if there were any changes at all. [/li][/ol]

Comments?
Jan

Having been awfully quiet for the past couple of weeks. Sorry.

I recently got my first samples of the PIC18F25K50. These chips are meant to replace the PIC18F2550. They are pin compatible but gave me a little headache first. Apparently, even the latest Microchip Application Libraries aren’t compatible with them yet.

Having zero input from anybody, this is what I decided to do:

[ul][li]Open8055 will not support the “J” family of PICs. They lack EEPROM and I intend to use that to save startup-config settings.[/li]
[li]Open8055 will run on a P8055-1 board, converted to 4 MHz and PIC18F.[/li]
[li]Open8055 will support PIC18F2550 and PIC18F25K50 chips.[/li]
[li]Open8055 will support P8055-1 and P8055N-2 boards.[/li][/ul]

Regards,

Jan

I think everyone is going to like the PIC18F25K50. It has a built in oscillator that can be used for high speed USB operation. It works on a K8055/K8055N even if no crystal is present at all. This means that running Open8055 on a K8055 requires the removal of one resistor. And running it on a K8055N requires no change at all. Just replace the PIC.

I wonder if turning off the internal USB pull up resistor would allow to use the chip on a completely stock P8055-1 board.

My bad. Should have read the USB specs first. Pulling D- to +3.3V signals to the host “Low-Speed”. We need to be “Full-Speed”, so that resistor must be removed.

Anyhow, it turns out that there are very few differences to the good old PIC18F2550. At this point I have ONE bootloader and ONE firmware source. Each can be compiled for both PICs and both boards (8 .hex files total). The PIC18F2550 runs on both boards with a 4 MHz crystal while the PIC18F25K50 ignores the crystal altogether and runs on its internal oscillator.

As a side note, the “K” chips seem to require a PICKIT-3 or compatible programmer. PICKIT-2 is grayed out in the MPLAB IDE when the device is set to the “K”. Both chips can be programmed via the ICSP on the K8055N board.

I should have a set of bootloader, firmware, library and some demo code together in a week or two.

Hi Jan

I recently bought a PIC18F2550 demo board. This board is different from the K8055 in a number of ways: e.g. I have a mini joystick on ports RB0-4 and 4 LED’s on ports RA0-3. I downloaded your DLL and accompanying VC sources. I wrote my own bootloader (well mostly copied from microchip). I had to hack your firmware of course and I also managed to switch the inputports and output ports (cause mine are different as mentioned). So far so good, I can now switch on/off LED’s and when I handle the joystick I see this in the open8055demo you provided. Then I connected a potmeter to the analogue input RA0 and that is also working!

Good job you’ve done, it really has helped me understand things.

One question though:
The digital outputs can be switched to Servo or Iservo. Now mnemonically I guess this is for servo motors. However, if I e.g. set ouput 4 to SERVO and watch the signal on the output pin (oscilloscope) nothing seems to happen?
And what’s the diff between Servo and Iservo?

Thanks for your input.

Thank you.

[quote=“Beach”]One question though:
The digital outputs can be switched to Servo or Iservo. Now mnemonically I guess this is for servo motors. However, if I e.g. set ouput 4 to SERVO and watch the signal on the output pin (oscilloscope) nothing seems to happen?[/quote]
The servo function is meant to drive a standard hobby servo with 40 pulses per second. I guess that is what you mean with “servo motor”. Be careful with this functionality. The firmware and DLL code allow for a wider signal range than many hobby servos operate on. Find the range of your servo and limit the values in your software. I have tested that functionality with a Futaba S3003 which accepts pulses from around 500 to 2,500 microseconds.

The servo code does not use the input/output pin assignments from the header file. It probably should.

If you look at the code in main.c, function highPriorityISRCode(), it uses PORTB hard coded there (all 8 pins of PORTB are used as digital outputs on the K8055).

[quote=“Beach”]And what’s the diff between Servo and Iservo?

Thanks for your input.[/quote]
One mode keeps the output pin low by default and generates high pulses. The other is inverted, keeping the pin high by default and generating low pulses. On the K8055 there is a NPN Darlington transistor connected to each output port, so the output becomes an open collector. Using that with a pull up resistor means signal inversion.

If you connect the control line of a servo directly to a PIC pin, you don’t have that signal inversion and want a default low, pulse high signal.

Regards,
Jan

Thanks for the explanation. I’ll have a look at the code. The hardcoding would explain why my setup is not working cause portB is input in my setup. PortA is output.
This is pretty inconvenient because when you use analogue inputs they will start with portA, so portA should be an input. That teaches me to check before you buy (although I got a pretty good deal, I’m not unhappy with the board).

Now my next experiment will be to expand your demo code and see whether I can get an analogue output as sort of an oscilloscope function pictured on the PC-screen.

Have you any idea how many people are using your code? We might exchange code/ideas.

Regards

It would perfectly explain it. Writing to an input port does nothing.

The demo boards all have one or the other gotcha on them. The K8055 for example uses PORTB as outputs, which is nuts because PORTB is the only digital input that can generate interrupts. And it uses the PORTC pins, needed to communicate via I²C, for digital inputs, so you can’t experiment with serial communication. Granted, the K8055 first of all was designed for a PIC16C745. And it is not sold as a PIC demo board to begin with. Exchanging the PIC and then saying that I can’t use all of its functionality is not a valid complaint.

Take them for what they are. DEMO boards meant to do your first steps. They implement some test signals already fixed connected to specific pins. Since on these PICs many pins have multiple “possible” functions, this limits how far you can get with them on your learning curve.

Sooner or later you’re going to etch your own PCBs anyway.

While possible in principle, this will have serious limitations.

The USB protocol used here limits the number of measurements to 1,000 per second. To see anything like a waveform, you need 10-20 measurements per period, so this won’t work well for frequencies above 50 Hz.

You can change the protocol of course and have the PIC collect multiple measurements per report at a higher interval. But in the end the PIC18F series simply doesn’t have a high speed ADC like the ones found in digital storage oscilloscopes.

It is still a nice experiment, teaching the basics. Just don’t expect to get a real oscilloscope out of it at the end.

I have no idea at all. With the code being on github, I don’t even know how often it was downloaded.

Obviously I do like to exchange code, ideas and knowledge. I like it so much that I spent several hours of my spare time to prepare a talk that I had in front of the Linux User Group in Toronto last week. The topic was “Building USB Devices and using them with libusb in Linux/FreeBSD”. A very interesting bunch of people. In the future I will try to align my trips to Toronto so that I have dinner and fun plans for Tuesday nights.

One of my next things to build is an infrared remote control protocol analyzer and generator. Nothing new, I know, but I just like to do things from scratch and see how far I can get on my own.

Regards,
Jan

I like your style, quoting the subtopic and then handling each one. I am lazy so I use what they call TOFU. Some find it offensive, so pls know that no offense is intended.

I recoded the hardcoded part of the Servo interrupt function, and that worked. Thanks for the pointer.
The demo board I have has ALL of its port pins directed to pinheaders. Not bad at all.

As for the scope function: I know that USB polls only once every msec. However I read somewhere that the 18F2550 is capable of a (theoretical) 1.3Msamples/s. The trick is -as you already outlined- to store the analogue values and then send them in a package every poll. I will focus on that.

I fail to see the use of your next project, but who am I to question that. Climbing a mountain is performed ‘‘because it is there’’. I also purchased a raspberry Pi and later on purchased a TSOP4838. Together with LIRC that functioned pretty good. I have no doubt that a PIC controller can do the job. So my advice is to purchase a TSOP4838 (try ebay worldwide). My tuppence worth

[quote=“Beach”]I recoded the hardcoded part of the Servo interrupt function, and that worked. Thanks for the pointer.
The demo board I have has ALL of its port pins directed to pinheaders. Not bad at all.[/quote]
Glad it works.

Where do you see 1.3Msamples/s?

I go by the data sheet. I think if you violate the specs a little, you can get under 10µS per A/D for 8 bits. The violation is that Parameter 130 in Table 28-29 specifies 0.7µS minimum TAD. We are using 48MHz FOSC. At FOSC/32 that is only 0.667µS. To be within the specs you’d need FOSC/64 which leads to 1.333µS TAD, which at 8 bits means 12µS TCNV (one extra TAD for discharge) plus 1.4µS TACQ. That is a total of 13.4µS resulting in a theoretical maximum sampling frequency of about 74kHz with 8 bits precision and 62kHz at 10 bits. With the spec violation the A/D will probably still work good enough, so then you get to some 100kHz sampling rate, or a little above.

As said, to see a waveform, you need 10 or more samples per period of the signal. So that OScope may be working well for signals up to 5kHz. Good enough to demonstrate the working principle of an oscilloscope.

More of a challenge will be to get those 74kB/s or more of data from the PIC to the PC over USB. This is more data than can be sent in one Interrupt Transfer Packet per millisecond. Sounds more like a good candidate for Isochronous Transfer, but that requires you to significantly change the library. I always wanted to find out what happens if I define an HID report to be 128 bytes large. If the two packets, required for that, are transferred within one msec or two. Or if the whole thing works at all.

Lack of practical use is probably what keeps me from implementing it.

Your math is correct. I did read it somewhere but probably for another PIC controller, I cannot find it anymore.

For now i am struggling with a chart I put in the form and trying to get the datapoints (at 1msec) in the chart. however, it dies immediately with some cryptic:
system.argumentexception’ occurred in system.windows.forms.datavisualization

and
system.reflection.targetinvocationexception

mmh Microsoft language. Where’s Bill when you need him?

Hurray I solved the problem. Something to do with the right indices. I now have a chart that displays every 1msec the analogue output on one channel.

I am not an USB expert, and I think you are so here’s my question:
I read in the HID USB spec that a possible 255 bytes can be submitted every time a poll is made. Is this true?
Then, with some overhead, I’d reckon we could send some 250 bytes each msec of analogue data. Given 2 analogue inputs I can squeeze 125 bytes every msec or 62 measures (10 bits) every msec. that would give me 62 ksamples/s for 2 ADC inputs. Am I wrong?

I studied your code and you use a union Open8055_hidMessage_t with (now) 32 bytes. Expanding it to 255 bytes could do the trick.

I somewhere read in the comments that making an array of the ADC values would dramatically decrease processor speed. So is there a soolution for this?

Regards
Beach (cause I live near it!)

That’s one USB detail that I am not sure about myself, with respect to Interrupt Transfer (that’s the transport protocol underneath HID). I need to do some tests about that.

I know that the maximum “packet” size for Interrupt Transfer is 64 bytes at Full Speed. That is not the maximum HID message size, but just the maximum size a single PIC side transfer takes. So at the very least the PIC will have to send the entire message in multiple packets. What I don’t know is if those multiple packets will be transferred within one msec, like a mini burst of packets, or if they trickle over the wire one per msec.

I am switching gears with the Open8055 project.

I am done with any type of application side USB access link library. It is a nightmare and a boatload of threads on this forum tell the story. Any number of people have trouble accessing their cards from language “X” because Windows is of another version, or 64 vs 32, or whatever language they are using likes to link “THE STANDARD” way (good that there are so many standards on Windows) … or they actually try using a real OS (like Linux … rare, but it has happened).

What I’m going to try now is to create a relatively simple to communicate with (telnet line protocol) TCP/IP server process. I got it pretty much working for Linux and FreeBSD. I can “telnet” to a remote Open8055 board and instruct it to set digital outputs. Good enough for proof of concept.

This means that the only real “low level” requirement for any language would be to be able to communicate via TCP/IP with “something”. As if there would be any language out there that cannot do that.

I believe that turning the Unix deamon into a Windows Service isn’t too much work. However, at this point I’m not going to do that part. If someone else wants to step up, let me know. I don’t mind supporting Windows users, I just don’t use Windows myself, so I don’t really care.

Regards,
Jan