In my first post on the C64 User Port, I only did output – blinking LEDs according to a program. You can do input too, which will let the C64 talk to other machines, or sense the world around it.
There’s a catch, of course. The User Port’s pins are binary – on and off – but the real world is analog. To bridge that gap, you need an Analog to Digital Converter (ADC). The ADC I’m using in this post is a microchip from, appropriately enough, Microchip Technologies – the MCP3008. You can see it straddling the center lane of the breadboard in the picture below.
Now, to be entirely honest, the MCP3008 was not my first choice for interfacing with a C64. However, I forgot to order the component I wanted in the rush to get ready for Gen Con, and this is what I actually had on hand. I got the 3008 from AdaFruit a few weeks ago, along with a few other toys that’ll be showing up in future entries.
For one thing, the 3008 outputs a 10-bit value – so it maps the input voltage to a value between 0 and 1023. That’s a slightly weird match for an 8-bit micro. There’s also the interface. I had wanted to use a chip with an 8-bit parallel interface because using it would be trivial. Instead, the MCP3008 uses an interface called SPI (Serial Peripheral Interface) bus.
SPI is a serial interface that uses four wires to connect a master (in this case, the C64) to a slave (the MCP3008). It’s not hugely complicated, but it’s not something I’ve ever used before, either. Luckily, AdaFruit included some tutorials to get me started. Of course, the tutorials were for a Raspberry Pi instead of a C64, and the code was in Python, instead of 6502 assembly. So there was a certain amount of adaptation to be done.
The analog input in my sample circuit is a potentiometer used as a voltage divider. It’s hooked up to one of the chip’s analog inputs (it actually has 8 separate input channels, which is cool) and provides 0 to 5 volts.
In addition to the +5v and ground connections from the User Port, I use four of the parallel port pins to implement the SPI bus. One line is the “chip select”, which enables the ADC. Another is a clock line, which is used to tell the ADC when to read or write data. Finally, there are individual input and output lines. The SPI bus is full-duplex – both sides can talk simultaneously – though we don’t need that feature in this example.
On the software side, I’m essentially bit-banging – changing the values of individual bits, rather than using a library or any kind of dedicated interface. To send a bit, I set the output line to 0 or 1, and then briefly toggle the clock line. To read, I toggle the clock and then read the input line.
To sample the analog input, the C64 sends a 5-bit message to the 3008: two 1’s, followed by a 3-bit number indicating which of the 8 inputs I want to sample. Shortly after, it starts reading: a 0 bit, followed by 10 bits of data. That gets inserted, one bit at a time, into a 16-bit number with a value between 0 and 1022.
Did I say 1022? Ideally, the max value would be 1023. I guess my potentiometer is eating a little voltage even when it’s turned all the way over. That’s another one of those “real world” things you have to watch out for.
Another real world thing still has me puzzled: according to the datasheet for the 3008, and also the python code at Adafruit, there should be one more clock transition between when I finish writing to the 3008 and start reading. But if I do that, all my data is one bit off from where it should be, and my values go from 0 to 2045 (not even 2047! sigh…). Either there’s a subtle timing issue involved, or I don’t understand the SPI bus as well as I think I do.
Probably the latter.
One last trick before we finish. How do you print a 16-bit number to the screen as its decimal value from assembly? You cheat. You copy the high byte of the value to the accumulator, copy the low byte to the X register, and then jsr to $BDCD. That’s actually the location of a routine in the C64’s BASIC ROM that’ll do all the work for you! That’s much simpler than doing it yourself!