Quantcast
Channel: Bit Bucket – Big Mess o' Wires
Viewing all articles
Browse latest Browse all 164

USB to ADB – Multitasking Two Interfaces

$
0
0

vl_mouse_label

It’s time for a new progress report on my USB to ADB converter project! My goal is to design a simple PIC32 device that enables USB keyboards and mice to be used with vintage ADB-based Macintosh and Apple IIgs computers. You can read my earlier reports here: 1, 2, 3, 4, and 5. I don’t know when I’ve ever spent so much time investigating the feasibility of a project, without actually doing the project. Hopefully all this preparation will pay off.

Good news: I spent some time plowing through the details of Microchip’s USB library, as well as Suwa Koubou’s modified version, and things are beginning to make much more sense. Hosting a “simple” USB HID device like a mouse or keyboard is proving to be much more complex than I imagined, once you factor in all the behind the scenes work that would normally be handled by an OS or driver code. While I certainly don’t understand it all, I can say I understand the basic framework now. That should make life much easier as I go forward.

Bad news: the Microchip PIC32 tools and libraries continue to disappoint. On top of my previous complaints, I’ll add the poor quality of the Microchip USB stack source code. It looks like something that was hacked together by a team of student interns over a period of years. It’s a confusing and messy overall structure, with heavy use of #defines for function names, many lines commented out, comments that says useless things like “HHHHHHHHHHH”, hard-coded assumptions, debug print messages… not good. Maybe I’m looking at the wrong code? It definitely doesn’t feel like a professional-quality software library you can rely on for a real product.

More bad news: I’ve also discovered that the MPLAB X debugger is flaky. It will often stop in the middle of a debugging session and report “user program finished”, even when I can tell the program is actually still running. And I found that if you set a breakpoint while the program is running, it won’t necessarily work. You have to pause the program, set the breakpoint, and then resume. I only discovered that by accident in a moment of frustration.

The urge to throw the PIC in the trash and start over with something like the SAMD21 is growing, but I still haven’t done it. I keep rationalizing that I’ve already come this far…

 
Multitasking ADB and USB

The major bad news relates to servicing the ADB and USB interfaces simultaneously. I wrote about this concern previously: if USB-related interrupts happen too often and take too long to service, they may interfere with the software-derived timing of my bit-banged ADB interface. I did some earlier tests by setting breakpoints inside the USB interrupt handler, and determined that interrupts are only used for USB device attach and detach events. So my concern was unfounded… or so it seemed. In reality I was bitten by the breakpoint bug that I only recently discovered, and the USB interrupt actually fires at least once every millisecond, plus additional times when transfers are completed. Doh!

I thought I might still be OK if the USB interrupt handler was very short, so its impact on ADB timing would be minimal. ADB data is encoded at 1 bit per 100 microseconds, and given its encoding method, a timing error of up to 10-15 us could probably be tolerated without causing errors. Using the PIC32’s built-in core timer, I was able to measure the typical and worst case execution times of the USB interrupt handler, and it was about 50 microseconds at 80 MHz with code compiled using -O1 optimization. That’s definitely too slow to avoid causing ADB timing problems. What’s worse, for the final device I’d planned to use a cheaper version of the PIC32 that only runs at 40 MHz, so the interrupt handler time would actually be 100 microseconds.

So it seems that naively combining a USB keyboard/mouse example with my bit-banged ADB implementation is not going to work. What are my alternatives?

 
UART Hacking

If there were a hardware ADB peripheral on the PIC32, then there would be no problem. ADB bytes could be read in/out of a buffer by the hardware peripheral in parallel with USB activity or other unrelated code, and then handled later in the main loop via polling, or via an ADB interrupt. Of course there is no ADB peripheral, but maybe the UART could be abused to do the job? ADB is sort of like serial communication, in that there’s no independent clock, and it runs at a fixed bit rate. But it’s unlike serial in the way bits are encoded. Instead of a simple high voltage for logical 1 and low voltage for logical zero, ADB encodes every bit as a pulse whose width determines its logical value. There are also some non-data signalling elements in a typical ADB transmission. But maybe I could run the UART at like 10x the actual ADB data rate, and capture the oversampled ADB waveform in a buffer for later analysis through software.

I think this UART hacking idea might almost work, except that serial communication always involves a start bit, at a minimum. It’s how the UART knows that a new byte has begun, but ADB communication doesn’t include a start bit. That would likely cause weird problems when reading and writing UART-as-ADB data. Maybe this could be overcome… I’ll need to think about it more. Unfortunately my PIC32 dev board doesn’t have the UART pins broken out to a header, or to anywhere I can solder, so I’d need to get new hardware if I want to try this route.

 
SPI Hacking

If the UART peripheral can’t do the job, maybe the SPI peripheral can? SPI uses an explicit clock signal, which ADB lacks, so that’s obviously a problem. But there’s no framing or start bits to worry about, which is good for the kind of hackery I’m envisioning. If the microcontroller were configured as SPI host, then it would wiggle the clock line for SPI reads and writes, but I could leave that signal unconnected and ignored. It’s so crazy, it just might work. But again, my PIC32 dev board doesn’t have the SPI pins broken out anywhere, so I’d need new hardware to explore this.

One point I’ve glossed over with both the UART and SPI hacking ideas is that ADB is an open collector bus. The host and devices pull it low when needed, but never actively drive it high. Instead, there’s a pull-up resistor to do that job. I’d need to include some extra circuitry to adapt UART or SPI traffic to an open collector bus.

 
Timer Hacking

Another approach would be to replace my software-derived ADB timing with hardware timers and pin state change interrupts. For reading ADB traffic, instead of polling in a tight loop in order to measure the widths of pulses on the ADB bus, I would use a pin state change interrupt and then determine the pulse width by checking a timer value in the interrupt handler. For writing ADB traffic, I would use hardware timer interrupts to change the ADB bus value at appropriate intervals. That would eliminate busy loops, and enable the USB and ADB communication to happen in parallel to some extent.

This would only work if the ADB pin state change and timer interrupts had higher priority than the USB interrupt, else USB interrupts would still introduce ADB timing errors. And this approach might possibly introduce USB timing errors, if the USB interrupt handler were delayed from executing by one of the ADB-related interrupts. Another concern is the latency for context switching and entering the ISR handler, after a pin state change occurs. This would need to happen quickly, even if the USB interrupt handler had been in the middle of executing, in order for ADB timing measurements to be correct.

 
Two Microcontrollers

My final idea is to use two wholly independent microcontrollers: one for ADB and one for USB. They would communicate with each other using SPI or another method supported with a hardware peripheral. That would probably work, but wouldn’t exactly be simple, as now I would have two different microcontrollers each managing two different interfaces in parallel. It would also increase the cost and complexity of the board, and make firmware updates more difficult. But it still might be a reasonable solution overall.

To be honest, I don’t love any of these ideas. I’m sitting on my hands for now, hoping that a better approach will jump into my mind. Here’s hoping! :-)


Viewing all articles
Browse latest Browse all 164

Trending Articles