terça-feira, 21 de fevereiro de 2012

MSX + Arduino

A plataforma Arduino tem suas qualidades e seus defeitos, mas sem entrar nesse mérito, uma coisa é inegável: A plataforma que 'pegou'. É fácil de encontrar para comprar, e qualquer um pode programar facilmente plataforma, usando seu computador, seja ele um PC ou Mac, sem a necessidade de hardware de programação especial.

Sendo assim, eu resolvi refazer o adaptador de Nunchuck para MSX mas desta vez usando um Arduino como plataforma.
Eu chamo de refazer pois já havia implementado o adaptador utilizando um 68HC908 em encapsulamento TSSOP, mas veio a dúvida: quantas pessoas poderiam replicar um adaptador assim? Daí a escolha do Arduino.

Segue abaixo o diagrama de conexão:


Eis o circuito em funcionamento:

Mais fotos no Álbum do Picasa.

O código encontra-se abaixo. Ainda é a versão básica, que mapeia somente os analógicos e os botões de tiro, mas é fácil adaptar para os acelerômetros, acrescentar calibração, etc....
O adaptador para o Wii nunchuck pode ser feito em casa ou então comprado.


//
// Play on MSX with Wii Nunchuck and Arduino By Danjovic, 2012
// Based nunchuck libraries by Michael Dreher and Tim Hirzel
//

/*
MSX Joystick
Activates sequentially the following directions from an MSX/Atari Joystick
UP, DOWN, LEFT, RIGHT, TRIGGER A, TRIGGER B

CONNECTIONS

Function  DB-9   Arduino   AVR
UP         1     Pin 2    (PD2)
DOWN       2     Pin 3    (PD3)
LEFT       3     Pin 4    (PD4)
RIGHT      4     Pin 5    (PD5)
+5VCC      5     +5V      +Vcc
TRG A      6     Pin 6    (PD6)
TRG B      7     Pin 7    (PD7)
GND        9     GND      GND

*/


// adapt to your hardware config
#define POWER_VIA_PORT_C2_C3 1 // use port pins port C2 and C3 as power supply of the Nunchuck (direct plug using wiichuck adapter)

#define USE_NEW_WAY_INIT 1 // use "The New Way" of initialization <http://wiibrew.org/wiki/Wiimote#The_New_Way>
#define WII_IDENT_LEN ((byte)6)
#define WII_TELEGRAM_LEN ((byte)6)
#define WII_NUNCHUCK_TWI_ADR ((byte)0x52)

#include <Wire.h>
//#include <string.h>
#include <utility\twi.h>
#undef int
#include <stdio.h>

uint8_t outbuf[WII_TELEGRAM_LEN];  // array to store arduino output
int cnt = 0;
int ledPin = 13;

int Up_Pin    = 2;                 // LED connected to digital pin 13
int Down_Pin  = 3;                 // LED connected to digital pin 13
int Left_Pin  = 4;                 // LED connected to digital pin 13
int Right_Pin = 5;                 // LED connected to digital pin 13
int TrgA_Pin  = 6;                 // LED connected to digital pin 13
int TrgB_Pin  = 7;                 // LED connected to digital pin 13


void setup ()
{
// initialize Pin directions
pinMode(Up_Pin, INPUT);
pinMode(Down_Pin, INPUT);
pinMode(Left_Pin, INPUT);
pinMode(Right_Pin, INPUT);
pinMode(TrgA_Pin, INPUT);
pinMode(TrgB_Pin, INPUT);

// initialize Buffer output buffers
digitalWrite(Up_Pin, LOW);
digitalWrite(Down_Pin, LOW);
digitalWrite(Left_Pin, LOW);
digitalWrite(Right_Pin, LOW);
digitalWrite(TrgA_Pin, LOW);
digitalWrite(TrgB_Pin, LOW);

#ifdef POWER_VIA_PORT_C2_C3 // power supply of the Nunchuck via port C2 and C3
PORTC &=~ _BV(PORTC2);
PORTC |= _BV(PORTC3);
DDRC |= _BV(PORTC2) | _BV(PORTC3); // make outputs
delay(100); // wait for things to stabilize
#endif

Wire.begin(); // initialize i2c
// we need to switch the TWI speed, because the nunchuck uses Fast-TWI
// normally set in hardware\libraries\Wire\utility\twi.c twi_init()
// this is the way of doing it without modifying the original files
#define TWI_FREQ_NUNCHUCK 200000L
TWBR = ((CPU_FREQ / TWI_FREQ_NUNCHUCK) - 16) / 2;

nunchuck_init(0); // send the initialization handshake

}


// params:
// timeout: abort when timeout (in ms) expires, 0 for unlimited timeout
// return: 0 == ok, 1 == timeout
byte nunchuck_init (unsigned short timeout)
{
byte rc = 1;

#ifndef USE_NEW_WAY_INIT  // look at <http://wiibrew.org/wiki/Wiimote#The_Old_Way> at "The Old Way"
Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
Wire.send (0x40);  // sends memory address
Wire.send (0x00);  // sends sent a zero.
Wire.endTransmission (); // stop transmitting
#else
// disable encryption
// look at <http://wiibrew.org/wiki/Wiimote#The_New_Way> at "The New Way"
unsigned long time = millis();
do {
Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
Wire.send (0xF0);  // sends memory address
Wire.send (0x55);  // sends data.
if(Wire.endTransmission() == 0) // stop transmitting
{
Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
Wire.send (0xFB);  // sends memory address
Wire.send (0x00);  // sends sent a zero.
if(Wire.endTransmission () == 0) // stop transmitting
{
rc = 0;
}
}
}
while (rc != 0 && (!timeout || ((millis() - time) < timeout)));
#endif
return rc;
}


// params:
// ident [out]: pointer to buffer where 6 bytes of identification is stored. Buffer must be at least 6 bytes long.
// A list of possible identifications can be found here: <http://wiibrew.org/wiki/Wiimote#The_New_Way>
// return: 0 == ok, 1 == error
byte readControllerIdent(byte* pIdent)
{
byte rc = 1;

// read identification
Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
Wire.send (0xFA);  // sends memory address of ident in controller
if(Wire.endTransmission () == 0) // stop transmitting
{
byte i;
Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN); // request data from nunchuck
for (i = 0; (i < WII_TELEGRAM_LEN) && Wire.available (); i++)
{
pIdent[i] = Wire.receive(); // receive byte as an integer
}
if(i == WII_TELEGRAM_LEN)
{
rc = 0;
}
}
return rc;
}

void clearTwiInputBuffer(void)
{
// clear the receive buffer from any partial data
while( Wire.available ())
Wire.receive ();
}


void send_zero ()
{
// I don't know why, but it only works correct when doing this exactly 3 times
// otherwise only each 3rd call reads data from the controller (cnt will be 0 the other times)
for(byte i = 0; i < 3; i++)
{
Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
Wire.send (0x00);  // sends one byte
Wire.endTransmission (); // stop transmitting
}
}

void loop ()
{
Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN); // request data from nunchuck

for (cnt = 0; (cnt < WII_TELEGRAM_LEN) && Wire.available (); cnt++)
{
outbuf[cnt] = nunchuk_decode_byte (Wire.receive ()); // receive byte as an integer
digitalWrite (ledPin, HIGH); // sets the LED on
}
clearTwiInputBuffer();

// If we recieved the 6 bytes, then go print them
if (cnt >= WII_TELEGRAM_LEN)
{
do_output ();
}
// send the request for next bytes
send_zero ();
delay (10);
}

// Drive the Joystick pins
// accel data is 10 bits long but only 8 MSBs are being considered
// Z axis is not mapped to anything
void do_output ()
{
int joy_x_axis = outbuf[0];
int joy_y_axis = outbuf[1];
int accel_x_axis = outbuf[2];
int accel_y_axis = outbuf[3];
int accel_z_axis = outbuf[4];
int C_Z_buttons = outbuf[5];  // byte outbuf[5] contains bits for z and c buttons

// Up Direction
if (joy_y_axis > 192)
{
pinMode(Up_Pin, OUTPUT);    // Unassert pin
}
else
{
pinMode(Up_Pin, INPUT);  // Assert pin
}

// Down Direction
if (joy_y_axis < 64)
{
pinMode(Down_Pin, OUTPUT);    // Unassert pin
}
else
{
pinMode(Down_Pin, INPUT);  // Assert pin
}

// Right Direction
if (joy_x_axis > 192)
{
pinMode(Right_Pin, OUTPUT);    // Unassert pin
}
else
{
pinMode(Right_Pin, INPUT);  // Assert pin
}

// Left Direction
if (joy_x_axis < 64)
{
pinMode(Left_Pin, OUTPUT);    // Unassert pin
}
else
{
pinMode(Left_Pin, INPUT);  // Assert pin
}


// Trigger B
if (C_Z_buttons & 1)
{
pinMode(TrgB_Pin, INPUT);    // Unassert pin
}
else
{
pinMode(TrgB_Pin, OUTPUT);  // Assert pin
}

// Trigger A
if (C_Z_buttons & 2)
{
pinMode(TrgA_Pin, INPUT);    // Unassert pin
}
else
{
pinMode(TrgA_Pin, OUTPUT);  // Assert pin
}

}

// Decode data format that original Nunchuck uses with old init sequence. This never worked with
// other controllers (e.g. wireless Nunchuck from other vendors)
char nunchuk_decode_byte (char x)
{
#ifndef USE_NEW_WAY_INIT
x = (x ^ 0x17) + 0x17;
#endif
return x;
}




2 comentários:

Ricardo Murad disse...

Cara bacana vou testar no meu hotbit

mekatron disse...

Cara ta dando erro pra mim numa linha do pgm, "TWBR = ((CPU_FREQ / TWI_FREQ_NUNCHUCK) - 16) / 2;"

'CPU_FREQ'was not declared in this scope.
???