I'm also working on an ADB interface for the Cypress PSoC digital blocks (alongside the SDLC interface for the same chips to talk to LocalTalk networks). Those chips have some really nice custom logic (including a very complete datapath section with FIFOs) available. That would give ADB properly in hardware with very low overhead.
Pity they don't have a USB host option, at least not on the 5LP series.
Well, anything that can do USB host can also work with hubs; that's a matter of software support (unless you're working with a device that does damn near everything in ROM and the ROM doesn't support hub enumeration, but that's not most things).
But yes, in response to a previous question: the PSoCs don't directly support USB, alas. The best you could hope for is to use an SPI<->USB bridge chip; Maxim makes some semi-popular ones. WIth DMA, the overhead for that shouldn't be too atrocious. The reason to use PSoC for ADB stuff (and possibly for LocalTalk) is that they have some pretty great programmable logic in there that should allow one to make an actual ADB or SDLC (for LocalTalk) type peripheral that substantially reduces overhead vs. a timer/interrupt based bit-banging solution.
Well, what I'm saying is that handling hubs is just a matter of software, and it's a standard. If you can hack together ADB bit-banging code, I daresay you could hack together USB hub handling code. Manufacturer-provided code is often dodgy at best; for a project I'm working on at work, we use it to get up and running, but eliminate it as soon as possible because it's usually not written in a space-efficient manner and takes about 4x the space.
If you're relying on code in a ROM to do your USB host support, you may have a problem, but to the best of my knowledge ST chips don't have much in the way of ROM drivers (NXP chips often do, for example).
Hi guys! I've been working on a USB-to-ADB converter device, similar to bbraun's concept here, but using a PIC32MX. My goal is to make a cheap-and-cheerful device that works with ADB Macs and the Apple IIgs, where you can plug in a USB mouse and keyboard. I'm ignoring PS/2 and the pre-ADB mouse/keyboards for the time being.
The good news is that I have basic ADB working (I can make a Mac SE's cursor spin in circles), and separately I have USB working through a hub (can read a keyboard and mouse at the same time). The bad news is that I can't put them together, because I run into interrupt problems similar to those bbraun had. My ADB implementation is bit-banged using software timing loops, and when a USB interrupt handler intrudes and takes 50-100 microseconds, it screws up all the ADB timing.
The first link above contains some of my ideas on how to solve this problem. I've mostly narrowed the choices to either using two separate microcontrollers, or redoing my ADB implementation using pin state change interrupts and timer interrupts for ADB reads and writes. I'm not too crazy about either approach, so if you have other ideas, I'd love to hear them!
I think, if I were faced with this situation, it would seem most direct to go the two-microcontroller route (a brute force approach, but highly likely to work). Each would be behaving as a dedicated I/O controller, one ADB and the other USB (as I understand it, this sort of poor man's ASIC substitute was a major use case for early microcontrollers), with some minor additional flerb inserted to glue the two together as a single functional device.
As proof that this approach is both straightforward and effective while not precluding clever implementation, I submit the Commodore PET/CBM line from the 1970s, specifically the hulking, 35-lb dual-floppy-drive peripheral units that at the time cost as much or more than the actual computer did. They had one system controller (a full 6502, I believe) that took care of the IEEE-488 interface and interpreted the disk commands received over it, and a second (either a 6504 or a 6507, can't recall which) that did nothing but control the low-level hardware functions of the two drive mechanisms. Rather than the headache of maintaining some sort of additional, dedicated I/O subsystem between them, they communicated via a simple command queue stored in a block of single-ported RAM that was simultaneously mapped into the address spaces of both CPUs, and thanks to a clever arrangement of the system timing, each CPU accessed it in perfect isolation on alternating clock cycles.
The most interesting and relevant thing about this specific example is that later implementations of closely related designs (still controlling two independent floppy drive units and a high-speed I/O bus while simultaneously interpreting high-level disk commands transmitted as uncompiled 7-bit text) managed to carry out all needed functions, with no apparent loss of effectiveness, using highly similar firmware—but running on only one CPU at the same 1 MHz clock rate, simply by means of much more resource-efficient programming techniques. Presumably, a similar evolution is theoretically feasible in any case of this nature, though if the individual sub-controllers are very busy, a higher clock rate may be necessary in the final combined one.
Not sure if this progression of digression is of any real use to you, but here, have it anyway.
I still really think the best solution is doing the ADB in hardware, whether with a CPLD (or a tiny cheap FPGA, like Altera's Max 10) or the digital blocks of a PSoC. The latter should actually be quite easy; in fact, I'm of a mind to build up a little PCB to attach an ADB port to my PSoC eval board.
Of course, the original use case for ADB was a simple bus that a 1 MHz 6800 microcontroller could manage in software. But I'll point out that that 1 MHz microcontroller didn't do anything else, and was for most intents and purposes a glorified parallel-to-serial converter. You could perform the ADB decoding function in a single UDB block on a PSoC, if my calculations are correct.
Note: the other really great thing the PSoC ought to be good at is SDLC conversion for LocalTalk. I think I'm going to make an implementation on a Max 10 to attach to a programmable WiFi module I mentioned in another thread, but for stuff that you want to run on a plain microcontroller, the PSoC is a great candidate.
Thanks! I had briefly considered the PSoC, but from the comments above, I thought it didn't support USB. A CPLD could work, and I'm pretty familiar with some of the small Xilinx CPLDs from other projects I've done. But the cost of even the smallest 5V CPLD is more than a low-end microcontroller (the smallest PICs are under $1), and I find the ADB protocol easier to implement as normal C code than as VHDL.
I'd still prefer a single microcontroller solution if possible, to keep the hardware simple. Beyond the cost and complexity of a two-mcu solution, I'm a little worried about what the firmware update process would be. I'd need to design a single firmware payload that was actually two firmwares, and have the first mcu reflash the second one. That should definitely be doable, but it sounds like a pain. I think I'll stew on this a while more, but I'll probably attempt the timer-hacking solution using a single mcu. If that bogs down and fails, then I'll go to the two-chip solution.
Well, it doesn't support USB directly, that's true. You'd need an external USB host, like Maxim makes.
If you want to do it with timers, there are a few things to do to reduce overhead. First, for input, is to use the capture functionality of your timer to capture the low periods. If you're lucky, your MCU may even be able
To DMA multiple captures out so the interrupt overhead doesn't totally murder you, but that's relatively rare and might be problematic in case of bus errors.
For the output, you should probably consider using the PWM output functionality of your microcontroller. That'll let you keep the bit cell time (mostly) constant, and just set the PWM compare value for each bit; the bit cell time is 100 us, and a 10 KHz interrupt rate isn't too bad on a Cortex-M3 (plus, since the output will automatically toggle at the end of the bit cell, you get a little bit of wiggle room on your timing).
My recommendation: do minimal handling of the USB packets (enough to DMA them into a buffer and flag reception) at interrupt time, and defer your actual report processing for non-interrupt time. You should be able to handle a timer-based ADB with little trouble that way.
Also, not to be a total downer, but reading your reports, I'd seriously consider either ditching the PIC32 (which is actually just a MIPS CPU with Microchip IP stapled on, not a 32-bit version of the PIC, which is an excellent architecture for a tiny 8-bit micro) or at least throwing out the garbage USB stack. You might have much better luck with either an NXP (LPC series) or an ST (STM32 series) ARM, with a Freescale (Kinetis) being a third choice (third not because the chips aren't good, but the support isn't great). If you need a well-groomed development and debug environment, my choice would be NXP, because their LPCXpresso IDE (Eclipse-based) is pretty decent. I'm personally a command-line GCC/GDB guy, in which case any of the above three would probably suit.
MPLAB has been junk since they tried to "evolve" it past the simple tool for PICs it wanted to be, much like Atmel's AVR environment when they moved to the Visual Studio based one.
In any case, with a suitable chip and stack, you shouldn't need more than about 25-40 MHz to make this work satisfactorily. The fact that the USB stack takes 50 us to process an interrupt is alarming. For a first pass, I'd suggest an LPC17xx device that has USB host capability. Beware, though, their ROM drivers also have a reputation.
In general, beware of vendor-supplied middleware, because while it often seems great when you're bringing stuff up and running quickly, you usually find out pretty quickly how badly it sucks, because they usually just fob that stuff off on summer interns. For example, at work we're doing a project with an NXP micro that has I2C drivers in ROM, which we thought would save some code space (which would be good, because it's only got 32 KB of flash). However, the ROM driver was clearly written by someone who hadn't done much I2C work before, and working around its shortcomings has taken more code space (and time) than just writing our own; the ROM driver doesn't even use the DMA engine. So just consider that vendor-supplied code isn't necessarily something you should use as a determinant of whether a chip is suitable.
Also (to complete my trifecta of lengthy epistles), don't bother with 5v CPLDs unless you're aiming for soMe sort of period accuracy. They're teetering on the brink of obsolescence, and it's so trivial to convert from 5v (you probably want to be driving the line with a stronger pull down like a 2N3904 anyway).
What people call a CPLD anymore often isn't anyway; since the Max II series, Altera's "CPLDs" have really just been tiny FPGAs (the difference being that CPLDs use the traditional PLD AND/OR product term array, while FPGAs use lookup tables). With the Max 10 (which adds additional things like block RAM) they've finally given in and started calling it an FPGA. Xilinx's CoolRunner II series is still a real PLD at heart, though it's actually SRAM based internally somehow.
Anyway, if you're trying to do something like lash fast synchronous SCSI to a micro's external bus or decode SDLC frames at high speed or something, an external PLD or a built in one like in the PSoC is the way to go. There are some things you just shouldn't bit-bang.
FWIW, a 1 MHz 6800 was never used for ADB that I know of. Egret and Cuda run the 6805 at ~2.1 MHz when the system power is on (and at 32.768 kHz when on battery power). The IIgs used a Mitsubishi M50740 (6502 with additional bitwise instructions like "branch if bit 3 of memory location is clear") running at 3.58 MHz. Interrupts are disabled during the cycle-counting ADB transactions in both cases, needless to say. Apple's ADB keyboards used an 8041, not sure what was in mice.
On something more modern running at 30-40 MHz where the GPIO pins have a state-change interrupt you should be able to implement ADB in software without interfering with the USB stack.