«

»

Nintendo 64 controller on AVR

I went on my regular trip to the dumpster the other day, and found no less than two Nintendo 64 controllers. It’s time to connect them to a microcontroller!

The interface

The N64 Controller uses only three pins: Ground, data and Vcc. It takes 3.6v signals bidirectionally.


The signal looks as follows:


A polling signal contains the bits “000000011”. These need to be sent to the N64 controller for it to respond by sending its data. The response is sent immediately after the polling signal has been received.

Index Content
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16-23
24-32
33
A
B
Z
Start
Up
Down
Left
Right
Nothing
Nothing
L
R
C-Up
C-Down
C-Left
C-Right
X-Axis
Y-Axis
Stop bit

 

Hardware

Using my sheep boards as a starting point, I connected a controller to an attiny85 microcontroller.

The data line on the controller was connected to the INT0 pin (PB2) on the AVR. This pin is for external interrupts, and can set a flag for falling, changing or rising edges on a pulse.


For testing purposes I cheated a little with the voltage source. As I’ve only got a 5v voltage source at the moment, I passed this through the 7805 IC on the sheep board. As the voltage regulator is ineffective, I got a voltage drop down to 4v. I then connected an additional diode between the voltage source and the voltage regulator, giving 3.3v.

Code

The main challenge with this interface is that 1us is a very short time. Only a very limited number of instructions can be called when interpreting each pulse. The sheep board does not have space for a crystal oscillator, so the AtTinys internal PLL was used to boost the 8MHz internal oscillator up to 16MHz. PLL configuration:

This configuration helps a lot on the accuracy of the pulse measurements. PLL mode must also be set in the fuse bits when programming the chip, for instance in AVR Studio:


The original plan was to use interrupts to generate a non-blocking polling signal, and to measure the incoming pulses by interrupt calls. As it turns out, there are not many cycles left to play with after the interrupt function is called. It is possible to generate the output pulse as an PWM signal, doing everything in hardware. The problem would then be to know when the first seven zeroes have been sent, to change the output to ones. For the pulse reading, an interrupt call did not leave enough free cycles to sort the received data.

Writing

cli() is called before writing, to make sure no interrupts can disturb the delicate timing. After data has been written and received, sei() is called to reactivate the interrupts.

To send a zero:

To send a one:

Reading

When “000000011” has been sent, the controller sends its data immediately. To have time to catch the first pulse, it was caught outside the for loop. It is important that the external interrupt flag is cleared for each incoming pulse, as it is vital to the timing.

In pseudo code:

For each pulse
Wait for the falling edge at the start of each bit
Reset pin to detect next falling edge when we are done
Count how long the low pulse is
Store the pulse length
Next pulse

Now, if the pulse measured is shorter than 8, it represents a bit 1. This is a little counter intuitive, but it works. As a worst case, one could wrap this into a set of functions, for instance isPressed(N64_A) returning true if button A is pressed. This is a choice between intuitiveness and effectiveness.

The analog joystick comes in as two signed bytes, one for x-axis and one for y-axis. These bytes are sorted into bits in a char in the following way:

The loop goes through the 8 bits for x and y, bitshifting an 1 into the char if the recorded bit was a 1 (low pulse shorter than 8).

Usage

The following code is an example of how to use this library:

Future work

– Support for two controllers at once
– Noise creates faulty A-button pushes

GitHub

n64-controller

Download

n64_code.zip

Further reading

The N64 interface project

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">