Subscribe via RSS
19Jun/1620

Commodore 64: Serial Interface to Arduino

So, in my previous post, I was heading towards building an archaic circuit to control trains with the User Port. This would've worked, had I spent a lot more time and built a very complex circuit. Instead I've now chosen a new path... let's hook the C64 up to an Arduino and do most of the work there. The C64 can be the display and input/output for controlling the trains.

Interfacing both components

The C64 User Port has both a 'parallel port' with 8 i/o pins and a serial port. I initially wanted to use the parallel pins, but came to the conclusion that I'd have to write my own language on both sides and deal with the data transfer timings and clock synchronisation. Instead, it'll be easier to use industry-standard RS-232!

I suppose this is a bit of a cop-out. I wanted to build something that was dated to the level of technology that existed back when these machines were in their prime... unfortunately my electronic knowledge isn't up to scratch... so getting to a variable 12v output wasn't overly easy. It also would not have been PWM. Due to all this, including the Arduino into the mix isn't such a bad idea. Plus, everyone I'd asked for help told me to do this... even sending me links to my own blog posts :)

DTE vs. DCE

Serial plugs have a single channel, with each end having one transmit (TX) and one receive (RX) pin. Each end will send data down the cable via the TX pin and expect to receive data on the RX pin. Standard serial cables are 'straight through', meaning that the TX pin is connected to the TX pin and likewise with RX. Doesn't quite make sense, does it? How are two separate devices meant to eachother if they are both transmitting down the same singular TX wire and hearing silence on the RX?

This all becomes clear once you realise that devices fit into two categories: DTE (data terminal equipment) and DCE (data circuit-terminating equipment, also known as data communication equipment.) In the end, these two devices boil down to one being the master (the DTE) and one being the slave (the DCE.)

Of course, you can purchase 'cross-over' cables for serial connections. These are known as null-modem cables and allow you to hook two DTEs together. Say, for example, you want to transfer a file from your PC to your Amiga, or somesuch!

In my previous serial project, when I connected the IBM receipt printer to the Arduino, I needed the Arduino to be the master, and so I hacked around until I had correctly configured it as a DTE. This time around we want the Arduino to be the DCE. Due to this, be careful to follow the pinouts and wiring from the serial port to the MAX232 in the circuits below!

Note: For further reading/wiring on RS-232, there's a good article at avr Programmers and another at Advantech.

C64 Serial Port

The User Port on the C64 also has serial connections. These are TTL and so need to be brought up to the RS-232 5v standard. The MAX232 IC will do this for us quite easily. We'll also use one at the other end for the Arduino.

UPDATED (2024): The CTS and RTS wires were incorrectly ordered on the original diagram. The following diagram is now correct, but I've decided to leave the comments at the bottom of this article which state otherwise.

The circuit is derived from 8bitfiles.net. This circuit was also correct in that the pins are wired up as DTE. This means that you could use it, as-is, to also hook to a modem or any other DCE device.

The MAX232 needs few extra components. Fortunately, these extra components are identical on both ends, so buy everything in duplicate! The capacitors are all 1.0uf electrolytic. I used 1k resisters on the LEDs so as not to draw too much current from the User Port.

Arduino Serial Port

This is nearly the same circuit as the C64 end. The funniest thing happened here... if you google enough 'arduino max232' you'll just loop back around to my post, from ages ago on interfacing an IBM printer to the Arduino. Just make sure you don't use that circuit! It's DTE, we need DCE as per below! I've left out the RTS/CTS as I don't intend on using any form of handshaking in this project. It's still in the circuit above for the C64 so that you can use the port for other purposes.

ARDUINO-RS232

As per usual, make sure you DO NOT apply 5v in the wrong direction... I did and it ruined a few caps and possibly the IC. Garbage then came through the serial port. If this ever happens, then throw out the components and start again; you'll never be able to trust them.

Also make sure that you use the 5v pin on the Arduino. AREF is NOT a valid voltage source.

Hooking it all together

Build both circuits above and give one a male and the other a female db-9 connector. The DCE device usually gets the female, so put this on the Arduino-side!

DSC03947 DSC03956 DSC03954

DSC03955 DSC03950

If you want to roll your own cable, then grab some grey IDC and two crimp-style plugs. Just make sure that you have pin 1 matched to pin 1. If you're splitting the cable, then paint a wire (or use a marker) to ensure that you get the orientation correct. It's really easy to confuse pin 1.

DSC03998 DSC04000 DSC04002

From above, you can see the pin numbering. I slid the second port all the way to the end, prior to crimping, to ensure that the numbers matched up. Using the red '#1 wire' on the cable worked wonders too.

Testing with Strike Terminal 2013 Final

Download Strike Term 2013 Final from here and then get it to your C64. I copied the D64 to my SD2IEC and loaded it up. Hit M and select User port. Hit b and switch it to 1200 Baud (or other baud, depending on what you've configured in the Arduino.)

DSC03976 DSC03978 DSC03957

DSC03958 DSC03964 DSC03965

Once ready, hit f5 and then hit enter on the default server. This'll start sending modem AT commands down the serial and they should start showing up at the other end. Either open the Arduino Serial Monitor... or edit the code to display it. I bought some 8x8 LED Matrices to render the data coming in.

DSC03963 DSC03971

There were no real caveats here... everything just worked! Press f3 to get to the terminal. Hit commodore+e for local echo and then commodore+i to 'send id'. You should now be able to type freely... everything will be sent down the wire.

DSC03983 DSC03984 DSC03985

DSC03986 DSC03987 DSC03988

At that point I only had one matrix... so the last char typed was displayed.

Writing C code to use the Serial Port

Nanoflite has written a 2400 baud User Port Serial Driver for cc65. I originally tried to use this at 1200 baud, as that's what I'd been using everywhere and heard it was the max the User Port was capable of. It turns out that this driver only supports 2400 baud! Download it and put the source somewhere.

Switch to the driver directory and compile it:

cl65 -t c64 --module -o c64-up2400.ser c64-up2400.s

Copy this to the folder that has your source file it. I slightly modified the example.

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <serial.h>
#define DRIVERNAME  "c64-up2400.ser"

static const struct ser_params serialParams = {
    SER_BAUD_2400,      /* Baudrate */
    SER_BITS_8,         /* Number of data bits */
    SER_STOP_1,         /* Number of stop bits */
    SER_PAR_NONE,       /* Parity setting */
    SER_HS_NONE         /* Type of handshake to use */
};

int main (void)
{
  int xx;
  
  clrscr();
  puts("C64 serial ...");

  // Load driver
  ser_load_driver(DRIVERNAME);

  // Open port
  ser_open(&serialParams);

  // Enable serial
  ser_ioctl(1, NULL);

  for (xx = 0; xx < 10; xx++) {
	ser_put('C'); 
	ser_put('6'); 
	ser_put('4');
	ser_put('.');
	ser_put('.');
	ser_put('.');
  }
 
  return EXIT_SUCCESS;
}

Compile this:

cl65 -t c64 -O -o trainctl2 trainctl2.c

I then put it on the SD2IEC and loaded it via LOAD "0:TRAINCTL2",8 followed by a RUN.

DSC03993 DSC03989 DSC03991

DSC03992 DSC03994 DSC04003

Shit... worked... this is great! Next it's time to put a PWM throttle onto the Arduino and control it from the Commodore... I'll tinker with graphical programming in C also.