¿Alguien tiene un código para emular un registro de desplazamiento de entrada de 16 bits con un ATtiny2313?

Me gustaría usar un ATtiny2313 para emular un controlador Super Nintendo porque tengo un ATtiny2313 pero no tengo un registro de desplazamiento de entrada y no tengo ganas de soldar cables en una placa controladora SNES existente.

Esta aplicación requiere 12 entradas (los bits 13-16 siempre son 1) y un pin de salida de datos, reloj y pestillo.

¿Tienes este código por ahí? No puede ser más de 20 instrucciones.

Antes de responder, lo que escucho es que desea recibir información de un controlador SNES usando un ATTiny2313, ¿es así? Creo que este es un proceso "activo" en el que necesita "muestrear" el controlador ...
Quiero que el ATtiny2313 actúe como dispositivo esclavo, conectado a una consola SNES real.

Respuestas (4)

Ya hay un registro de desplazamiento de 8 bits integrado en la interfaz serie universal (USI) . Todo lo que tienes que hacer es usarlo dos veces seguidas.

No tengo este código por ahí, pero tengo las siguientes mejores cosas:

Podrías decir que tengo el código por ahí, pero no está organizado y concatenado correctamente... ;)

lol, tenía un código para un registro de desplazamiento de 16 bits para MSP430, pero tampoco puedo encontrarlo
Olvidaste incluir '1' y '0', los dos números que necesitaré para aprender a programar una computadora binaria.
@joeforker, Afortunadamente, tenemos mnemónicos incluso para secuencias largas de 1 y 0, ¡y además técnicas avanzadas para unir estos mnemotécnicos! No hay nada mejor que eso. ;)
@joe: he programado usando código de máquina, y además de 1 y 0, uso 2-9 y AF.

Como dice @tyblu, todo está en el manual de la USI. En particular, lo que desea es usarlo en "Operación en modo maestro de tres cables". Eche un vistazo a la Figura 61 en la página 143 de la hoja de datos. La hoja de datos es incluso lo suficientemente amable como para proporcionar una rutina de ensamblador optimizada (8 instrucciones) (SPITransfer) para este propósito exacto:

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

Si lo conecta de modo que tenga un pin 2313 conectado al cable de "cierre" del controlador (naranja), el pin USCK se conecta al cable de "pulso" del controlador (rojo) y el pin DI se conecta al cable de "datos" del controlador. " alambre (amarillo) - deberías estar listo para irte. Según la hoja de datos, el ejemplo de código asume que los pines DO y USCK están habilitados como salidas en el registro DDRB.

Una vez que el hardware está configurado, todo lo que necesita hacer es generar un pulso alto de 12 us en el pin de "cierre", dejar caer el pin bajo para 6 us, luego llamar a la rutina de ensamblaje anterior dos veces (guardando el valor de retorno) después de cada llamada.

Encontré esta referencia muy informativa sobre la interfaz (S)NES.

@joeforker, me gustaría saber si esto funciona para usted; háganos saber sus hallazgos ... Creo que debe estar en modo maestro para que ATTiny2313 genere la señal de reloj (como lo haría SNES de otra manera hacer).
nuevamente, gracias por esto, pero el ATtiny2313 pretende ser el gamepad. El papel de maestro lo cumplirá una consola SNES real.
@joeforker, lo entendí mal, pensé que estabas tratando de controlar el Tiny2313 con un gamepad.

El siguiente código funciona muy bien. La señal de enganche está conectada a PB5. El SIG_PIN_CHANGEcontrolador hace su trabajo en ambos bordes de la señal del pestillo, pero no importa, y la última vez que intenté agregar el cheque no funcionó.

#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();
    }
}
Usualmente lo uso -Ospara optimizar el tamaño y -fwhole-programcuando construyo todo a la vez. Ahorra algo de espacio y generalmente acelera las cosas. Para hacer coincidir con precisión el controlador, desea aceptar el bloqueo en cualquier momento (razón por la cual los controladores SNES funcionan en NES, que solo lee 8 bits). Para errores tipográficos: hola se lee desde PORTDen lugar de PORTB, y debe usar PINBy PINDde todos modos para leer las entradas. Posiblemente quiera enmascarar D7 usando PIND&0x7ftambién.
Originalmente, me preocupaba no tener suficiente tiempo para verificar el pasador del pestillo en una interrupción. De acuerdo con repairfaq.org/REPAIR/F_SNES.html#SNES_005 , el reloj es una onda cuadrada de 12 microsegundos por ciclo y los gamepads se sondean a aproximadamente 60 Hz. Eso debería ser suficiente tiempo para hacer lo que hay que hacer.
¡Buen progreso! El siguiente bit que desea ver es cómo borra los bits de estado. Ha notado que los indicadores de interrupción se borran al configurarlos, pero la razón es evitar los ciclos de lectura, modificación y escritura como lo hace |=. Además, hay más cosas que desea cambiar. Escriba el USISR completo para restablecer el contador de desplazamiento, de modo que la siguiente interrupción de desbordamiento se produzca después de 8 bits (cuenta ambos bordes, por lo que 0 probablemente sea correcto). También puede hacer que la interrupción de cambio de pin ignore uno de los bordes al verificar el nivel del pin.
Gracias Yann. Me di cuenta de que tenía que configurar el contador, pero aún no he probado el nuevo código. En gcc, |emite la sbiinstrucción cuando es posible. De acuerdo con la hoja de datos, a diferencia de la mayoría de los indicadores de interrupción, USIOIFsolo se borra con un 1y no se borra automáticamente cuando regresa el controlador de interrupción.
Bien. sbiestá limitado a los 32 registros de E/S inferiores, pero el USI está en ese rango en el tiny2313. Lo que conduce a una peculiaridad extraña: PORTA|=1; PORTA|=2;es más rápido que PORTA|=3;, ya que este último compila en 3 instrucciones (pero establece ambos bits en el mismo ciclo). No es tu problema, solo una peculiaridad.
Felicidades por la publicación de Hackaday. hackaday.com/2011/01/30/snes-arcade-controller Además, ¡es bueno ver la compilación!