NES controller decoder help

Started by knohbody, October 19, 2009, 12:38:51 AM

Previous topic - Next topic

knohbody

Ok guys, I'm trying to build a slightly modified version of this :

NES adapter

I'm just trying to convert the button presses from the NES controller into a basic parallel output (i.e. what the neogeo uses) so I can interface it with a UPCB.

Toodles' UPCB

I've built the circuit, minus the part that converts the digital signal to analog, which includes d1-4; R5, 6, and 7; and Q1 and 2.

When testing the circuit, the output lines go low only when A is pressed. No other button does anything, and button A triggers all outputs. Anyone have any clue what I did wrong?

Thanks.

NFG

You're well aware of course that the DB15 used on a PC is a completely different pinout than the NeoGeo connector, right?  Assuming you've skipped that stage entirely...

If you're getting all signals pressed with one button, look for shorts in the circuit, and look for things hooked up backwards, and check your wiring to the NES port a million times.  A is the first data out of the NES pad, so it's possible by pressing it you've somehow latched all output from the pad to LOW (by shorting it somehow?), or stopped the clock to the pad, etc.

Gotta be a wiring fault, IMO.

knohbody

Quote from: Lawrence on October 19, 2009, 07:40:31 AM
You're well aware of course that the DB15 used on a PC is a completely different pinout than the NeoGeo connector, right?  Assuming you've skipped that stage entirely...

If you're getting all signals pressed with one button, look for shorts in the circuit, and look for things hooked up backwards, and check your wiring to the NES port a million times.  A is the first data out of the NES pad, so it's possible by pressing it you've somehow latched all output from the pad to LOW (by shorting it somehow?), or stopped the clock to the pad, etc.

Gotta be a wiring fault, IMO.

I figured it was probably something in the wiring, but everything looked correct when i was checking. I'll triple and quadruple check till I find it. I'm aware that the pinout is different for a PC, but I'm only using this to produce the signals needed. Once I get that sorted out, I'll make a custom header to attach to the UPCB or a DB15 with a NEO GEO pinout to connect to the UPCB.

I posted this on the nesdev forums as well, and one of the guys there said it was likely a problem with the controller not seeing the clock, or the circuit not relatching after the clock signal, so I'll be looking at the 555 I have in there pretty closely.

Thanks for the help Lawrence.

ulao

#3
knohbody, If you dont have any luck ,this would be extremely easy on an avr chip.  I practically have the code for you, if you up for it.  The decoding for the nes is a walk in the park as its blasted all over the net, then you just pull up or low on a pin.  Done.. I'd suggest an atmega8. Let me know if you want to try it, be happy to help.


dimensionxor

#4
If you can get access to an oscilloscope, it could save you a lot of time for this sort of debugging. You should be able to pinpoint where the problem is pretty quickly with just a two channel scope by triggering on the clock and looking at what the data line is doing.

I also second ulao's suggestion, an AVR could make quick and easy work of this as it's basically the only component required. It isn't quite as cool as building the whole thing from raw logic chips, but the simplicity makes up for it. Something like this should do it:


#include <avr/io.h>
#define F_CPU    8000000UL
#include <util/delay.h>

#define NES_CLOCK PC2
#define NES_LATCH PC3
#define NES_DATA PC4
#define NES_DATA_PIN PINC
#define NES_PORT PORTC
#define NES_DDR DDRC

// NES controller read function, returns one byte containing 8 button states
uint8_t nes_read(void){
uint8_t nes_byte = 0xFF;
NES_PORT |= _BV(NES_LATCH);
_delay_us(12);
NES_PORT &= ~_BV(NES_LATCH);
_delay_us(6);
for(uint8_t bit_counter = 0; bit_counter < 8; bit_counter++){
(NES_DATA_PIN & _BV(NES_DATA)) ? (nes_byte |= _BV(bit_counter)) : (nes_byte &= ~_BV(bit_counter));
NES_PORT |= _BV(NES_CLOCK);
_delay_us(6);
NES_PORT &= ~_BV(NES_CLOCK);
_delay_us(6);
}
return(nes_byte);
}

int main(void){
DDRD = 0xFF; // all pins on PORTD set to output
NES_DDR |= _BV(NES_LATCH) | _BV(NES_CLOCK); // latch and clock pins set to output
NES_PORT |= _BV(NES_DATA); // enable pull-up resistor on NES data pin
uint8_t nes_data = 0xFF;

while(1){ // infinite loop
nes_data = nes_read(); // read states of 8 buttons from NES controller
PORTD = nes_data; // output NES button states on 8 pins of PORTD
_delay_ms(16); // delay for roughly 60Hz controller polling
}
return(0);
}

knohbody

Quote from: ulao on October 21, 2009, 03:01:33 AM
knohbody, If you dont have any luck ,this would be extremely easy on an avr chip.  I practically have the code for you, if you up for it.  The decoding for the nes is a walk in the park as its blasted all over the net, then you just pull up or low on a pin.  Done.. I'd suggest an atmega8. Let me know if you want to try it, be happy to help.


Quote from: dimensionxor on October 21, 2009, 02:31:54 PM
If you can get access to an oscilloscope, it could save you a lot of time for this sort of debugging. You should be able to pinpoint where the problem is pretty quickly with just a two channel scope by triggering on the clock and looking at what the data line is doing.

I also second ulao's suggestion, an AVR could make quick and easy work of this as it's basically the only component required. It isn't quite as cool as building the whole thing from raw logic chips, but the simplicity makes up for it. Something like this should do it:

Thanks guys, I think I'll probably end up going this route. I'd like to get the circuit I built working, but this way I could add snes support as well without adding any extra chips. I've never done anything with AVR chips, so it'll be a learning experience for me.

ulao

Do not hesistate to ask for help, lots of us have been using these chips for years.  Also visit avrfreeks.com Great forums!

kyuusaku

If you want to go the logic route, it's still pretty freaking easy. You need:

-74HC04
-74XX161
-74XX595

-use 3 inverters to create an RC oscillator, buffer it with a fourth -- you now have the clock signal
-clock the controller and counter with the clock signal
-connect the counter's Q3 to the controller's load input
-connect the controller's data output to the 594's serial input
-use the 5th inverter to invert the clock, feed it to the 594's serial clock
-use the 6th inverter to invert the load signal, feed it to the 594's output register clock

Since it takes 16 states to sample the pad and pad is typically polled 60 times per second and you want 2+x oversampling, better set the oscillator above 2 kHz.

knohbody

Ok, I've been working on this on and off since my last post. I got an ATTINY2313 to play with, and the first order of business was trying it out with LED output projects, like everyone else   ;D. I got that working fine on the ATTINY, and set to work making a little program to run a series of lights in a certain order when i pressed a button. So far so good. I then set out to make dimensionxor's code work on it, and started running into problems.

Obviously, I had to change some of the settings for the pins, as the ATTINY doesn't have the same pins as the larger chips.


#define F_CPU    8000000UL
#include <avr/io.h>
#include <util/delay.h>

#define NES_CLOCK PA0
#define NES_LATCH PA1
#define NES_DATA PA2
#define NES_DATA_PIN PINA
#define NES_PORT PORTA
#define NES_DDR DDRA

// NES controller read function, returns one byte containing 8 button states

uint8_t nes_read(void){
uint8_t nes_byte = 0xFF;
NES_PORT |= _BV(NES_LATCH); //turns the latch on
_delay_us(12);
NES_PORT &= ~_BV(NES_LATCH); //turns the latch off
_delay_us(6);
for(uint8_t bit_counter = 0; bit_counter < 8; bit_counter++){
(NES_DATA_PIN & _BV(NES_DATA)) ? (nes_byte |= _BV(bit_counter)) : (nes_byte &= ~_BV(bit_counter));
NES_PORT |= _BV(NES_CLOCK); // turns the clock on
_delay_us(6);
NES_PORT &= ~_BV(NES_CLOCK); // turns the clock off
_delay_us(6);
}
return(nes_byte);
}

int main(void){
DDRB = 0xFF; // all pins on PORTB set to output
NES_DDR |= _BV(NES_LATCH) | _BV(NES_CLOCK); // latch and clock pins set to output
NES_PORT |= _BV(NES_DATA); // enable pull-up resistor on NES data pin
uint8_t nes_data = 0xFF;

while(1){ // infinite loop
nes_data = nes_read(); // read states of 8 buttons from NES controller
PORTB = nes_data; // output NES button states on 8 pins of PORTB
_delay_ms(16); // delay for roughly 60Hz controller polling
}
return(0);
}


With this code, I'm getting the same results as my first attempt, i.e. the A button is the only one registered, and it turns all output ports low. I think the problem is in
(NES_DATA_PIN & _BV(NES_DATA)) ? (nes_byte |= _BV(bit_counter)) : (nes_byte &= ~_BV(bit_counter));
,which seems to read the nes data line, and store on/off bits into the nes_byte variable, but I am too inexperienced with bit operations to make out exactly what's wrong.

Any help is appreciated.
Thanks,
-James

ulao

#9
QuoteWith this code, I'm getting the same results as my first attempt, i.e. the A button is the only one registered, and it turns all output ports low. I think the problem is in
The A is the 8th data read so it would seem your code is over writing the bits..

I really can help much from what you said but maybe I can help you help your self.

I mean no offense here just trying to help

(NES_DATA_PIN & _BV(NES_DATA)) ? (nes_byte |= _BV(bit_counter)) : (nes_byte &= ~_BV(bit_counter));

first do the and (NES_DATA_PIN & _BV(NES_DATA) This checks the status of pinA, and checks to see if its high or low at that time in the cycle.
If ( aka the '?' ) hi then  (nes_byte |= _BV(bit_counter))
otherwise (nes_byte &= ~_BV(bit_counter))

for the case of  (nes_byte |= _BV(bit_counter)) your result is to bring the nes_byte BIT hi and leave the other bits alone.

for the case of (nes_byte &= ~_BV(bit_counter))  your result is to bring the nes_byte BIT low and leave the other bits alone.

than you just read the other bits one after the other and preform this same operation.

This is very basic but I know we all have to start somewhere..

To answer your question your code is perfect for the most part ( see notes ) .. I think the problem is deeper.  Maybe the clock is not working, are you sure your pins changes are right.. is it clocking the nes correctly? Do you have a scope or analyzer?




   NES_PORT |= _BV(NES_LATCH); //turns the latch on
   _delay_us(12); //nes nees 12 us to do this right
   NES_PORT &= ~_BV(NES_LATCH); //turns the latch off
   _delay_us(6);//not sure why this is here, its not needed, however I think its fine to keep it here..
   for(uint8_t bit_counter = 0; bit_counter < 8; bit_counter++){//  ok lets get 8 bits ( a byte ) of data
      (NES_DATA_PIN & _BV(NES_DATA)) ? (nes_byte |= _BV(bit_counter)) : (nes_byte &= ~_BV(bit_counter));//was it low or hi ? store the result in nes_byte
      NES_PORT |= _BV(NES_CLOCK); // turns the clock on
      _delay_us(6);//nes need 6 us here
      NES_PORT &= ~_BV(NES_CLOCK); // turns the clock off
      _delay_us(6);//nes need 6 us here
   }

 The best way to beat this is with a logical analyzer. Like this http://www.saleae.com/logic/

If you wish to go at this blind start by reducing the loop to 1 [for(uint8_t bit_counter = 0; bit_counter < 1; bit_counter++){] and check nes_byte, it should default to all 11111111.Test this by not pressing any buttons...  If you do this while B is pressed you should get 01111111 If this does not happen  your wiring or clock is not working...

dimensionxor

#10
I agree with Ulao, this sounds like a timing/clock issue. I'm not sure if this is responsible for the problem, but one other important note about the code above is that if your AVR is clocked at anything other than 8MHz you will need to change the top "#define F_CPU    8000000UL" line accordingly. This defines the clock frequency for the compiler so it can set the delays for an appropriate number of cycles. For a 1MHz clock it should read "#define F_CPU    1000000UL".  AVRs are clocked at 1MHz by default and can be configured for different clock sources and frequencies by setting the fuse bits. I have only used this code on an AVR clocked at 8MHz, but my guess is that it should work fine at 1MHz with the #define F_CPU set accordingly.

Thanks for commenting everything Ulao, you are right about that second delay, I tend to insert a lot of brief delays as an initial precaution and then comment them out once everything is working. I think I left that one in because it made everything look more symmetrical on the logic analyzer (or some other equally stupid reason).

This is a waveform view of an AVR reading an NES controller using this code,  the B and Start buttons are being held down on the NES controller.

ulao

QuoteAVR is clocked at anything other than 8MHz you will need to change the top "#define F_CPU    8000000UL" line accordingly
I good god way didn'y I not think of that. LOL bet that is it..

QuoteThanks for commenting everything Ulao, you are right about that second delay, I tend to insert a lot of brief delays as an initial precaution when writing new code and then comment them out once everything is working. I think I left that one in because it made everything look more symmetrical on the logic analyzer.
Ah cool, hey something of interest...

this works.. and the clock func's have no waits ( other then the jump to do the code of course) . I found this out when I was looking for ways to shrink my code. Turns out the (s)nes really dont need the waits at all. but its best to keep them in.. Sorry for off topic..


LATCH_HIGH();LATCH_LOW();
for (i=0; i<8; i++)
{
CLOCK_LOW();
tmp <<= 1;
if (!GET_DATA()) { tmp |= 1; }
CLOCK_HIGH();
}

knohbody

Thanks to both ulao and dimensionxor. I found that the problem was that I was using a DAPA (parallel port) programming cable (because I'm cheap! ;D) and that I set the NES data pin to PA2, which is the reset pin for the ATTINY2313, and cannot be set to an I/O reliably with a DAPA cable. I set the data pin to PD1 and it solved the problem, and the code works perfectly.

Thanks again for all the help.

-James

ulao

ahhh.. nice find.. Glad you got it working.

BOC

Quote from: knohbody on October 19, 2009, 12:38:51 AM
Ok guys, I'm trying to build a slightly modified version of this :

NES adapter


Hi

I've been looking through Google for a few hours now but can't find any complete diagram for this circuit, does anyone have this link saved perhaps ?

(I don't want to use any programmable device like a PIC ).

Regards