November 6, 2023

Let's learn USB! -- Papoon with interrupts

I have gotten papoon running using polling as in the stock example. It enumerates properly, then echos characters I type to it.

The next step is to get it working using interrupts. This will make it possible to use it for real purposes rather than tying up the processor polling. I leave the working code with polling behind intacts and begin a new project:

I am doing things like setting the Nvic interrupts from my C code. As I add things It is an interesting choice whether to add the the Papoon C++ code or my own C code. My general tendency is to do as much on the C side as possible.

C++ is not so bad! I don't intend to start working with it, but it is pretty easy to get the feel for his "regbits" scheme and see when he is accessing device registers and how.

USB has 3 interrupt vectors allocated to it. I added handlers for all 3 and put printf statements in them so I could learn when they get called. Only one of the 3 ever gets called (namely "lp"). The three are "hp", "lp" and "wk". The last ("wk" for wakeup) should never get called. It expects some kind of wakeup circuit to be set up using a EXTI interrupt (i.e. a configured gpio line if I understand it right). But I will announce if one ever comes along.

The names "hp" and "lp" are entirely confusing and undocumented (as near as I can tell). In situations where documentation fails, experimentation is the only recourse. Does "hp" means high power? Maybe it means "high priority". We may never know. Also note that these vectors are labeled "usb_lp_can_rx0" and "usb_hp_can_tx". My take on this is that the rx0 and tx business pertains to can and we can ignore that. We get a "lp" interrupt for both receive and transmit events for USB. USB and CAN share resources (both interrupts and packet memory) and cannot be used at the same time. Not that I care -- I never intend to use CAN.

When I first tried running the interrupt code, it did not work (it failed to enumerate). I had printf statements in each interrupt handlers. When I commented these out, it would work. I have seen this kind of thing before. It takes time to send characters over a serial line, and printing messages from inside the interrupt routine, while informative, either upset timing, or caused interrupts to be missed.

So what I did was to add a debug flag to prevent printout until enumeration was finished. After adding sufficient delay, I got this to work. So now I have interrupt driven USB with debug after enumeration finishes. This leaves two steps next:

I could add a queue for USB reception. Doing a callback is simpler and I think will serve my purposes. The poor little F103 has only 20K of ram and even a modest character queue would take a chunk of that. At some point we will move on to the F411 where we have 128K of ram and feel less restricted.

I am also tempted to begin migrating code out of the C++ world. There is code for the Nvic in papoon, but it never gets used, so I might as well delete it. Also there is code for the Rcc to set up the 48 Mhz USB clock. I should migrate this, then delete that from papoon. Why do this? It is not that I hate C++ (well I do, but that is not why I want to move the code in this case). The point is that the fewer "distractions" I have in the papoon code, the easier it is to study it.

Also a quick comment on TinyUSB. I abandoned it because it did not offer a quick path to running code on the F103. It may well be worth revisiting once I have a low level USB driver that I am happy with. USB code can be partitioned (like network code) into a portable upper part, and a hardware specific lower part. TinyUSB could be a source for the portable upper part. I have not yet looked at the coding style and such to evaluate whether this would be a good choice. In addition, a serial port is all that I am looking for from USB, at least thus far. Here are two comments on the serial port as provided by papoon:

I expect the "cat" experiment to just work, but I do want to try it. I want to implement "usb_puts ( message )" where message could be longer than 64 bytes. I could of course just loop sending single characters via usb_putc(). It would be much better though to send as many as 64 characters at a time, and it would be a basic C programming exercise to chop up longer strings in such a way as to do that.
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org