C64 User Port Input

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.

Breadboard with the MCP3008 ADC. The potentiometer is connected to one of the analog input pins on the right side of the chip. Power, ground, and four data lines connect to the left side of the chip. The yellow wires at the top are unconnected.

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!

I am trying to get my Vic-20 to communicate with a ADC over RS232.
I want to read analog values from a temperate sensor into the Vic and display them on the screen. I eventually would like to control some peripherals with the Vic. I will be using the User port RS232 for this endeavor. Right now, I just want to read a value in from the ADC and display it on the Vic screen.
The ADC is an 8 bit device.

I could really use some help with this and there are not may guys out there that can do what you have done. Any help would be greatly appreciated.

Actually, After reading more of your posts, I am quite open to using the SSI method. I would like to be able to program in Basic, since it is all that I know.

Long story short: I want the Vic-20 to read the value of a temperature sensor through and ADC and display it on the screen . I don’t care what kind of ADC. Ideally, I would like the Vic-20 to communicate with an Arduino I have. I would use the ADC on the Arduino and could send data to and from the VIC-20.

Sorry if this seems a little vague, I am still in the planning stages and am open to any suggestions.
You can E-mail with your response either way.

Well, if you’re trying to do it in Basic, I’d steer clear of rs-232. The advantage to using a chip with an SPI interface like the 3008 I used is that you don’t have to worry about the timing so much. You’ll still have to do a lot of bit-twiddling to communicate with it, though.

The simplest solution by far would be to find an ADC that has a parallel interface – then there’s no real communication protocol to worry about, and you can read the data with a single PEEK.