As I said a while ago:
[T]his bloke has a super-cheap approach to USB interfacing, viz using those USB-RS232 cables that don't do level conversion. I'm off to buy a cache of them from eBay.
Well, I bought five of those cables from these guys in China for a grand total of about $AU11. They turned up less than a week later, and are about as cheap and nasty as their price suggests.
The first thing to do was to ensure the Mac recognised them. This was painless as they look like a Prolific PL2303, about as stock as they come. The second step was to extract the circuit board from the (very cheap and very nasty) serial plug. This took a while as the plastic is pretty tough; next time I'll heat it up a bit first, or perhaps try out the Portasol's hot knife. Also I smashed the ceramic resonator in the process, which was a mixed blessing as apparently they are not very stable wrt temperature, so replacing it with a 12MHz crystal is probably worthwhile anyway. Good thing I got something like a lifetime supply from Sure a while ago.
The remaining problem is that RS-232 inverts the signals: low is a logic 1. These cheap cables treat low as something like 0v, so there's nothing fancy required to get the signal the right way up; two instances of the circuit at the bottom of this page did the trick, one for each direction. I tried a 10kΩ / 100kΩ pair as he suggests but the power sucking 1kΩ / 10kΩ combination seemed necessary.
After wiring that up, the magic incantation screen
/dev/tty.PL2303-0000xxx 9600
for some xxxx
worked
fine as a zero-functionality terminal. It seems that 9600 baud is the
limit with the default 8MHz-divided-by-8 clock of the ATmega328P,
which surprised me as I wasn't expecting anything more than about 1200
baud to work due to the
probably huge error in the clock.
So all up there's the cable ($AU2.20), two transistors (Jaycar is expensive here, $AU0.26 per BC549, where Farnell only wants $AU0.10 or so), four resistors (marginal) and some time. Much better than $US20 for a "USB TTL cable".
The other thing to note is that while the AVR's UART is incredibly easy to get going, the code depends a lot on which particular chip you've got: I had to pepper the I/O register names in mine with 0s for no reason I could fathom, for the ATmega328P only has one U(S)ART. (Yes, yes, there are a few other serial interfaces but those are hardly universal; they are not even asynchronous AFAIK.) Here's the guts of it:
#include <stdint.h> #include <avr/io.h> #define BAUD 9600 static inline void UART_read(uint8_t *c) { while(! (UCSR0A & _BV(RXC0))) ; *c = UDR0; } static inline void UART_write(uint8_t c) { while(! (UCSR0A & _BV(UDRE0))) ; UDR0 = c; } static inline void UART_init(void) { #include <util/setbaud.h> /* Set the baud rate */ UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; /* Turn on the transmission and reception circuitry: 8 N 1. */ UCSR0B = _BV(RXEN0) | _BV(TXEN0); UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif }
Next up is fabbing it on some veroboard.