Reading an N64 controller with a microcontroller

We’ve seen NES, SNES, Sega, and just about every weird controller Atari put out connected to microcontrollers, but connecting the N64 controller to a project has remained one of those seldom-seen, rarely copied endeavors, not often tackled by makers around the globe. [Pieter-Jan] decided to throw his hat in the ring and give reading an N64 controller with a PIC a try, and we’re pleased to report he’s been completely successful.

One of the difficulties of reading an N64 controller is simply the speeds involved; with only three pins on the controller port, the N64 controller uses a serial protocol to send 32 bits of controller data at a fairly fast rate. Armed with a PIC18F ‘micro, [Pieter] realized that programming in C would be too slow, he needed to go all the way down to the bare metal and program his micro in assembly.

Every time the N64 controller data needs to be read, the console sends out a 9-bit polling request. The controller responds in turn with a 32-bit sequence informing the console of the status of all the buttons and joysticks. Once [Pieter] got his micro sending the correct polling response, it was only an issue of parsing the data returned from the controller.

Right now, [Pieter] has a small demo board rigged up that flashes a LED whenever the A, B, or Z buttons are pressed. This can be expanded to the remaining buttons and joystick, but for now we’ll just enjoy [Pieter]’s demo after the break.

29 thoughts on “Reading an N64 controller with a microcontroller

  1. I wonder if this is the reason that Killer Instinct Gold has always felt like the most responsive fighting game to me…

    I always thought it was the KI software, but it may just have been the N64 hardware all along!

  2. Why limit yourself as far as clock speed to the point where you can’t write in C anymore? Just put in a faster crystal. There are 48mhz versions of those USB 18f chips. There are also C routines for fast interrupts and stuff like that. It’s not like you have to deal with an undeterministic scheduler or something. If you are just spinning in a loop somewhere it’s not wasting many cycles having been written in C.
    Or just get a faster chip. There are enough hard things you don’t’ have control over. Use the best tools for the job.

    1. I worked out at one point that to do the n64 controller side without assembly would require a 200MHz micro. For the host side I think you’d possibly be able to do it with 48MHz but that would require turning a host of space optimisations off. Assembly isn’t that hard, it’s better for high speed bit banging and writing the routines in c would require examination of the disassembly.

      This is perhaps one of the two applications you’ll ever want to use assembly.

  3. “Armed with a PIC18F ‘micro, [Pieter] realized that programming in C would be too slow, he needed to go all the way down to the bare metal and program his micro in assembly.”

    That’s simply not true.

    1. As I explained in the article I wanted to limit myself to a clock speed of 20Mhz so that I could maintain compability with my other projects. The resolution of the signal to be read is 1 uS. With a PIC this means that there are only 5 instruction cycles. (On an ATmega this is a whole other story – you have 20 instruction cycles there) so that is not enough to use C.

      Switching to other architectures and other clock speeds would make C possible but that was not the challenge I put out for myself.

  4. Excellent write-up on the project. I actually did this using a PIC24 and C/assembly. It was quite a challenge since I didn’t have access to an oscilloscope and was relying on MPlab simulator for the timing information. It would be very difficult to do anything like this only using C

  5. I’ve been trying this on the Raspberry Pi without any supporting circuitry and can’t get a reliable input. I can send poll requests to the controller just fine, but it takes about 2 seconds to get a response that I’m confident in (32 bits) which just won’t do. Of course this is because I’m running Raspbian and coding it in C, but I’m much too busy to get it down further.

  6. I think a cooler solution would repurpose an existing shifter in the MCU to do the work for you. For example you could use an MSP430 USI in SPI mode for the output quite trivially (4 bits per bit in fixed patterns). For the receive you could do clock syncronisation and just sample at the mid-point of each period giving you 1 bit per bit input.

    If you used the G2121 for example and connected P1.6+P1.7 together to the data line you would output on 1.7 and then swap it to a timer input to track the clock synchronisation.

    If you ran TimerA in up mode with TimerA0 set to half the expected period and the output set to toggle on reset you could use it to clock USI. If TimerA1 is set to capture mode (falling edge) then the value captured in A1 is twice synchronisation error of TimerA0 allowing you to track the clock of the controller.

    This only requires you to run at 1Mhz for transmit and maybe for receive (the timers can run at double the clock rate, giving 4 counts per half period) if you don’t need correction. Running the receive with correction you’d need at least 4+2+4+1+4=15 CPU cycles per half period so 8Mhz to correct every cycle.

    1. +1. Had just finished reading the linked article and was getting ready to post the same output technique, but you beat me to it. :)

      There’s probably several ways input could be managed. SPI might be able to do it, if after sending the poll request you immediately drop the clock rate by a factor of four so it samples in the middle of each period, then send 4x bytes containing zero. Or use the Input Capture peripheral to measure the time of each on/off interval.

      At any rate, bitbanging is not the only way to accomplish this, nor is assembly required.

      1. I don’t have any familiarity with PIC but reading an app note on the Input Capture the FIFO in particular makes it attractive (I love getting peripherals to do as much work without MCU intervention as possible). You could capture just the rising edges and toggle high on a short interval and low on a long interval and preserve the existing value otherwise. You’d just need to measure the first low interval to get the starting value.

  7. Honestly can’t understand why anyone would willingly choose to use an N64 pad…

    It might be worth noting that GameCube pads use pretty much the same communication methods with like one extra byte for the C stick’s analog data IIRC.

      1. I’ve never used an n64 (honest) but gamecube controllers were shockingly bad. I don’t think ot was the controller itself so much but the quality of them, the thumb sticks almost seemed digital, buttons were noisey and rattled in their places and it wasn’t the most comfortable. Although I still have my gamecube and controllers I instead use a wired Xbox 360 pad on the dolphin emulator, much better experience. Build quality has room for improvement but is far superior to that of the GC pad and its analogue sticks are analogue… My games have been improved substantially…

        Now, if someone built a good controller for the GC in the first place….

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.