PSX Analog in and N64 Controller

Started by kylejw, February 13, 2009, 06:43:31 PM

Previous topic - Next topic

kylejw

Just signed up here, being a more technical forum than most I figured some of you may be interested in a project I've been working on.
I'm sick of all my N64 controllers grinding away their joysticks so I decided to try and work out a permanent fix.  The PSX joystick is the stick of choice because I found a used controller at EB Games for $10 and I've always liked the feel of the PSX stick.  Xbox's analog would be easy to adapt to what I've done as long as it's stick isn't for some reason really massive to the point that it won't fit in the N64 controller case.

I toyed with a few ideas that I think could all work.

1.    Physically attaching the N64 quadrature encoding wheels to the PSX stick.  It's tough to explain, but the two sticks have similar dimensions ' so similar that I think this is possible.  The PSX even has 'nubs' that stick out of the potentiometers that rotate with the stick.

Why didn't I do this?  Well frankly I think it would be very difficult to align everything perfectly, and you may lose some range of motion after all the effort in retrofitting.  Not worth the hassle to me.

2.   Replacing the current chip.  The current chip is pretty big, and PICs today are available that are as good and better than this chip while being significantly smaller, so there would be room to solder in some jumpers.  Unfortunately it has something like 84 pins (can't remember exactly, but it's a lot).  Also, I hate troubleshooting interrupts as I don't really have much in terms of equipment at my disposal.  The other issue with this method is the controller memory card/rumble pack port.  I don't know how the signals coming out of this work and I'd need to redo the code for that myself.  No fun.

3.   Converting the PSX potentiometer's voltage levels to mimic the N64's quadrature encoded signal.

This is what I did.  The PIC I used has a bunch of ADC's so it was easy to read in the voltage, all I had to do was determine when the stick had moved a specific amount, then send a pulse on two lines separated by a quarter wavelength.  Google quadrature encoding if you're curious/confused about this part.  In fact, the code I'm using was initially just to see if I could do this, but it ended up working so well that I've just kept it.  I'm still leaning towards completely redoing it to give me more control over stick sensitivity.

4.   Last but definitely not least, I considered intercepting the controller's output data and patching the 'stick position' bits to the position my ADC's read from the PSX analog.

Why didn't I do this?  To be honest I didn't think of it until I had already started the method in 3.  Otherwise, I probably would have.  Maybe better I didn't though as there is a timing aspect that I'd have to adhere to with this; and my oscilloscope is just the line-in port on my sound card.  Not exactly state-of-the-art.  It is very inaccurate and only works for a very limited frequency range (the same range that the human ear can hear).

Now onto the good part'


kylejw

First up, the test rig:


After I got everything figured out, I cracked the controller I wanted to use open


Using some 60 grit sand paper I chewed through the original thumbstick case.  Once upon a time I had a Dremel that would have made this part cake, but sadly it's broken.


I ferric chlorided myself a nice little PCB that I didn't end up using, so it was pretty much a big waste of time.


New idea was to retrofit the stick into the N64 module, so the Z-button mounts properly


The new stick components


Cut up the back so I can route wires through it.  The white line is whiteout that I put on the ridges of the controller case, then mounted the thumbstick, so I could see where the ridges ended up and place my microcontroller accordingly.


Inside view of the stick case, I broke off some little nubs that held the original stick hardware in place


New thumbstick module taking shape, the glue around the cover is kind of sloppy due to some careless sanding, but not much I could do at this point as I planned to make two n64 controllers from one PSX


PIC attached to the back of the case, Normally I wouldn't use hot glue on electronics like this as I have read some bad things about it possibly conducting electricity a tinnnny amount (can anyone confirm?), but it's all I had :)


Routing the wires needed for programming the chip


Rest of the wired soldered on, and covered with hot glue like an amateur.  Unfortunately the only suitable wire I had is 30 AWG solid core Kynar, and I hate this stuff.  It ALWAYS breaks on me so I just covered it with glue.  Notice I had to move the chip over to the right side so it's by the original plug.  Oops.


Another view of the completed circuit


In the above picture, note the two blue wires going across the width of the thumbstick module.  They go over my white line, so I need to remove part of the plastic which may cut them when I put the controller back together



Finished product.  Notice how sloppy the glue around the PSX border is.  Ugh.  I promise the other controller won't be as bad.


Programmer doing its thang


And that's it.  I will have a Youtube video for you if it ever finishes uploading.  I can't go back to the old joystick now.  It really was the weakness of the N64 controller.  Once I get the code down it will be perfect, but as of right now it is completely playable.  I'm willing to bet most people would be happy with it but I'm not.

kylejw

#2
Video finally uploaded, it's not the greatest though.

Playing one-handed, and I wasn't paying attention and the controller drifted out of view near the end.  Also would have made the video longer but my memory card is missing, so I had to make due with my old 256MB card.

http://www.youtube.com/watch?v=QFbFlmr-h2Q


NFG

That is seriously cool!  =D

Will you be making the PIC code available?

ken_cinder

Cool, good job, but I personally hate the Playstation analog sticks. They're far too light with too little resistance for precise control. I like sticks that require more force to move.
This is surely better than the N64 stick once it's worn though, they get pretty loose when worn.

kylejw

Quote from: Lawrence on February 13, 2009, 10:48:42 PM
That is seriously cool!  =D

Will you be making the PIC code available?

I don't really mind posting the code.  Just right now it isn't very well written, at the very least I'd have to clean it all up so it's readable for someone who isn't me :p
But at the same time I'm thinking a rewrite is in order, so maybe in the future I can release something.


Quote from: IJTF_Cinder on February 14, 2009, 04:40:32 AM
Cool, good job, but I personally hate the Playstation analog sticks. They're far too light with too little resistance for precise control. I like sticks that require more force to move.
This is surely better than the N64 stick once it's worn though, they get pretty loose when worn.

I don't mind a loose stick.  In the video check out how much I flick my thumb back and forth.  That's just the way I use analog sticks, no smooth motions for these thumbs haha.  Easily movable sticks makes it easier to do my hyper flick thing.  The problem I have with loose N64 sticks though is when they get loose they lose some of their range of motion.

But I do see your point.  An Xbox analog would work with this too if someone were to fit one, since they use potentiometers too.  I'm also looking at using a GC stick.

kendrick

Welcome to GamesX, Kyle. This is excellent work, and you've made a really positive first impression. You may now get away with saying things carelessly. :)

About using a GC stick? If you're going to put in a traditional dual-pot analog stick anyway, I'm thinking the GC stick is the best option. That octagonal gate is something that's unique to Nintendo hardware, and there are some games where it's absolutely necessary to have a guide as to where the 'absolute' up position is.

jk399

This is fantastic, I have in the past, re-cased 3rd party controllers in the original shell to swap analogue stick as most 3rd party pads are pot-based.

But the 3rd party pads have issues with the rumble pak and memory pak corruption etc. it would be great to use a gc joystick with the original n64 controller.

I would love to see a release of the code/schematic.

micro

I'm new to this forum :)

I've done exactly the same mod about a year ago. Back then I've also utilized a PSX analog stick.
Yesterday I've finished another one but this time I used the analog stick and the octagonal restrictor plate of a 3rd party Gamecube controller.

I've decided to publish my experiences with this mod as well as the source code for the MCU I've written on this forum.

The victim:

I didn't have a guilty conscience about cannibalizing a bigben controller :D

The finished N64 controller:

You can actually see the MCU in the left handle. I'm very pleased with the look and (of course) the feeling of the new stick. It's really perfect.
The hardest part by far was to perfectly fit the stick and the octagonal plate. Really! If you look at the original N64 stick from the side you'll notice that the stick is mounted in a really weird way, the lower end of the gray frame sticks out far.
So take your time  mounting the stick & plate and use lots of hot glue. If it's not perfectly fit you can always use a hot air blower to heat the hot glue again (but don't melt the plastic of the controller) and then correct the angle of the stick.

As mentioned before on some threads of this forum, the original N64 analog stick consists of two wheels and a rotary encoder. If you want to know what this means and how it works you should have a look at the wikipedia entry: http://en.wikipedia.org/wiki/Rotary_encoder

Inside the N64 controller there is a connector that provides us with everything we need:


I didn't use a PIC like kylejw but instead an Atmel AVR MCU. Unfortunately the only one I had available with analog-to-digital converters was an ATMega8. Having 28 pins it's a little bit over the top but there's plenty of room in the controller housing so it's not really a problem.

I drew this little picture of how to wire the ATMega8:

As you can see there are only 2 inputs from the pots of the new PSX-/GC-style analog stick as well 4 outputs (XA, XB, YA, YB) that go straight into the connector on the N64 controller PCB.

For writing MCU code I'm using Bascom AVR IDE, you can get it for free at http://www.mcselec.com/ (demo version limited to 4k of code which is MORE THAN ENOUGH for projects like this)
If your PC has a parallel printer port you can get a suitable programmer very cheap or even build one youself.
My code probably has much room for improvement and tweaking but it works great. Also, I've translated my comments into english, I hope they're understandable yet :D

$regfile = "m8def.dat"

'8MHz = highest internal frequency of the ATMega8
$crystal = 8000000

'configuring the ADC, its resolution is 10bit per channel
Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc

'pins for the output of the X-axis
Config Portb.1 = Output
Config Portb.2 = Output

'pins for the output of the y-axis
Config Portb.3 = Output
Config Portb.4 = Output

'variables in which the 10bit value of the ADC will be stored
Dim X As Integer , Y As Integer

'init X axis. the value is divided by 2 because otherwise it's too big
X = Getadc(1)
Shift X , Right , 2 , Signed

'init Y-axis
Y = Getadc(2)
Shift Y , Right , 2 , Signed

'variables to store the old X and Y values for comparison in the next round
Dim Oldx As Integer , Oldy As Integer
Oldx = X
Oldy = Y

'rotating the xwheel and ywheel bytes 1 step to the left or the right is the
'same as turning the optical wheels in the N64 stick 1 step clockwise or
'counter-clockwise.
'by fixating on two bits of these bytes (let's say xwheel.0 and xwheel.1) and
'then rotating the byte 1 step to the left or the right, you'll get the new
'gray code on the these two bits (e.g. xwheel.0=A , xwheel.1=B)
Dim Xwheel As Byte , Ywheel As Byte
Xwheel = &B11001100
Ywheel = &B11001100

Dim I As Byte

'in these two variables we're storing the number of steps we have to process for each axis
Dim Xsteps As Integer , Ysteps As Integer



'main loop:
'-------------
Do

'get and store X-value; resolution/2
X = Getadc(1)
Shift X , Right , 2 , Signed

'get and store X-value; resolution/2
Y = Getadc(2)
Shift Y , Right , 2 , Signed

'calculate the number of steps we have to process
Xsteps = X
Xsteps = Xsteps - Oldx
Ysteps = Y
Ysteps = Ysteps - Oldy


'stay in while-loop until all steps of the X- and Y-axis are processed
While Xsteps <> 0 Or Ysteps <> 0

   'steps of the x-axis
   If Xsteps > 0 Then
      Rotate Xwheel , Left , 1
      Xsteps = Xsteps - 1
   Elseif Xsteps < 0 Then
      Rotate Xwheel , Right , 1
      Xsteps = Xsteps + 1
   End If

   'steps of the y-axis
   If Ysteps > 0 Then
      Rotate Ywheel , Right , 1
      Ysteps = Ysteps - 1
   Elseif Ysteps < 0 Then
      Rotate Ywheel , Left , 1
      Ysteps = Ysteps + 1
   End If

   'write the new gray codes for both axis in I
   I.1 = Xwheel.1
   I.2 = Xwheel.2
   I.3 = Ywheel.1
   I.4 = Ywheel.2
   '...and then write I to port B
   Portb = I

   'we have to wait a little bit for processing the next steps because otherwise
   'it would be too fast and the IC in the N64 controller would skip some steps
   Waitus 10

Wend

'store the values of both axis for comparison in the next round
Oldx = X
Oldy = Y

Loop
End


After having compiled the code and flashed the ATMega8 don't forget to set the speed of the ATMega8 correctly in the fusebits settings, 8MHz internal RC.
I recommend using a socket for the MCU or even including a 10pin ISP connector inside the controller housing itself so you can re-flash the ATMega8 if something went wrong.

There are several ways how to connect VCC and GND to the pots of the new analog stick AND the stick can be mounted in 4 different angles (0°, 90°, 180°, 270°).
That means you could end up with switched X- and Y-axis and/or reversed X-axis and/or Y-axis. There are 2 ways to solve problems like that:

1.) Redo wiring, e.g. switch the VCC and GND connections on the pots.
2.) Simply switch some words in the code->

if X- and Y-axis are switched:
Go to line 22, 26, 56 & 60. Switch the ADC channel in brackets, e.g.  getadc(1) -> getadc(2) ; getadc(2) -> getadc(1)

if the X-axis or Y-axis is reversed:
Go to line 75 & 78 for the X-axis and line 84 & 87 for the Y-axis. Simply switch "right" for "left" and you're set

So go try it yourself and post your finished controllers :)


kendrick

Welcome to GamesX. That's an excellent write-up of an impressive mod. I'm personally very appreciative when new members are able to contribute in this fashion. I look forward to seeing more of your posts.


Link83

#11
Wow, only just noticed this thread  :o

kylejw and micro - thats really awesome work  ;D I especially love micro's GameCube stick in the N64 controller (No offense kylejw, you also did great work  :) I would just prefer to use the GameCube's analog stick to the PlayStation's - in fact would your chip/code work with the GameCube analog stick aswell?)

micro, I dont suppose you could give anymore details on how you did it? I have a USB programmer but have not used any Atmega chips before and am guessing I will need a different programmer for the Atmega8? What would you recommend for the cheapest/easiest to use? (I cant find much info online about the pros/cons of different programmers for Atmega chips)

Im also curious how you made the stick fit in the controller without being recessed - did you really just fill it up the base with hot glue? The stick appears to be right up against the octaganal gate, yet im guessing you used the original N64 analog stick base in order to hold the Z button? Would love to see any other pictures you may have of the insides of your controller.

Oh, and should this work using an official GameCube controllers Analog stick? (I have one here with a busted C-Stick)  Or are the potentiometer values different?

Thanks in advance  ;D

P.S. Cant believe how neatly you managed to cut out that octagonal gate!

l_oliveira

Link83, I can program ATMEGA8 chips with only wires tied to the PC parallel port.

ATMEL has a great design for their chips with ISP (In System Programming) in mind.

So the design only requires 3 data pins and the reset signal to be connected from the MCU to the PC parallel port. You feed it also GND and 5v and "voila", you have an programmer.

I recommend "Pony PROG 2000" as programming software (http://ponyprog.sourceforge.net).
Can't get cheaper than free, right ?

Jon8RFC

I'd love to learn how to go about swapping a gamecube stick into my N64 controllers.  The part about programming a chip is confusing to me, but if there's a straight-forward way to just go through what needs to be done without customizing each chip that would be great.  I've been hoping to get a few thousand CNC machined replacement sticks made from a more durable and lower-friction polymer, but it's not been easy to get all the details figured out...a DIY gamecube stick swap would be easier!

Link83

#14
Quote from: l_oliveira on June 20, 2009, 02:29:37 AM
Link83, I can program ATMEGA8 chips with only wires tied to the PC parallel port.

ATMEL has a great design for their chips with ISP (In System Programming) in mind.

So the design only requires 3 data pins and the reset signal to be connected from the MCU to the PC parallel port. You feed it also GND and 5v and "voila", you have an programmer.

I recommend "Pony PROG 2000" as programming software (http://ponyprog.sourceforge.net).
Can't get cheaper than free, right ?
Thanks l_oliveira, that sounds great  :) I will have to give it a go when I get a spare parallel cable I can hack up.

Quote from: Jon8RFC on June 26, 2009, 05:17:42 AM
I'd love to learn how to go about swapping a gamecube stick into my N64 controllers.  The part about programming a chip is confusing to me, but if there's a straight-forward way to just go through what needs to be done without customizing each chip that would be great.  I've been hoping to get a few thousand CNC machined replacement sticks made from a more durable and lower-friction polymer, but it's not been easy to get all the details figured out...a DIY gamecube stick swap would be easier!
Coincidentally I too had been looking into getting replacement parts made for the N64 Analog stick, but havent had much success  :( Did you manage to find out how much it would cost for a production run of CNC machined parts?

As im sure you know its not just the stick that wears down, but usually the white base piece and sometimes the black 'gear' pieces aswell. I have a brand new N64 controller here which I would consider taking apart to make perfect molds or scans if we could find a company who would make replica parts for a reasonable price.  I have found that a new N64 analog stick which is properly greased is quite smooth and is very nice to use - and would probably last forever if cared for correctly  ;)

I think almost all the problems with the N64 analog stick stem from the poor plastic 'concentric circles' grip on the top of the stick, if it had been 'rubberised' like the GameCube stick top we would probably have had much fewer problems. The original stick is made from quite a smooth plastic and when your thumb gets sweaty during a heated gaming session you have to put downward pressure on the stick just to maintain your grip. This has the unfortunate effect of digging the bottom end of the stick into the soft white plastic surface in the base and grinding it up - the ground up pieces then act like sandpaper and erode even more plastic, and its all downhill from there...eventually the stick doesnt even rest on the white base piece and ends up putting all the pressure on the black gear pieces instead, which then wears them out aswell  ::)

It would be fantastic if some Hong Kong company could take the information provided in this thread and build a pre-made replacement N64 analog stick unit using a copy of the GameCube's analog stick with a slightly redesigned casing, and of course the neccessary chip and connectors - although I think this is just wishful thinking on my part, and we will probably always have to rob analog sticks from other controllers and 'hack' them too fit  :-\

P.S. Does anybody know what the insides of the analog stick used in the clone N64 controllers look like?:-
http://cgi.ebay.com/new-white-Game-Controller-For-Nintendo-64-N64-system_W0QQitemZ370218800471
Its doesnt look to be the best mold  :P and im guessing that inside its redesigned and probably uses a standard potentiometer based analog stick, but i'm still curious...

public-pervert

#15
hi guys, im new to this forum too and what micro and kylejw did is my dream! i serious want to do this mod with my n64 controller.. ill love to see one step-by-step tutorial on how to do this. i do not understand how can i conect the atmega8 on my computer.. can someone answer? please :D

micro

Hi perv,

in order to program the ATMega8 you'll need a programming cable for your PC. But don't do the mod now. I've rewritten the code and I also use another microcontroller, it's much smaller now.

Just be patient, I'll explain & show everything in a few days.

public-pervert

serious!! wooow! please please, micro!! :D :D

by the way, yesterday i buy a psx controller just for " canniballize"  him. this one is the single controller i got for buy.  but i think the pots are the same one used in GC, correct? I'm anxious to do this *-*

since now, thank you very much! :D

Link83

#18
Quote from: micro on July 08, 2010, 03:15:56 AM
Hi perv,

in order to program the ATMega8 you'll need a programming cable for your PC. But don't do the mod now. I've rewritten the code and I also use another microcontroller, it's much smaller now.

Just be patient, I'll explain & show everything in a few days.
Hi micro, this sounds great :) I very much look forward to hearing more about it.

I just have two questions if you dont mind:-
- Does your code work with any analog stick, or do they have to use a certain pot value? (Really hoping to use a broken official GameCube controller)
- Is the sensitivity set to about the same level as the original N64 analog stick?

micro

Sorry I kept you guys waiting!

The discovery I made is that a PSX or Gamecube stick with my microcontroller code is TOO sensitive. It wasn't a problem in Super Mario 64 but in first person shooter like Turok or Perfect Dark it was just too fast when the stick was fully tilted.

So I took a N64 expansion cord and connecting the socket of that cord to a microcontroller I was able to read N64 Pads.
I borrowed 2 original unaltered N64 pads in very good condition, the sticks weren't worn out.

So as I read the data I was really surprised. For both X- and Y-axis the N64 controller sends 1 byte.  So I was assuming the range would be 0-255 like a PSX controller.
But the resolution of original N64 Padsis only 160!

That's the reason why in first person shooter the crosshair was accelerating more and more as you pushed the stick to the edge. That's also the reason why the guy in Blast Corps would slowly walk to the right, when you pushed the stick all the way to the LEFT.

So what I did in the new code is: Sampling the analog stick with 10bit resolution (1024 steps), multiplying by 10 (=10240 steps) and finally dividing by 64 (=160 steps).

The result is that first person shooter are now much more playable, the controls are much more accurate. In Super Mario 64 there's no difference at all. It seems that game was just ignoring the extra steps or it got a way to handle it.

The new microcontroller I chose (ATTiny 13) is really small but right now it's not working perfectly. It got only 8 pins. I needed 6 pins so I had to use the /reset pin of the microcontrller as an I/O pin. Unfortunately without /reset pin I can flash the microntroller only once. It's working, but first I have to reset the N64 controller (hold L+R+Start, works with every N64 controller, even original ones). Without resetting the Y axis is fully tilted although the stick isn't touched.
The Y axis of the analog stick is sampled through the /reset pin so I'm pretty sure that's the cause of the problem.

But right now I've run out ATTiny13 and I won't release the code until that problem is fixed.

Link, a Gamecube analog stick should work!

Sorry guys, right now I'm busy. I will keep you posted!


Link83

#20
Hmm...so the N64 analog stick actually has 160 degrees/positions?

Over the years I have read quite a bit of conflicting information about the N64 analog sticks resolution, for instance:-

N64 USB Adaptoid
http://www.adaptoid.com/info.html
Quote from: N64 USB AdaptoidAnalog joystick resolution of 150 positions on each axis.

N64 Controller Software FAQ:-
http://www.eurasia.nu/wiki/index.php/N64ControllerSoftwareFaq
Quote from: N64 Controller Software FAQbit       16 -> 23 - X axis reading, 2's compliment
                    -128 = full left
                       0 = center
                    +128 = full right

bit       24 -> 31 - Y axis reading, 2's compliment
                    +128 = full top
                       0 = center
                    -128 = full bottom

Nintendo 64 to PS/2 Mouse Translator
http://courses.cit.cornell.edu/ee476/FinalProjects/s2002/jew17/lld.html
Quote from: Nintendo 64 to PS/2 Mouse TranslatorThe last 16 bits are two 8 bit 2's-complement segments showing the position of the analog stick along the X and Y axis. The controller then enters a 'quiet' state for about 200 microseconds where it will not respond to further poll requests.

Nintendo 64 Controller Information
http://www.mixdown.ca/n64dev/
Quote from: Nintendo 64 Controller InformationThe analog joystick is an optical module and operates very much the same as most mice. If I recall correctly, it has 24 'positions' in each direction from center, with center returning 0.

Some other interesting sites:-
http://svn.navi.cx/misc/trunk/wasabi/devices/cube64/notes/n64-observations
http://www.cs.duke.edu/~brownan/n642gc.html
http://web.archive.org/web/20020602053950/http://www.ihl.t.u-tokyo.ac.jp/~yamada/CABIN/device/N64RS.htm

Also, these Nintendo patents may be of help/interest:-

Pages 13-15 of this pdf:-
http://www.google.com/patents?id=rVUIAAAAEBAJ&printsec=abstract&zoom=4&source=gbs_overview_r&cad=0

Pages 27-35 of this pdf:-
http://www.google.com/patents?id=XjoYAAAAEBAJ&printsec=abstract&zoom=4&source=gbs_overview_r&cad=0

Are there any other microcontrollers that would have enough I/O pins so you would not have to use the /reset pin as an I/O?

Please keep up the great work micro :)

micro

It really has to be around 160 steps. 256 steps per axis definitely are too much as you will run into problems in Blast Corps, that's for sure.

On the other hand, 150 steps are not enough. Is I said before I borrowed 2 N64 pads from my sister. They were purple & green transparent. One of them was really like new and it gave my -80/+80 per axis; it was -90/+90 when I pushed the stick with brute force against the edge, the plastic was squeaking :D
The other controller was also in good condition but it had a little bit of dead zone. It gave me about -78/+78, with brute force about +-83 or +-85, I don't remember. But that's not the way it was designed to be played.

Link, you got really some good sources, but this one's not right:
Quotebit       16 -> 23 - X axis reading, 2's compliment
                     -128 = full left
                        0 = center
                     +128 = full right

bit       24 -> 31 - Y axis reading, 2's compliment
                     +128 = full top
                        0 = center
                     -128 = full bottom
It IS true that center = 0, but there is no way you can store -128 to +128 in one byte ^^

QuoteAre there any other microcontrollers that would have enough I/O pins so you would not have to use the /reset pin as an I/O?
Of course! The first thing I did when I found out about the 160 steps was fixing the code for the ATMega8 (the microcontroller I used on the pics above). It worked like a charm and it still does. But I thought it would be nice to have a smaller IC.

I attached the improved source code & hex file for the ATMega8, you can use it for non commercial use. The pinout is just the same as on the pictures above.

But I'd really love to get the ATTiny13 working properly without having to reset the controller with L+R+start. As you can see the ATTiny13 fits on the right half of stick unit, it's very neat:



public-pervert

i dont have the knowledge enough :-X , and i really wanna do this mod. so i need a step by step tutorial.. please, micro.. of course, if you're not much busy  ;D

thankss

micro

Ok public-pervert, first of all you'll need the ATMega8. I've got 2 or 3 of them left, I could send you a preprogrammed one. Just send me a private message.

You said you wanted to use the stick of a Playstation controller, right? Open the PSX controller and cut out one stick from the pcb. You'll end up getting something like this:


On the back side, solder 4 wires to the stick: VCC, GND, X-Pot & Y-Pot.

Now it's time to open the N64 controller and remove the stick unit. Just look at the kylejw's pictures. Once you removed the stick unit, open it and throw everthing out of it. You'll just need the housing of the stick unit. Also you'll need to make some space, take a dremel, knife, plier and/or sand paper to remove the bits of plastic in the stick unit housing that are in your way.

Now comes the difficult part. Take your PSX stick with the 4 wires and attach it into the stick unit. Route the 4 wires out of the unit, on the backside there should be an opening through which you can put the 4 wires.
I used hot glue to position the stick. Getting the stick fitting right takes VERY long. You always have to put the stick unit into the N64 controller and make sure the stick is centered right. If it's not, use a heat blower gun to make the hot glue liquid again and reposition the stick, again and again until the stick sits right.
There is no easy fool-proof way, it's all about trial & error. And because of that it's important to use hot glue and a heat gun :)

Here you see a picture of my Gamecube stick:

Yes, it looks like a mess!

To make things worse you'll also have to think about the bezel for the stick. The bezel also has to be fitted right so it won't be in the way of the stick.
For my Gamecube stick I glued the octagonal shaped bezel to the controller and I didn't use the top half of the stick housing at all, it wasn't needed.

Kylejw on the other hand glued the bezel directly onto the stick unit housing. You see, you'll have to try for yourself :D

But once the stick sits right in the stick unit, just wire up the stick with the ATMega8 and the N64 controller PCB. Just look at the 3 pin outs, it's straight forward:



The only extra components you'll need is a 10kOhm resistor and a 100nF ceramic cap. Both are very common & cheap, I got plenty of them. As I said send me a private message if you'd like me to send you the components.



@Link83:
QuoteI just have two questions if you dont mind:-
- Does your code work with any analog stick, or do they have to use a certain pot value? (Really hoping to use a broken official GameCube controller)
- Is the sensitivity set to about the same level as the original N64 analog stick?
I think the pot value isn't THAT critical, I assume they're all 10k (?)
Sensitivity now is about the same level but of course it feels different.

The original N64 stick was always a little hard to move, these days analog sticks are much easier to move.
Link, have you ever played Ocarina of Time on the Gamecube? It was on the bonus disc of Wind Waker. When I played it back in '03 or '04 I was really stunned by the improved visuals. But then I had serious problems aiming with the bow and the slingshot. I almost couldn't win the bow mini game in the city. N64 and Gamecube stick just aren't the same.

I had similar experience with N64 Ocarina of Time and my N64 Hori Mini Pad.

The Mini Pad featured a GC-style stick. The pad was ok, but really small. If I hadn't sold it a year ago I could check how many steps it had for the X- and Y-axis :(


public-pervert

thank you very very much, micro! ;D ;D ;D ;D im very exited to do this mod!! it shoud be pretty nice if you send the components. but theres just one problem.. i live in brazil  :-X. is it possible to you send me anyway?

Link83

#25
Thanks for the new ATMega8 code and the great writeup micro ;D

Quote from: micro on July 27, 2010, 06:43:20 AM
Quote from: Link83Are there any other microcontrollers that would have enough I/O pins so you would not have to use the /reset pin as an I/O?
Of course! The first thing I did when I found out about the 160 steps was fixing the code for the ATMega8 (the microcontroller I used on the pics above). It worked like a charm and it still does. But I thought it would be nice to have a smaller IC.
Sorry, I probably wasnt very clear - I was curious if there are any other (common) suitable Atmel chips that are smaller than the ATMega8 and have less pins, but still have more pins that than the ATTiny13, which doesnt really have enough? Maybe there is a suitable ATTiny chip on this list?:-
http://www.atmel.com/dyn/products/param_table.asp?family_id=607&OrderBy=1214&Direction=ASC#791
Of course if you can get around the /RESET problem on the ATTiny13 it would be perfect.

Quote from: micro on July 27, 2010, 06:43:20 AM
But I'd really love to get the ATTiny13 working properly without having to reset the controller with L+R+start. As you can see the ATTiny13 fits on the right half of stick unit, it's very neat:
It does look great, really nice and compact :)

Quote from: micro on July 28, 2010, 12:48:37 AM
I think the pot value isn't THAT critical, I assume they're all 10k (?)
On my official GameCube controller each potentiometer is marked as "4K", so I assume they are both 4 kiloohm? Would that make any difference?

Quote from: micro on July 28, 2010, 12:48:37 AM
The original N64 stick was always a little hard to move, these days analog sticks are much easier to move.
Link, have you ever played Ocarina of Time on the Gamecube? It was on the bonus disc of Wind Waker. When I played it back in '03 or '04 I was really stunned by the improved visuals. But then I had serious problems aiming with the bow and the slingshot. I almost couldn't win the bow mini game in the city. N64 and Gamecube stick just aren't the same.
I know exactly what you mean, but I got used to the difference over time. I didn't find the analog stick sensitivity a problem in Wind Waker, so I think the emulation may have been part of the problem as well (Although I realise the GameCube analog stick has more 'steps' than the N64 controller so has greater precision)

Quote from: micro on July 28, 2010, 12:48:37 AM
I had similar experience with N64 Ocarina of Time and my N64 Hori Mini Pad.

The Mini Pad featured a GC-style stick. The pad was ok, but really small. If I hadn't sold it a year ago I could check how many steps it had for the X- and Y-axis :(
I always wanted a Hori N64 Mini Pad but never managed to find one available for a reasonable price. A while ago I found this picture showing the insides:-

It looks like Hori might have used a secondary chip to convert the potentiometer signals into quadrature encoded signals, exactly like we are doing - I cant think of any other reason for Hori using two chips when other Hori N64 controllers used just one chip?

I have a few more questions if you dont mind!

How exactly did you glue the octagonal restrictor to the top of the N64 controller without using the top half of the analog stick case?

When melting the hot glue repeatedly to adjust the sticks position is there a risk of heating the analog stick or plastic casing too much? and would a hair dryer be suitable? (Dont have a heat gun)

I have a USB programmer that apparently supports an AVR ISP connection and is capable of programming an ATMega8:-
http://www.mcumall.com/forum/topic.asp?TOPIC_ID=1691
However its not clear to me exactly what needs to be connected to the ATMega8 in order to program it. I looked at quite a few datasheets and they didn't seem to explain a lot. Is it just a direct connection to the pins on the ATMega8, or do you need any resistors/capacitors in the programming schematic? I believe you need to connect the following five signals to these ATMega8 pins:-

SIGNAL - PIN
RST - 1
MISO - 18
SCK - 19
MOSI - 17
GND - 8 & 22
VCC - 7, 20 & 21?

I came across this tutorial:-
http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=93
Which seems to suggest that pin 21 should be left unconnected, and pin 20 should be connected to VCC - is that just for programming or should that be the case in the actual circuit?

Sorry for so many questions micro! Your input and hard work is very much appreciated :)

micro

Quote
Sorry, I probably wasnt very clear - I was curious if there are any other (common) suitable Atmel chips that are smaller than the ATMega8 and have less pins, but still have more pins that than the ATTiny13, which doesnt really have enough? Maybe there is a suitable ATTiny chip on this list?:-
http://www.atmel.com/dyn/products/param_table.asp?family_id=607&OrderBy=1214&Direction=ASC#791
Of course if you can get around the /RESET problem on the ATTiny13 it would be perfect.
Yes I think the ATtiny24 would be most suitable. It got enough I/O pins (but not too much), A/D converter, internal 8MHz clock, runs perfectly with 3,3V VCC. And of course you wouldn't have to touch /reset so further programming through ISP would be possible. My favourite electronics shop got them in stock and they're cheaper than ATmega8's.

But for me they're not an option as I only got 2 N64 controllers and now both are equipped with ATMega8's (again ;)).  My experiment with the ATtiny13's was a waste of time & money after all.
But I encourage you to try the ATtiny24! Only the input and output pins have to be changed in the source code, everything else should be the same.


QuoteIt does look great, really nice and compact
It turned out that this is the ideal place to attach the ATmega8 or even the ATtiny24 as well. As long as you don't cross the middle line you got plenty of space:



QuoteI have a USB programmer that apparently supports an AVR ISP connection and is capable of programming an ATMega8:-
http://www.mcumall.com/forum/topic.asp?TOPIC_ID=1691
However its not clear to me exactly what needs to be connected to the ATMega8 in order to program it. I looked at quite a few datasheets and they didn't seem to explain a lot. Is it just a direct connection to the pins on the ATMega8, or do you need any resistors/capacitors in the programming schematic? I believe you need to connect the following five signals to these ATMega8 pins:-

SIGNAL - PIN
RST - 1
MISO - 18
SCK - 19
MOSI - 17
GND - 8 & 22
VCC - 7, 20 & 21?
ISP means in-system programming. So normally when the AVR is in the system /reset is held high through a pull-up resistor connected to VCC. So I guess to program the AVR with your USB programmer you'll need to put a 10k resistor between VCC and /reset as you will need to do later anyway. So for in-system programming you'll need to power the ATmega8 (VCC 7 & GND 8+22 are enough for programming) and connect RST, MOSI, MISO & SCK.


Personally I'm using a simple ISP programming adaptor for the parallel port. You can get these for about 10$ on ebay.
All you need to do then is connecting a 10pin connector to your AVR (only need VCC, GND, RST, MOSI, MISO & SCK) and you can reprogram the AVR without removing it :)
This is how the ATtiny13 programming device that I built looks like, veeery small:


QuoteOn my official GameCube controller each potentiometer is marked as "4K", so I assume they are both 4 kiloohm? Would that make any difference?
No, I don't think that will make a difference.


QuoteIt looks like Hori might have used a secondary chip to convert the potentiometer signals into quadrature encoded signals, exactly like we are doing - I cant think of any other reason for Hori using two chips when other Hori N64 controllers used just one chip?
You could be right as it seems that most pins of the MAD IC are unconnected. I've never seen the insides of a Hori Mini but I'm pretty sure the analog stick of mine had a switch - just like XBox or Playstation controller (without a real function of course). Obviously the stick of this red Hori Mini doesn't has a switch...

QuoteHow exactly did you glue the octagonal restrictor to the top of the N64 controller without using the top half of the analog stick case?
That's easy! Using a dremel and sand paper/file I made the octagonal gate perfectly round. It had just the ideal diameter. I put it through the hole from the inside of the N64 controller. I didn't fell through the hole as it was still bigger than the hole. But the dome-shaped part of it sticked through the hole.
So I put some hot glue on it and that's it :) Here you can see how it looks from the inside of the controller:


Maybe it would be best to glue the bezel first and THEN attach the analog stick. (I think that's the way I did anyway :))

QuoteWhen melting the hot glue repeatedly to adjust the sticks position is there a risk of heating the analog stick or plastic casing too much? and would a hair dryer be suitable? (Dont have a heat gun)
I think that's possible. I mean my heat gun is able to melt solder, so you really have be careful. Pay extra attention to those 2 clips that are holding the Z-button. You don't wanna melt them down :)
I really don't know if a hair dryer has enough power to make hot glue liquid again. Just try it. You don't have to make the hot glue highly fluid, you can reposition the stick if the glue is gooey. The best would be if you don't let the glue totally cool down.

Nevertheless it will take some time until the stick is on the right spot.

Fortunately the Gamecube controller has 2 analog sticks, right? So you can melt down one, haha! (I actually used the C-stick as it was already on a small daughterboard)


QuoteI came across this tutorial:-
http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=93
Which seems to suggest that pin 21 should be left unconnected, and pin 20 should be connected to VCC - is that just for programming or should that be the case in the actual circuit?
Pin 21 is AREF and we need it for A/D conversion. But you're right about pin 20. I took a brief look into the datasheet. Pin 20 is AVCC and it should be connected to VCC, especially when using the A/D converters as we do. In that case a low pass filter is also recommended.
All I can say is that the ATmega8 works perfectly with Pin 20 unconnected. But I will try to find out what filter should be used and then we can add it to schematic.


micro

I'd also like to mention that it's possible to build your own AVR ISP programmer for the parallel port with very few components.
You'll only need:

1x male SUB-D 25 connector
1x 74HC244 IC
1x 100kOhm resistor
1x 100nF ceramic cap
1x 1N4148 diode

The schematics can be seen on:
http://www.rn-wissen.de/index.php/AVR-ISP_Programmierkabel
http://www.mikrocontroller.net/articles/STK200

That's also the same stuff that's in my programmer except for the 2 LEDs + resistors which you don't need anyway.

kylejw

Hey I'm back!  Can't post any pictures right now since I'm not home at the moment.

I know I was gone for quite a while, I was pretty busy with work and school, but lately have gotten back into this.  Trying to transition into some PS3 work, so  hoping to finally give everyone that's messaged me on Youtube a little closure.

I've gotten some professionally made PCBs (don't get too excited, they're pretty barebones) and revamped the code a lot.

Very glad I logged in today to see the post about 160 steps.  That's actually a bit surprising, I thought I was much closer to the end of the potentiometer... around 230ish for full swing.  When I get a chance, I'll  experiment with my cap a bit.

My Gamecube stick is a bit loose so for games like Goldeneye I've noticed the crosshair is a little jumpy, so I've implemented a software deadzone that has actually added a fair bit of complexity to the code.  I will of course be posting the source this time (sorry :p).  Be aware though that it's pretty messy.

And another thing I thought I'd mention, someone on Youtube suggested replacing the C-pad on the controller with another joystick, for games like Goldeneye which allowed you to use two controllers to control one player (so one analog would be slide left, slide right, forward, backward, and the other analog would look around).  I've began to implement this just for the coolness factor so hopefully I'll have pics up soon.

Until next time,

Kyle


micro

Welcome back :)

QuoteI've gotten some professionally made PCBs (don't get too excited, they're pretty barebones) and revamped the code a lot.
That's cool. With those PCBs I wonder if the annoying aligning of the analog stick is no longer required.

QuoteAnd another thing I thought I'd mention, someone on Youtube suggested replacing the C-pad on the controller with another joystick, for games like Goldeneye which allowed you to use two controllers to control one player (so one analog would be slide left, slide right, forward, backward, and the other analog would look around).  I've began to implement this just for the coolness factor so hopefully I'll have pics up soon
Wow, didn't know that. So you're building a N64 controller with 2 cords? I'm curious for the pics :D

QuoteVery glad I logged in today to see the post about 160 steps.  That's actually a bit surprising, I thought I was much closer to the end of the potentiometer... around 230ish for full swing.  When I get a chance, I'll  experiment with my cap a bit.

My Gamecube stick is a bit loose so for games like Goldeneye I've noticed the crosshair is a little jumpy, so I've implemented a software deadzone that has actually added a fair bit of complexity to the code.  I will of course be posting the source this time (sorry :p).  Be aware though that it's pretty messy.
You should really try it with 160 steps. It even may make the dead zone unnecessary.


kylejw

#30
Unfortunately the PCBs don't help with alignment.  They're simply for cleaner install.

I rewrote the code mostly from scratch, and ended up with great results.  I'll post it below then describe the functionality.

main.c:

/*  
File contains main functionality of the gray code simulator

Author:  Kyle Wagner 2010

main.c

Code may be changed and distributed FREELY, but please give credit to the author.
*/

#include <system.h>

#include "movement.h"

//#define DEADZONE 15
#define MAX_SWING_PER_DIRECTION 80

#pragma DATA _CONFIG, _BOR_OFF & _IOSCFS_8MHZ & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSCIO

void initPorts(void);
void makeHorizEqual( long target );
void makeVertEqual( long target );
char setDeadzone( long *horizDeadzone, long *vertDeadzone );

/* The next two globals contain the positions of the vertical and horizontal movement ports
in each of their respective state machines */
char stateVert=0;
char stateHoriz=0;

/* These are global because I code with poor style.  Sue me.
Contain the current ADC sample of each potentiometer */
long horizCurrent = 0;
long vertCurrent = 0;


int main(void)
{

/* Since I'm greedy and inefficient, I'll use longs everywhere.
From what I understant, PICs are much faster with unsigned ints, but I don't
want to deal with correcting everything to 0-255... */
long horizNew = 0;
long vertNew = 0;

/* I've tried to make the deadzone as transparent as possible if it is not needed
If the user plugs the controller in with the joystick centered, most of the deadzone code is
skipped over, though the memory is of course allocated */
long horizDeadzone = 0;
long vertDeadzone = 0;

/* Hold the upper and lower limits of the deadzone */
long horizDeadLower = 0;
long horizDeadUpper = 0;
long vertDeadLower = 0;
long vertDeadUpper = 0;

char useDeadzone = 0;

/* Init I/O and ADCs */
initPorts();

/* First decide if the user wants to use a deadzone, and if so, set up the related variables. */
useDeadzone = setDeadzone(&horizDeadzone, &vertDeadzone);

/* Grab initial potentiometer values */
horizCurrent = returnHorizAvg();
vertCurrent = returnVertAvg();

/* Set up deadzone ranges.  For example if the joystick center is at
100 counts, and the user selects a deadzone of 3, the limits of the deadzone will be 97-103
both horizontally and vertically */
horizDeadLower = horizCurrent - horizDeadzone;
horizDeadUpper = horizCurrent + horizDeadzone;
vertDeadLower = vertCurrent - vertDeadzone;
vertDeadUpper = vertCurrent + vertDeadzone;


while(1) {
horizNew = returnHorizAvg();
if( horizNew > horizDeadUpper ){
/* Horizontal is greater than the old value, and outside the deadzone, move it! */
makeHorizEqual( horizNew - horizDeadzone );
} else if( horizNew < horizDeadLower ) {
/* Horizontal is less than the old value, and outside the deadzone, move it! */
makeHorizEqual( horizNew + horizDeadzone );
} else if( useDeadzone == 1 ) {
/* Need to handle the casewhere we've switched from one side of the deadzone
to the other, so simply zero out the position */
makeHorizEqual( 0 );
}

vertNew = returnVertAvg();
if( vertNew > vertDeadUpper ){
/* Vertical is greater than the old value, and outside the deadzone, move it! */
makeVertEqual( vertNew - vertDeadzone );
} else if( vertNew < vertDeadLower ) {
/* Vertical is less than the old value, and outside the deadzone, move it! */
makeVertEqual( vertNew + vertDeadzone );
} else if( useDeadzone == 1 ) {
/* Need to handle the casewhere we've switched from one side of the deadzone
to the other, so simply zero out the position */
makeVertEqual( 0 );
}

}

return 0;
}

/* Our desired value is different from the current position index, so move in the direction of the desired value! */
void makeHorizEqual( long target ){
if( horizCurrent == target ){
return;
} else if( (horizCurrent > target) && (horizCurrent > -MAX_SWING_PER_DIRECTION) ) {
moveLeft();
horizCurrent--;
} else if( horizCurrent < MAX_SWING_PER_DIRECTION ){
moveRight();
horizCurrent++;
}
return;
}

/* Our desired value is different from the current position index, so move in the direction of the desired value! */
void makeVertEqual( long target ){
if( vertCurrent == target ){
return;
} else if( (vertCurrent > target) && (vertCurrent > -MAX_SWING_PER_DIRECTION) ) {
moveDown();
vertCurrent--;
} else if(vertCurrent < MAX_SWING_PER_DIRECTION){
moveUp();
vertCurrent++;
}
return;
}

/*  Set the deadzone to one of 5 possible presets
It would be trivial to increase this to nine presets (utilize the diagonal)
I just didn't so I could reduce complexity */
char setDeadzone( long *horizDeadzone, long *vertDeadzone )
{
/* To activate deadzone, hold the joystick in one direction while plugging it in (or turning the system on)
Wait approximately a second and release the joystick to the center position and wait three seconds before moving it */

/* Hold the stick all the way to the right to get medium deadzone horizontally, and slight deadzone vertically */
while( returnHorizAvg() < -50 ){
*horizDeadzone = 8;
*vertDeadzone = 3;
}
/* Hold the stick all the way to the left to add a slight deadzone to the horizontal axis */
while( returnHorizAvg() > 50 ){
*horizDeadzone = 5;
*vertDeadzone = 0;
}
/* Hold the stick all the way down to add a slight deadzone */
while( returnVertAvg() > 50 ){
*horizDeadzone = 3;
*vertDeadzone = 3;
}
/* Hold the stick all the way up to add a medium deadzone */
while( returnVertAvg() < -50 ){
*horizDeadzone = 8;
*vertDeadzone = 8;
}

if( (horizDeadzone > 0) || (vertDeadzone > 0)){
/* Delay for a second to allow the joystick to return to center */
delay_ms(3000);
return 1;
} else {
/* Without holding the analog in any direction, the default is 0 deadzone */
return 0;
}
}

/* Initialize the I/O and set up the ADCs */
void initPorts(void){
   trisa = 0b00010100;

trisc = 0x00;
adcon1 = 0x10;
ansel = 0b00001010;

   trisc = 0;
porta = 0;

/* For no reason... */
delay(100);

return;
}




movement.c:
/*  
File contains movement related functions.

Author:  Kyle Wagner 2010

movement.c

Code may be changed and distributed FREELY, but please give credit to the author.
*/

#include <system.h>
#include "movement.h"

/* The next two globals contain the positions of the vertical and horizontal movement ports
in each of their respective state machines */
extern char stateVert;
extern char stateHoriz;

/* Really random delay function. I use it to add a delay for the ADCs
but can't remember how it got all ugly like this...  Probably has
something to do with keeping it from being optimized out.  Should probably use some nop()s... */
void delay(unsigned char d) {
 unsigned int n;
 while (d--)
   for (n=0; n<1; n++) ;
}

/* Movement involves reading the ADC value, then moving within a state machine
appropriately.
Counting up, the output ports should read:
00
01
11
10
...

Counting down:
00
10
11
01

Counting up 3 then down 2:
00
10
11
10
00
*/

void moveLeft(void) {
switch(stateVert) {
case 0:
clear_bit(portc,3);
stateVert = 1;
break;
case 1:
clear_bit(portc,2);
stateVert = 3;
break;
case 2:
set_bit(portc,2);
stateVert = 0;
break;
case 3:
set_bit(portc,3);
stateVert = 2;
break;
}
return;
}

void moveRight(void) {
switch(stateVert) {
case 0:
clear_bit(portc,2);
stateVert = 2;
break;
case 1:
set_bit(portc,3);
stateVert = 0;
break;
case 2:
clear_bit(portc,3);
stateVert = 3;
break;
case 3:
set_bit(portc,2);
stateVert = 1;
break;
}
return;
}

void moveDown(void) {
switch(stateHoriz) {
case 0:
clear_bit(portc,0);
stateHoriz = 2;
break;
case 1:
set_bit(portc,1);
stateHoriz = 0;
break;
case 2:
clear_bit(portc,1);
stateHoriz = 3;
break;
case 3:
set_bit(portc,0);
stateHoriz = 1;
break;
}
return;
}

void moveUp(void) {
switch(stateHoriz) {
case 0:
clear_bit(portc,1);
stateHoriz = 1;
break;
case 1:
clear_bit(portc,0);
stateHoriz = 3;
break;
case 2:
set_bit(portc,0);
stateHoriz = 0;
break;
case 3:
set_bit(portc,1);
stateHoriz = 2;
break;
}
return;
}

/*  Poll the ADC on the horizontal potentiometer port,
summing twice then shifting (dividing) to get an average of two values
due to stability issues.  May be able to drop the second poll and shift,
to speed execution */
long returnHorizAvg(void){
long horizAvg=0;
adcon0 = 0b00001001;

adcon0 = adcon0 | 0b00000010;
delay(1);
horizAvg += adresh;
adcon0 = adcon0 | 0b00000010;
delay(1);
horizAvg += adresh;

/* Subtract 127 to center the result around 0 (theoretically) -- pot may not be at the exact middle */
return (horizAvg >> 1) - 127;
}

/*  Poll the ADC on the vertical potentiometer port,
summing twice then shifting (dividing) to get an average of two values
due to stability issues.  May be able to drop the second poll and shift,
to speed execution */
long returnVertAvg(void){
long vertAvg=0;
adcon0 = 0b00001101;

adcon0 = adcon0 | 0b00000010;
delay(1);
vertAvg += adresh;
adcon0 = adcon0 | 0b00000010;
delay(1);
vertAvg += adresh;

/* Subtract 127 to center the result around 0 (theoretically) -- pot may not be at the exact middle */
return (vertAvg >> 1) - 127;
}


movement.h
/*  
File contains headers for movement related functions.

Author:  Kyle Wagner 2010

movement.h

Code may be changed and distributed FREELY, but please give credit to the author.
*/

void delay(unsigned char d);

void moveUp(void);
void moveDown(void);
void moveLeft(void);
void moveRight(void);

long returnHorizAvg(void);
long returnVertAvg(void);


kylejw

#31
So in most cases the following happens:

1.  User Plugs in controller
2.  User turns on machine
3.  My chip continually samples the potentiometers and translates it to quadrature output

And nobody knows anything funky is going on.

However, since I wanted this to be an adaptive solution, you also have the following optional functionality:

OPTIONAL 1.  User holds the joystick up/down/left/right
2.  User Plugs in controller / User turns on machine
IF STEP 1 EXECUTED:3.  My chip reads the initial position and sets one of the following deadzones:
           Down:  Slight deadzone in both directions
           Up:  Medium deadzone in both directions
           Left:  No vertical deadzone, slight horizontal deadzone
           Right:  Slight vertical deadzone, medium horizontal deadzone
IF STEP 1 EXECUTED: 4.  User releases joystick
IF STEP 1 EXECUTED: 5.  My chip initializes itself like normal, but with the selected deadzone

6.  My chip continually samples the potentiometers and translates it to quadrature output

If the user leaves the joystick in the neutral position (as happens normally), there is no difference to their experience.

The reason I went with the staggered deadzones for left and right is I notice on a widescreen TV I always move horizontally too much, so this kind of delays my movement.  I prefer it, but most people may not...

It seems like the 160 steps is true too.  I suspect that it would be acceptable to use the full range of 0-255 though, since I believe the issues of 'down turning to up' and other anomalies are a result of 3rd party controllers not monitoring for overflow.

kylejw


PIC16F616:


                 __________
       Gray(4) -|          |- Gray(3)
               -|          |-
      Vertical -|          |-
               -|          |- Horizontal
               -|          |- White
               -|          |- Gray(1)
      Gray(2)  -|__________|- Gray(5)



Where the whites and grays are the connector from the original stick

White | Gray(1) | Gray(2) | Gray(3) | Gray(4) | Gray(5)

micro

That's great, now people can choose between PIC and AVR :)
You put some great effort into the implementation of dead-zones.

Quote
It seems like the 160 steps is true too.  I suspect that it would be acceptable to use the full range of 0-255 though, since I believe the issues of 'down turning to up' and other anomalies are a result of 3rd party controllers not monitoring for overflow.

Even original N64 controllers by Nintendo don't care if the stick exceeds 160 steps / axis. It seems it's up to the game to interpret the value of the x- and y-axis. Mari0 64 does a perfect job, Turok is moving too fast & too far and Blast Corps is failing when using 256 steps.
I guess there are many more games that can't handle 256 steps.

I made a video showing the resolution of orignal N64 controllers:
resolution of the N64 analog stick





Link83

#34
Its great to see you back kylejw and thanks for all your awesome work, its great that people now have more options  ;) (Especially since not everyone has the correct programmer for one particular chip)

I am curious about the software dead zone you mentioned, have you tried your code without it? and could it be removed? (Down to personal preference of course)

Also looking forward to hearing more about your dual analog N64 controller mod for GoldenEye :)

micro, thanks for the great video - that very nicely demostrates the difference. I am curious though, are your measurements being made between the analog stick and the controller PCB, or from the actual controller plug? I only ask as I have been reading Nintendo's patent:-
http://www.google.com/patents?id=XjoYAAAAEBAJ&printsec=abstract&zoom=4&source=gbs_overview_r&cad=0
and on page 45 it says:-
Quote from: Nintendo Patent 5963196With referring to FIG. 32 and FIG. 33, a first center correction method will be described. A method shown in FIG. 32 is a method that when the data Xa and Ya of the X counter 444X and the Y counter 444Y respectively exist within a predetermined range defined by oblique lines in FIG. 32, data outputted from the analog joystick 45 (hereinafter called as "joystick data") X and Y are regarded as "0", respectively. Specifically, if the counter data Xa exists in a range from "+16" to "-16", the joystick data X is outputted as "0". Similarly, if the counter data Ya exists in a range from "+16" to "-16", the joystick data Y of "0" is outputted. In addition, these specific numeral values "16" and "-16" are values determined through a laboratory work by the inventors et al.; however, it is needless to say that other arbitrary numeral values may be set. That is, in the laboratory work by the inventors et al., the deviation of the data at the neutral position of the lever 474 is approximately within the range from "16" to "-16", and therefore, the above described numeral values are utilized; however, if it is considered that other numeral values are preferable, the other numeral values may be used.
So its likely that Nintendo choose +/-16 steps as the dead zone for the N64 analog stick, but it doesnt seem clear to me what is actually deciding this dead zone, is it the controllers CNT-NUS chip, the PIF-NUS chip in the N64, or the game software itself? What i'm wondering is if the range is actually +/-96 with +/-16 already 'ignored' as the dead zone, in which case the sensitivity would be slightly different (i.e. The actual N64 analog stick range might be 192 steps but the controller only outputs data for 160 steps, which when we match this number to the 256 steps of the GameCube analog stick might make the sensitivity feel different) Maybe I dont know what im talking about though!

micro

I was using the socket of an extension cord, so the first 2 controllers have never been opened or tampered with.

I got really a hard time reading and understanding patents, but I'm pretty sure dead-zones as described in the patent are handled by the N64 itself. But I don't know if the PIF-NUS or the game corrects the analog stick data, but I guess it's up to the game.

No, the range transmitted by the N64 controller to the N64 console probably isn't +/-96. The Gamecube stick of the 4th controller in the video has a range of exactly 160 steps and that's also the same range that you'll get when reading the whole controller via controller cord. :) Otherwise you'd only get +/-64 steps. (which is the resolution the N64 games are using after the dead-zone correction in the end)


micro

#36
Now my code is also available for ATtiny24's. 50% smaller, 30% less expensive.

Many thanks to Link83 who provided me with some free ATtiny24's :)

The latest source code for both AVR microcontrollers as well as instructions and schematics are included in one zip file (1.7MB). Go grab it!
http://www.mediafire.com/file/q4232gx1w2q2w0s/GC_PSX_stick_in_N64_controller_v1.2.zip (v1.2 corrected ISP pinout)

Obligatory youtube vid:  8)
Gamecube analog stick in N64 controller mod

Link83

#37
Thats great micro :) I expecially like the pdf explaining how to program the ATmega8/ATtiny24, I was a bit concerned about setting the fuse bits correctly.

I also like the range modifier, that extra 5% might help in certain games 8) Do you mind if I ask what calculation you used to get 168 steps?

I have ordered all the parts and cant wait for them to arrive so I can try this out!

btw, I have a brand new and unused N64 controller which I could try and measure the range on if your interested? I am sure we are already at the maximum range, but thought i'd mention it just in case.

micro

QuoteI also like the range modifier, that extra 5% might help in certain games Cool Do you mind if I ask what calculation you used to get 168 steps?

The formula is quite simple. Both ATmega8 and ATtiny24 got 10bit A/D converters, so when sampling X Pot and Y Pot you'll get 1024 steps (0-1023). In the source code, there is the variable called "R". Default value of R is 20, if range modifier pin is low during power up then R=21.
Formula:  range = 1024 * R / 128 steps

So when R=20 you get 1024*20/128=160 steps and for R=21 you get 1024*21/128=168 steps. :)

It would be nice to know the range of a brand new N64 controller but for the program I wrote you would need some more components for RS232 communication to your PC as well as a extension cord...

QuoteI have ordered all the parts and cant wait for them to arrive so I can try this out!
And I'm curious for your finished controller and your experience with the games :D

Link83

#39
Quote from: micro on August 07, 2010, 03:42:51 PM
It would be nice to know the range of a brand new N64 controller but for the program I wrote you would need some more components for RS232 communication to your PC as well as a extension cord...
I do have an RS232 port and an extension cord I can hack up, so I guess I just need the other components?

Alternatively I came across this thread for an "N64 Controller Demo":-
http://retroactive.be/64dev/forum/viewtopic.php?t=75
I have an N64 backup unit (I'm not a pirate, I just like playing unreleased N64 games such as Zelda Master Quest or 40 Winks on a real N64) so thought if I could run that demo on a real N64 I might be able to get the measurements that way, although obviously I would have to add +/-16 to account for the dead zone.

Unfortunately the download link in the above thread is dead :( but I have emailed Cyanide in the hope that they can send me the file for testing.

Quote from: micro on August 07, 2010, 03:42:51 PM
And I'm curious for your finished controller and your experience with the games :D
I will post pictures when its finished :) I think the programmer will take the longest to arrive since its coming from Taiwan.