USB-GPIO
USB-GPIO – Simple Digital I/O for a PC
Ever need just a couple of bits of Digital I/O for your PC? Maybe want to bit-bang and SPI interface to simple test a new device? Used to be the PC’s parallel port was the answer. These days, that’s no longer an option.
Here’s a quick solution. Sixerdoodle USB General Purpose IO board (USB-GPIO). One small circuit board, and a bit of software, and magically, you’ve got simple IO for your PC again. You can even use Perl to read and write data bits! The hardware can even be configured for either 3.3v digital I/O or 5v digital I/O so that it’s compatible with whatever you need to interface to.
Hardware
Here’s the circuit diagram. It’s pretty simple. The 12 pin header is an edge connector, so you can plug this directly into your breadboard. The 3×2 header allows reprogramming the firmware.
The three solder jumpers configure the power:
- SJ2 open and SJ3 closed selects the operation with the 3.3v regulator (3.3v Digital I/O signals, Zener not needed).
- SJ2 closed, SJ3 open, configures for 5v Digital I/O (Zener must be in place)
- SJ1 (1-2)(3) = off board power; (1)(2-3) = USB powered; (1-2-3) = USB Powered, USB 5vdc available on 12 pin connector
The 12×1 PIN header can be used to connect the USB-GPIO board directly to a breadboard so you can connect the breadboard signals to your PC. The available signals from pin 1 out are: GND, PA7, PA6, PA5, PA4, PA3, PA2, PA1, PA0, VCC (either 3.3vdc or 5vdc), USB 5vdc or power in (depending on SJ1 selection), GND
LED1, by default is simply a status indicator. My V-USB software toggles this LED with each USB command received.
Here’s the USB-GPIO v2 bare board with component locations.
The component list is pretty short and some of the parts are optional.
| 15pf – 0603 C1,C2 | 3.3Vdc – Reg optional, only for 5v I/O capability MCP1700T-3302E or equivalent in SOT-23 pkg |
| LED – 0603 LED1 optional, indicator only |
1K – 0805 R1 optional, only if using LED1 |
| BZB84 – Zener optional if using 3.3v reg BZB84-B3V6,215 or equivalent in SOT-23 pkg |
68R – 0805 R2, R4 optional, for impedance matching short with wire if unused |
| 1K5 – 0805 R5 | ATTINY261 SOICW-20 |
| XTAL 12Mhz – Q1 | Mini-USB connector |
| 10uf – thru hole C3 | 12X1 PIN |
| 3×2 PIN optional, only if you want to reprogram the ATTiny261 |
Here’s the fully populated USB-GPIO v2 board populated. Yes, it’s mostly surface mount components, but it’s completely feasible to hand solder this together. In fact this photo is of a board I hand soldered with a soldering iron with a big wedge tip just to prove that it can be done.
Software
There’s two parts to the software for this device, the firmware for the ATTiny261 and the windows driver and DLL.
For the firmware, use Objective Developments V-USB. Three pluses to using V-USB for the firmware. First, it’s still being actively supported. Second, the majority of the code is in C, so it’s easy to enhance. Third, it resolves the problem of how to get a valid USB ID by providing a general purpose one everyone can use as long as you follow their usage guidelines (see the V-USB site for more info)
For the Windows side of things, LibUSB-Win32, a universal driver for USB devices that works on Windows XP, Windows 7 both 32 and 64 bit mode. There are multiple wrappers and bindings making LibUSB-Win32 usable from a variety of languages from C# and .Net to Python, Perl, and Ruby. Take your pick and you should be able to easily write code to access the digitial I/O on the Sixerdoodle USB-GPIO device.
So, what’s the end result? an USB general purpose 8 bit I/O interface that you can control almost as easily as that old parallel port!
Uses
You might ask why even do this? Well, here’s a use case. There are times when I need to cycle the power on my cable modem to get it working again. So now I’ve got a Perl script that automatically pings a selected web site. if that ping fails, the Perl code uses the USB-GPIO board to cycle the power on the cable modem. PA2 drives a relay which controls the power to the cable modem. Simple Perl code to turn a single IO bit on and off looks like this:
#
# Power off modem
#
print "power off modem and network \n";
$value = 0x0000;
$index = 0x0000;
print "Set Port Data ", $value, " returned ",
$dev->control_msg( USB_ENDPOINT_IN | USB_TYPE_VENDOR,
SetOutDataPort, $value, $index, $bytes, 1, 250 ),"\n";
#
# Wait a bit...
#
sleep 10;
#
# Power on modem
#
print "power modem and network back up\n";
$value = 0x0004;
$index = 0x0000;
print "Set Port Data ", $value, " returned ",
$dev->control_msg( USB_ENDPOINT_IN | USB_TYPE_VENDOR,
SetOutDataPort, $value, $index, $bytes, 1, 250 ),"\n";
Here’s a v1 version of the board showing how the 12×1 PIN header works on a breadboard:
Firmware
For firmware, this circuit is compatable with the V-USB firmware. Basically start with their download, modify the usbconfig.h file to match the specific hardware requirements and you’re good to go. You do have to add code for what you want the firmware to actually do. In my case everything ties into the stub usbFunctionSetup since this routine traps the Vendor Control messages (same way as the Atmel code firmware did).
So you’ll find my V-USB.c with it’s simple main() routine and usbFunctionSetup routine in the below zip file.
You can download my specific V-USB implementation here. Features implemented include 8 bits of digital I/O, simple ADC converter, i2c and SPI interfaces. The i2c routines come from i2c master library by Peter Fleury
Feel free to modify my code to add your own user functions! Software Now about that LibUSB-Win32 stuff…. LibUSB-Win32 is a generic USB driver and a corresponding DLL. You can actually use LibUSB-Win32 to talk to any USB device, but just using it here for talking to the modified AVR309 firmware. Your program talks to the DLL, the DLL talks to the LibUSB-Win32 driver and the driver talks directly to your USB device. The modified AVR309 firmware is really simple USB, it utilizes USB Control Messages for all data transfers, so that greatly simplifies the interface and any programming you need to do. Once you’ve unpacked the LibUSB-Win32 software, The first step you have to do is tell windows to use the LibUSB=Win32 driver for your USB device. Do this by running the inf-wizard.exe delivered with LibUSB-Win32. Run the wizard with your device plugged in, select your device and the wizard will generate the appropriate inf file to connect it’s driver to the selected USB device. From that point on, you can use the LibUSB-Win32 DLL to communicate with your USB device. As mentioned earlier, the firmware is designed to send small chunks of data back and forth through the a USB control message and implements it’s own specific functions through a vendor control command with the “Value”, “Index”, and “Bytes” arguments being passed back and forth. “Value” and “Index” are data send to the USB device and “Bytes” contains the resulting data from. The functions accessible through the Vendor Control message are: Again, simple Perl script showing an example of using the Vendor Command “setOutDataPort” to output a 04x to the ATTiny261’s DDRA port. There are some additional steps necessary to use the LibUSB-Win32 API to get the API to locate your USB device and start the connection before you can send control messages. Check the LibUSB-Win32 site for more info and look at my USBtest.pl for a complete example. Note that the USB device ID supplied by V-USB is a generic, shared device ID. There will be other devices out there using this ID so you must enumerate the list of all connected USB devices and pick only the one with the correct Product and Manufacturer name. You cannot rely on the USB device ID alone! Note – if you try installing Perl Device-USB follow the documentation on installing with StrawberryPerl. it’s pretty close. use cpan to do the install. Don’t forget to set environment variables outlined in that documentation. cpan’s got to compile some stuff against the libUSB-win32 libraries. You may also need to rename lusb0_usb.h to usb.h to get cpan Device::USB to correctly pull in the libusb-win32 include file. and finally…. just to finish up, here’s the C code to do an equivalent “toggle PA0/PA1″ to show that you can also do this with straight C if you want. You don’t have to use Perl. (note – I need to update this C routine with the V-USB ID and device ID enumeration requirements as well, but again it’s still a good template to follow) I’ve got a few of the v2 prototype boards left. call it $7.50 + shipping. If there’s enough demand I’ll do another larger run and we can likely get the price down a bit. I can also put together kits and/or fully assembled boards as well. Assembled board probably cost $23 + shipping and kit $18 + shipping, again e-mail if you’re interested, might take a few weeks to get the parts together on kits and assembled boards. You can download all the software here
$value = 0x0004;
$dev->control_msg( USB_ENDPOINT_IN | USB_TYPE_VENDOR,
SetOutDataPort, $value, $index, $bytes, 1, 250 );



