SNES Controller Detection

Started by Waterbury, March 03, 2010, 09:36:42 AM

Previous topic - Next topic

Waterbury

Does anybody know of a foolproof way of detecting if an SNES controller is connected to a circuit? I'd like to minimize triggering false detected button presses due to plugging in a controller halfway though my serial protocol code.

NFG

Just off the top of my head, what about running a detect loop between regular polling?  If you quickly cycle through the detection on that port and get nothing back, you can ignore that port for the next polling cycle.  You could run the detection every second or so...

Depends on your circuit too.  If your circuit holds the clock line until it's completed, the controller won't be sending any signals back to the circuit on the serial line, right?  Then you can run the detection, and turn the clock line back on only when there's a connected pad.

If you do it quickly enough you should be able to run this detection between regular polling of any other pads.

Waterbury

Quote from: Lawrence on March 03, 2010, 09:52:02 AM
 If your circuit holds the clock line until it's completed, the controller won't be sending any signals back to the circuit on the serial line, right?  Then you can run the detection, and turn the clock line back on only when there's a connected pad.

That's what has me confused though. How do I know when a controller is reconnected? Does the data line get held high when first plugged in?

NFG

It's not held high, so much as...  Hrm...  If your internal logic is holding the line high, then yeah, you won't be able to detect it.  Logic failure on my part, clearly.  Possibly the only way to tell if there's a controller connected is to watch for buttons pulled LOW?

Waterbury

I know that there are 4 unused "buttons" in the bit shifting I am going to try and manipulate. I was seeing if there was a more legit, official way of implementing a controller check, because I know the SNES has a way of doing things. Looks like I'm going to have to figure it out myself, because all the SNES controller tech sheets on the 'net don't reference detection.  ;D

I'm probably going to implement a weak pulldown of the data line. If those 4 button registers are low, I'll take it as the controller is unplugged. I'll share my findings.

I bought a 40 pin microcontroller for my Saturn to 360 project. I'm just going to implement SNES/NES/Saturn reading all at once, so you could reach for another type of controller based on what kind of game you are playing. Just trying to get all my chips together first. 

dimensionxor

If you pull-up the data line you shouldn't have to worry about false button detections while the controller is being connected. This way the button data will always read 0xFFFF (even when a controller is not connected) unless a button on the controller is pressed.

ulao

#6
Ahh sorry I was late, here ya go.


//(s)nes
testData=0;
LATCH_HIGH();_delay_us(12);LATCH_LOW();
for (char i=0; i<32; i++)  
{
CLOCK_LOW();_delay_us(6); if ( !GET_DATA() ) { testData++; } CLOCK_HIGH();_delay_ms(6);
}

if (testData==24 || testData==16) {  gamePadType = 2;  return 2; }
if (testData==18 )  { gamePadType = 4;  return 2; }
gamePadType = 0; return 0;


gamePadType  of 4 is a virtual boy. nes and snes will be gamePadType  of 2. I think the 16 is snes and 24 is nes. If you need to see my defines let me know but I;m guessing you know the low hi bit( no pun intended )



I have a controller detection also to know when its unplugged ( note: You may notice I dont use delays here.  Not relevant , I just did that to free up space. as they are not really needed. )

//sample data. 8 time first!!
uint8_t i;
LATCH_HIGH();LATCH_LOW();
for (i=0; i<8; i++)
{
CLOCK_LOW();
tmp <<= 1;
if (!GET_DATA()) { tmp |= 1; }
CLOCK_HIGH();
}


if ( tmp  == 0xff )  // now was it all hi?
{ //pause - controller was unplugged
reportBuffer[0]=reportBuffer[1]=128;// center analogs
reportBuffer[6]=0x20; return; //clear buttons, all but start
}
If not go on to 8 more reads..



Waterbury

Quote from: dimensionxor on March 03, 2010, 01:30:41 PM
If you pull-up the data line you shouldn't have to worry about false button detections while the controller is being connected. This way the button data will always read 0xFFFF (even when a controller is not connected) unless a button on the controller is pressed.

I agree and disagree. Honestly, my test circuit I made for SNES controllers has no pull ups or pull downs, so I haven't tested this, but have experienced it with my Saturn code. A pull up would prevent a disconnected controller port's data line from floating and would prevent button presses when there is no controller, yes. However, what I am trying to prevent is when a controller is connected mid-execution of the clock/load cycle it may (very briefly) register some button presses and say un-pause the game or do some other undesirable stuff.

Granted, I need to look over my code some more, which I haven't worked on in a while. What I'd like is a surefire way for my code to find there is no controller and start the serial cycle once it is detected. Honestly, I was just throwing the general question out there, and do not have a specialized question just yet.  :P


I will dissect ulao's code when the weekend comes. I've been working in nothing but ASM for my project so higher level controller code confuses me after working with nothing but low level. Not that ASM is any easier...  >:(

ulao

#8
QuoteWhat I'd like is a surefire way for my code to find there is no controller and start the serial cycle once it is detected
Exhibit A.

MEGA comment version.
Quote//(s)nes
   testData=0;//init
   LATCH_HIGH();_delay_us(12);LATCH_LOW();//drop the latch pin low, wait 12 us, bring it hi.
   for (char i=0; i<8; i++)  //loop 8 times
   {// drop the latch pin, wait 6 us, look at the data pin, if its Hi increment testData, bring it hi, wait 6 us.
      CLOCK_LOW();_delay_us(6); if ( !GET_DATA() ) { testData++; }   CLOCK_HIGH();_delay_ms(6);
   }
//if we get 8 then everything was hi or pressed, since that not possible the only conclusion is the controller is unpluggged.
   if (testData==8 ) { }
//why 8? u,d,l,r,a,b,sel,start.. this covers both snes and nes. You could do 16 but its not needed for the controller unplug test.

the logic here is that you get ad low bit from the Shift register when a button is pressed, and normally its high. No pull up is needed.




Waterbury

Thanks ulao, I will take your logic into consideration. I plan on using your Saturn digital pad recognition logic in my updated code as well.

ulao

#10
Hey no problem. I finally fished my project , huge support list too ;) I have unplug detection  logic for all controllers except atari like devices.

http://spreadsheets.google.com/pub?key=tGD-U_eW1Rc7rNyqkpgJuIg&output=html

Was one of my favorite projects to date.  Need any help feel free to pm.

dimensionxor

#11
I know what you mean about the saturn controller, I have run into that problem before as well, though I have not had this problem with NES/SNES controllers. With the data inputs pulled up you can simply discard bytes received containing only zeros to eliminate glitches. In AVR assembly it would be something like this:

SNES_READ_INIT:
ser temp_1 // set all bits in registers for storing button data
ser temp_2
ldi temp_4, 16 // bit counter register, 16 bits of data
sbi _SFR_IO_ADDR(SNES_PORT), SNES_LATCH // set the shift register latch
nop // wait a few cycles
nop
nop
nop
cbi _SFR_IO_ADDR(SNES_PORT), SNES_LATCH // clear the latch and begin clocking out bits

SNES_READ_BIT:
lsr temp_2 // shift the high byte right
ror temp_1 // rotate the low byte right through carry
in temp_3, _SFR_IO_ADDR(SNES_PIN) // read in state of snes data pin to temp reg
bst temp_3, SNES_DATA // store current snes data bit in sreg t flag
bld temp_2, 7 // load current snes data bit from t flag to msb of high byte
sbi _SFR_IO_ADDR(SNES_PORT), SNES_CLOCK // set the clock high
nop // wait a few cycles
nop
nop
nop
cbi _SFR_IO_ADDR(SNES_PORT), SNES_CLOCK // clear the clock
dec temp_4 // decrement the bit counter
brne SNES_READ_BIT // if bit counter is not zero, read another bit

tst temp_1 // error check, if byte is equal to zero disregard data
breq SLEEP // branch to sleep instruction and wait for next poll cycle
tst temp_2 // do the same for second byte
breq SLEEP

SEND_FIRST_BYTE:
lds temp_3, UCSR0A // read usart status reg into temp reg
sbrs temp_3, UDRE0 // check if usart data register empty flag is set
rjmp SEND_FIRST_BYTE // loop until flag is set and usart is ready to transmit byte
sts _SFR_IO_ADDR(UDR0), temp_1 // send first byte of snes button data

SEND_SECOND_BYTE:
lds temp_3, UCSR0A // read usart status reg into temp reg
sbrs temp_3, UDRE0 // check if usart data register empty flag is set
rjmp SEND_FIRST_BYTE // loop until flag is set and usart is ready to transmit byte
sts _SFR_IO_ADDR(UDR0), temp_2 // send second byte of snes button data

Waterbury

I've decided. I'm going to use a pull down resistor for SNES controllers and check bits 13-16. If low, I will take that no controller is connected. For NES controllers, I'm going to use a pull up resistor and might not even bother with the code.

NFG

Will that work?  The SNES controller goes HighZ (ie: floats/disconnects/returns nothing at all) when no button is pressed.  When a button is pressed, it basically connects the console line to ground, which changes the held-high line to LOW.

If you're going to hold them low, and the controller is either <disconnected> or <LOW> then...  you'll never see anything but low.

TBH I don't know why it's a bother for you - the original console didn't prevent you from mashing buttons while plugging in the pad.  You say the SNES does, but I've never noticed it.   

Waterbury

Quote from: Lawrence on March 15, 2010, 10:13:20 AM
Will that work?  The SNES controller goes HighZ (ie: floats/disconnects/returns nothing at all) when no button is pressed.  When a button is pressed, it basically connects the console line to ground, which changes the held-high line to LOW.

If you're going to hold them low, and the controller is either <disconnected> or <LOW> then...  you'll never see anything but low.

TBH I don't know why it's a bother for you - the original console didn't prevent you from mashing buttons while plugging in the pad.  You say the SNES does, but I've never noticed it.   

Can anyone else confirm this? The reason why I want to drive the lines low is so I could have a LED status for controllers plugged in and disconnected.

I have 10K Ohm resistors driving the data input for the SNES data of my microcontroller low, and its working. Maybe the fact that it's working is due to luck and the controller may be working out of spec?

If I remember correctly, Super Mario Kart detects when a 2nd player controller is unplugged so the SNES must have a way of detecting?

Waterbury

Great, I just googled "SNES Controller Detection" and this page comes up as the first result. Fun, looks like Google can't answer everything. Or maybe Google assumes that people of this forum are video game gods?  ;D

dimensionxor

Looking at it on a logic analyzer, the nes/snes data output doesn't appear to be open collector. The controller will ground the data line when it is connected even if it is pulled up by the AVR. After a rising edge on the latch, the data line will be set high and remain high through 16 rising clock edges (excluding any button presses), and then return low following the rising edge of the 16th clock cycle.  There is a logic analyzer view here which shows this.

Pulling the data line up or down seems to only determine what the logic state of the data input pin on the AVR will be when no controller is connected. Another simple way for detecting the controller might be to pull the data line up and then check the data line before the rising edge of the latch. If low, you know a controller must be present pulling the line down, if high, no controller is connected and the AVR is pulling the line up.

Waterbury

#17
Quote from: dimensionxor on March 17, 2010, 12:43:30 PM
Another simple way for detecting the controller might be to pull the data line up and then check the data line before the rising edge of the latch. If low, you know a controller must be present pulling the line down, if high, no controller is connected and the AVR is pulling the line up.

I tried a pull up resister while reading the data line last night. I kept reading the data just prior to the rising edge of the latch. Unfortunately the data line would always read high here, regardless if there was a controller or not.

I was using a 10K Ohm resistor as my pull up. I considered that maybe the pull up was too strong, but the controller worked fine otherwise. It appears that the controller would sink button presses fine. I did not try using a higher resistance resistor. It is possible that my code isn't properly coded right, keeping my data line normally high, but I'm not seeing it, unless if I'm missing something. I did have to run back and forth to keep re-programming the chip as my original code had ALOT of bugs, it's amazing the code worked at all before. I think it's good now though.

What I finally did, that works, is I'm using a pull down resistor. On bits 13-16 I check to see if they are all high. If so I take the controller as being connected, else I assume that there is no controller.

In this guide, http://www.repairfaq.org/REPAIR/F_SNES.html by Jim Christy it is mentioned that Donkey Kong Country uses the last 4 bits to determine if a controller is connected. If that's true, then the only controllers that shouldn't work with my code are controllers not compatible with one of the best selling SNES games, I'll take those odds. If not, I could always simply change the code later.

That seems to be the trick though. Hold the data line low, and check bits 13-16. If high, you have a controller connected. Haven't tested this with anything except standard game pads though.

dimensionxor

#18
Glad to see you got it working, if that was the method used for detection in official games then it is probably the safest bet. Thanks for the link too, some useful info there. His timing diagram shows the data line should be normally low and the clock line normally high. Having never observed controller polling on a real snes, I have been keeping the clock normally low, though it seems to work fine just the same.

I also decided to try checking for controller connection status using the state of data before the rising edge of latch (just to see if it would work) by adding these lines immediately before calling the snes controller read function.


in temp_1, _SFR_IO_ADDR(SNES_PIN) // read in state of snes input to temp reg
com temp_1 // invert bits so LED will be off when controller is not connected
bst temp_1, SNES_DATA // store inverted state of snes data line in sreg t flag
in temp_1, _SFR_IO_ADDR(SNES_PORT) // read in copy of port state to temp reg
bld temp_1, SNES_CON // load inverted state of snes data line bit into led bit in copy of port state
out _SFR_IO_ADDR(SNES_PORT), temp_1 // write modified copy of port state back to port register

rcall SNES_READ_ALL // call function to read two bytes of button data from snes pad


This worked just as expected, when the controller is connected the data line is grounded and the inverted state bit turns the LED on,  when the controller is disconnected the AVR pulls the line up and the inverted bit turns the LED off. The pull-up resistor used in this case is the 10k internal pull-up within the AVR. The controller I am using for testing is the off-brand InterAct superpad controller, which may or may not make a difference. I can test with an official Nintendo controller tomorrow when I can get hold of one.

Waterbury

Quote from: Waterbury on March 18, 2010, 09:17:14 AM
That seems to be the trick though. Hold the data line low, and check bits 13-16. If high, you have a controller connected. Haven't tested this with anything except standard game pads though.

When the controller has been connected after being disconnected using this method it may be a good idea to implement a 500ms delay and re-sample the port. Even though bits 13-16 may be stable and high, there may be a few stray false button presses in the same latch.

micro

Maybe this isn't relevant to you anymore but to detect if a SNES controller is plugged in you don't need a pull-down resistor. Just read the 17th bit, it should be low if a controller is plugged in :)

SNES games may read beyond the 16th bit and DKC does so. And if bit no. 17 isn't 0 it assumes theres no pad plugged in.
DIrt Trax FX even gives you an error message on start up.

On the other hand, Super Street Fighter 2 Turbo doesn't give a shit about bit no.17. It doesn't even matter to it if bit 13-16 are 1111 or not.

ulao



ulao

the entire point leading up to the 8th post was that you can just receive the two  bytes and the last 4 are always the same. As indicated in the 8th post you dont need a resistor at all.

micro

Maybe I'm too stupid but I have to admit that I don't quite understand your code and the comments in reply no. 8  ;D
You can't tell the difference between a connected and an unconnected SNES pad just by looking at UDLR, can you? It's all 1's... Just as the last 4 bits of the 16 data bits are always 1's.

Recently I wanted to make a microcontroller to act as a SNES pad and I ran into troubles with some games.  It's all because of the 17th bit. The SNES software may read the 17th bit and if it isn't 0, some games just won't work (Super Gameboy, example).
Also, the SNES Dev Manual clearly states that if a controller is connected then the 4 signature bits are 1111 and the 17th bit=0. If no controller is connected, the signature is 1111 and the 17th bit = 1, too.

ulao

Well I dont have pull up and all I need to look at is the udlr. I may have my hi / low backwards but when its unplugged I see all four ( udlr) begin pressed.  And a bit above that post I show some code that proves what what you found to be true, the tail end 4 bytes. My point being, it was already mentioned that you can detect a snes via the protocol.

micro

Quote from: ulao on February 10, 2011, 03:48:48 AM
Well I dont have pull up and all I need to look at is the udlr. I may have my hi / low backwards but when its unplugged I see all four ( udlr) begin pressed. 
Maybe you're just lucky. I mean you got a SNES data input pin on your microcontroller and you don't use a pull-up resistor? So the data you're getting could be random.

It's safe to use a pull-up resistor on the SNES dataline. The real SNES console also uses a 10k resistor between data and vcc to hold data high when there's no controller plugged in.

My point is that you need to look at the 17th bit. It's the way the SNES handles controller detection. And that hasn't been said in this thread yet so that should be a valuable information. Sorry, I don't want to sound like an asshole but that's just the way it is. :)

ulao

#27
I dont think you being an ass at all?

It cant be luck, many people use my adapter. I have 10 snes controller to test with. I know its safe to do so, but its also not needed.  

Well look at the first code block ( post 7 ) think about it. It runs 32 cycles. Then you count the hi bits.  It works for all nintendo pads.


For example the snes.

32 runs
-12 bits of data plus 4 are always hi.    ( u,d,l,r,sel,st,x,y,a,b,l,r,hi,hi,hi,hi)
----
16

This is how I detect what is plugged in, so its very clear how you would check if its unplugged. My point is you can use the 16,15,14, or 13.  ( this is counting the first bit as 1 ) Granted you dont need the extra 16 runs for just snes detection.

In conclusion.:

I'm not trying to be an ass either.
I'm not saying you wrong
I'm only pointed out it was talked about
Its not the 17th bit, its the last 4. ( but yeah you could just use one bit )
Only arguing for fun here. I think the horse has been beet enough.




micro

QuoteIt cant be luck, many people use my adapter. I have 10 snes controller to test with.
I really don't doubt your adapter and your pad detection work. But if you got an input pin that's not connected to anything and you don't use a pull-up then you got a floating pin. And when you read that pin the result could be 1 or 0, that's luck then.

I guess I understand what you are doing. You read 32 bits and depending on the sum of 0's you decide which controller is connected or if no controller is connected. So 16 0's mean SNES controller, 24 0's mean NES controller, 18 0's meas Virtual Boy and anything else means that there's no controller connected, right? :)
I really believe that this is working, but again: If you read a floating pin 32 times, chances are that you read 16 or 24 0's at some point...


Quote from: Waterbury
I was seeing if there was a more legit, official way of implementing a controller check, because I know the SNES has a way of doing things. Looks like I'm going to have to figure it out myself, because all the SNES controller tech sheets on the 'net don't reference detection.
Just to answer Waterbury: The SNES got a 10k pull-up resistor between VCC and data (at least my SNES consoles).  If you got access to the SNES Dev Manual Book 2, read page 4-9-12 & 4-9-13. It describes how controller detection is handled by interpreting bits 13-16 & bit 17. It doesn't get more legit ;)

ulao

#29
QuoteI really don't doubt your adapter and your pad detection work.
LOL ok now your just being an ass. I have over 500 people using it, the odds are against you.

Quoteif you got an input pin that's not connected to anything and you don't use a pull-up then you got a floating pin. And when you read that pin the result could be 1 or 0, that's luck then.
Most micros have enough competences to set out, set the condition , set in.  I think it largely depends on what ports are in use, temperature and the board. Either I dont use that and it works.

Would your argument above not be true in your own detection? You also are checking on a bit to be hi when the controller is not plugged in, so that same would apply no? The only difference is you check one bit where I check many. Unless your looping and waiting for it to eventually change.

Example:
ctrl:  xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
------------------------------------------------------------------
snes : dddddddd dddd1111 (16) are low
-nes : dddddddd xxxxxxxx ( 24 ) are low
--vb : dddddddd dddd11xx( 18) are low
none:aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa ( a range from 0-32 low )

Now with the controller attached you will get a 16, 24, or 18. If the controller is removed you could get 0-32.  In my case, if this occurs the logic is tested again when the controller is in used ( pause detection ).  Mean when reading a controller I check to make sure its plugged in.  If its unplugged I run the detection again. So it will always catch a false detection.  I could always extend the 32 to 255 for higher compatibility but why, as I said it works ;)





kendrick

Everybody please refrain from personal attacks. I appreciate that everyone's been open to different viewpoints so far. With that said, I have two reminders for everyone:

1) "It worked for me, I don't know why it doesn't work for you" is not a logical defense.

2) If you're not willing to try something yourself, you get to be critical or skeptical exactly once.

You guys are usually great and I don't have to play the moderator card often. Let's keep it that way, please.

micro

Quote from: ulao on February 16, 2011, 04:14:34 AM
QuoteI really don't doubt your adapter and your pad detection work.
LOL ok now your just being an ass. I have over 500 people using it, the odds are against you.

No no, I  believe ulao just got me wrong. I said I don't doubt his pad detection works meaning I really believe that the detection works.  :D


QuoteMost micros have enough competences to set out, set the condition , set in.  I think it largely depends on what ports are in use, temperature and the board. Either I dont use that and it works.
Hmm, I've tried to read a floating pin and switched a LED on or off depending on the result. The LED wasn't flickering the whole time but it changed as I touched the PCB or changed its position. It was also more flaky when using a 5V PSP PSU instead of a 3V CR2032 battery.

QuoteWould your argument above not be true in your own detection? You also are checking on a bit to be hi when the controller is not plugged in, so that same would apply no? The only difference is you check one bit where I check many. Unless your looping and waiting for it to eventually change.
Right now I'm not detecting SNES pads at all ;) It was the other way around. I wanted to make the SNES accept my AVR as a legit controller. But the difference would be that with a pull-up resistor a single 0 in bit 17 would be enough, because that would mean a pad is plugged in for sure.


QuoteExample:
ctrl:  xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
------------------------------------------------------------------
snes : dddddddd dddd1111 (16) are low
-nes : dddddddd xxxxxxxx ( 24 ) are low
--vb : dddddddd dddd11xx( 18) are low
none:aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa ( a range from 0-32 low )

Now with the controller attached you will get a 16, 24, or 18. If the controller is removed you could get 0-32.  In my case, if this occurs the logic is tested again when the controller is in used ( pause detection ).  Mean when reading a controller I check to make sure its plugged in.  If its unplugged I run the detection again. So it will always catch a false detection.  I could always extend the 32 to 255 for higher compatibility but why, as I said it works Wink
Yes, and as I said I really think this is working. But I also think that it's possible that you might get 16, 24 or 18 0's when there's no controller plugged in because of the missing pull-up. You said "If the controller is removed you could get 0-32." But if you're running other tests after that to verify if the pad is really plugged in or not, then that's fine.

Right now I think everything has been said :)




ulao