3
\$\begingroup\$

I would like to use an ATtiny2313 to emulate a Super Nintendo controller because I have an ATtiny2313 but I do not have an input shift register and I don't feel like soldering wires onto an existing SNES controller board.

This application requires 12 inputs (bits 13-16 are always 1) and a latch, clock, and data out pin.

Do you have this code lying around? It can't be more than 20 instructions.

\$\endgroup\$
  • \$\begingroup\$ Before I answer, what I'm hearing is that you want to receive input from an SNES controller using an ATTiny2313, is that right? I believe this is an "active" process in that you need to "sample" the controller... \$\endgroup\$ – vicatcu Jan 12 '11 at 20:55
  • \$\begingroup\$ I want the ATtiny2313 to act as the slave device, attached to a real SNES console. \$\endgroup\$ – joeforker Jan 14 '11 at 15:33
  • \$\begingroup\$ Nicely done! hackaday.com/2011/01/30/snes-arcade-controller \$\endgroup\$ – Toby Jaffey Jan 30 '11 at 21:56
6
\$\begingroup\$

There's already an 8-bit shift register built into the Universal Serial Interface (USI). All you have to do is use it twice in a row.

\$\endgroup\$
3
\$\begingroup\$

I don't have this code lying around, but I do have the next best things:

You might say I do have the code laying around, but it's not organized and concatenated correctly... ;)

\$\endgroup\$
  • \$\begingroup\$ lol I had code for a 16bit shift register for MSP430, but cant find it as well \$\endgroup\$ – jsolarski Jan 12 '11 at 4:57
  • 1
    \$\begingroup\$ You forgot to include '1' and '0', the two numbers I will need to learn to program a binary computer. \$\endgroup\$ – joeforker Jan 12 '11 at 18:41
  • \$\begingroup\$ @joeforker, Fortunately we have mnemonics for even long sequences of 1's and 0's, and furthermore advanced techniques for stringing these mnemonics together! I doesn't get any better than that. ;) \$\endgroup\$ – tyblu Jan 12 '11 at 18:55
  • \$\begingroup\$ @joe: I have programmed using machine code, and in addition to 1's and 0's I use 2-9 and A-F. \$\endgroup\$ – Nick T Jan 13 '11 at 1:12
3
\$\begingroup\$

As @tyblu says, it's all right there in the manual for the USI. In particular what you want is to use it in "Three-Wire Master Mode Operation." Take a look at Figure 61 on page 143 of the datasheet. The datasheet is even kind enough to provide an optimized (8 instruction) assembler routine (SPITransfer) for this exact purpose:

SPITransfer:
  out USIDR,r16
  ldi r16,(1<<USIOIF)
  out USISR,r16
  ldi r16,(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC)
SPITransfer_loop:
  out USICR,r16
  sbis USISR,USIOIF
  rjmp SPITransfer_loop
  in r16,USIDR
  ret

If you wire it up so that the you have a 2313 pin connected to the controller's "latch" wire (orange), the USCK pin connects to the controller's "pulse" wire (red), and the DI pin connects to the controller's "data" wire (yellow) - you should be good to go. Per the datasheet, the code example assumes that the DO and USCK pins are enabled as outputs in the DDRB Register.

Once the hardware is set up, all you need to do is generate a 12us high pulse on the "latch" pin, drop the pin low for 6us, then call the above assembly routine twice (saving the return value away) after each call.

I found this reference very informative regarding the (S)NES interface.

\$\endgroup\$
  • \$\begingroup\$ @joeforker, I'd be curious to know if this works for you - please let us know your findings... I think you have to be in master mode in order for the ATTiny2313 to generate the clock signal (as the SNES would otherwise do). \$\endgroup\$ – vicatcu Jan 14 '11 at 15:08
  • \$\begingroup\$ again, thanks for this, but the ATtiny2313 is pretending to be the gamepad. The role of master will be fulfilled by a real SNES console. \$\endgroup\$ – joeforker Jan 14 '11 at 15:32
  • \$\begingroup\$ @joeforker, got it I misunderstood - thought you were trying to control the Tiny2313 with a gamepad. \$\endgroup\$ – vicatcu Jan 14 '11 at 16:52
0
\$\begingroup\$

The following code works great. The latch signal is connected to PB5. The SIG_PIN_CHANGE handler does its work on both edges of the latch signal but it doesn't matter, and the last time I tried adding the check it did not work.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

ISR(SIG_PIN_CHANGE)
{
    USIDR = PINA | (PIND & 0b01111100) | (PINB << 7);
    USISR = (1 << USIOIF); // clear overflow bit, set counter to 0
    USICR |= (1 << USIOIE); // enable overflow interrupt
}

ISR(SIG_USI_OVERFLOW)
{
    USIDR = (PINB << 3) | 0x0f;
    USISR |= (1 << USIOIF); // clear overflow bit
    USICR &= (0xff ^ (1 << USIOIE)); // disable overflow interrupt
}

int main() {

    USIDR = 0xff;
    USICR = (1 << USIWM0) | (1 << USICS1); // 3-wire mode; external, positive edge.

    DDRA = 0;
    DDRD = 0;
    DDRB = 1 << 6; // MISO

    // Enable pullups
    PORTA = 0x3;
    PORTB = 0b11111;
    PORTD = 0xfc;

    // USIDR is shifted out MSB first.

    // pin change interrupt for latch pin
    PCMSK = (1 << 5);
    GIMSK |= (1 << PCIE);

    sei();

    while (1) {
        sleep_mode();
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ I usually use -Os for size optimization, and -fwhole-program when building everything at once. It saves some space and usually speeds things up. To accurately match the controller, you want to accept latch at any time (which is why SNES controllers work on NES, which only reads 8 bits). For typos: hi is read from PORTD instead of PORTB, and you should use PINB and PIND anyway to read the inputs. Possibly want to mask away D7 using PIND&0x7f too. \$\endgroup\$ – Yann Vernier Jan 14 '11 at 7:35
  • \$\begingroup\$ Originally I was worried about not having enough time to check the latch pin in an interrupt. According to repairfaq.org/REPAIR/F_SNES.html#SNES_005 , the clock is a 12 microseconds per cycle square wave and the gamepads are polled at about 60Hz. That should be plenty of time to do what needs to be done. \$\endgroup\$ – joeforker Jan 14 '11 at 13:59
  • \$\begingroup\$ Good progress! The next bit you want to look at is how you clear the status bits. You've noted interrupt flags are cleared by setting them, but the reason is to avoid read-modify-write cycles like |= does. In addition, there's more you want to change. Write the entire USISR to reset the shift counter, so the next overflow interrupt comes after 8 bits (it counts both edges, so 0 is probably right). You may also want to make the pin change interrupt ignore one of the edges by checking the pin level. \$\endgroup\$ – Yann Vernier Jan 29 '11 at 12:00
  • \$\begingroup\$ Thanks Yann. I did realize I had to set the counter, but I haven't quite tested the new code yet. In gcc, | does emit the sbi instruction when possible. According to the datasheet, unlike most interrupt flags, USIOIF is only cleared by a 1 and not automatically cleared when the interrupt handler returns. \$\endgroup\$ – joeforker Jan 29 '11 at 13:41
  • \$\begingroup\$ Right. sbi is limited to the lower 32 I/O registers, but the USI is in that range on the tiny2313. Which leads to an odd quirk - PORTA|=1; PORTA|=2; is faster than PORTA|=3;, as the latter compiles to 3 instructions (but does set both bits the same cycle). Not your problem, just a quirk. \$\endgroup\$ – Yann Vernier Jan 30 '11 at 1:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.