SHARE
TWEET

N64_Stick_Converter_PCB_v2.1-mod.c

a guest Nov 9th, 2014 187 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * N64_Stick_Converter_PCB_v2.1-mod.c
  3.  *
  4.  * Created: 30.06.2013 10:31:30
  5.  * Author: Jakob Schäfer
  6.  * Modified: 26.10.2014 16:00:00
  7.  * Modified By: blecky
  8.  *
  9.  * ONLY FOR YOUR OWN PERSONAL USE! COMMERCIAL USE PROHIBITED!
  10.  * NUR FÜR DEN EIGENGEBRAUCH! GEWERBLICHE NUTZUNG VERBOTEN!
  11.  *
  12.  * fusebyte low:    0x42
  13.  * fusebyte high:   0xDF
  14.  *
  15.  * --------------------------------------------------------------
  16.  * ATtiny24A pin    |   function                                |
  17.  * (PDIP / SOIC)    |                                           |
  18.  * -------------------------------------------------------------|
  19.  * 1                |   VCC =                                   |
  20.  *                  |   N64 controller PCB pin no. 5.           |
  21.  *                  |   Bypass to GND with 100 nF capacitor     |
  22.  * -------------------------------------------------------------|
  23.  * 2                |   VCC =                                   |
  24.  *                  |   N64 controller PCB pin no. 5.           |
  25.  *                  |   Bypass to GND with 100 nF capacitor     |
  26.  * -------------------------------------------------------------|
  27.  * 3                |   Enable normal calibration (active low); |
  28.  *                      short to ground to enable calibration   |
  29.  * -------------------------------------------------------------|
  30.  * 4                |   RESET for programming                   |
  31.  *                  |   Connect with 10 kOhm resistor to VCC    |
  32.  * -------------------------------------------------------------|
  33.  * 5                |   N64 controller PCB pin no. 3            |
  34.  * -------------------------------------------------------------|
  35.  * 6                |   N64 controller PCB pin no. 6            |
  36.  * -------------------------------------------------------------|
  37.  * 7                |   Invert X axis (active high); lift pin   |
  38.  *                  |   to invert - MOSI for programming        |
  39.  * -------------------------------------------------------------|
  40.  * 8                |   Invert Y axis (active high); lift pin   |
  41.  *                  |   to invert - MISO for programming        |
  42.  * -------------------------------------------------------------|
  43.  * 9                |   N64 controller PCB pin no. 2            |
  44.  *                  |   SCK for programming                     |
  45.  * -------------------------------------------------------------|
  46.  * 10               |   N64 controller PCB pin no. 1            |
  47.  * -------------------------------------------------------------|
  48.  * 11               |   X axis of the stick potentiometer       |
  49.  * -------------------------------------------------------------|
  50.  * 12               |   Enable calibration with dead zones      |
  51.  *                  |  (active low); short to ground to enable  |
  52.  *                  |   calibration                             |
  53.  * -------------------------------------------------------------|
  54.  * 13               |   X axis of the stick potentiometer       |
  55.  * -------------------------------------------------------------|
  56.  * 14               |   GND =                                   |
  57.  *                  |   N64 controller PCB pin no. 4            |
  58.  * -------------------------------------------------------------|
  59.  *
  60.  * This is modified from the very same program that's running on
  61.  * these PCB's:
  62.  * http://nfggames.com/forum2/index.php?topic=5023.0
  63.  * http://www.mediafire.com/?encle2go54et96l
  64.  *
  65.  * Yes, English comments only. Deal with it ;)
  66.  *
  67.  * If you want to increase/decrease the range of the stick, then try
  68.  * out new values for the MIN_RANGE and MAX_RANGE constants below:
  69.  */
  70.  
  71.  
  72.  
  73. #define F_CPU 1000000UL
  74. #define MIN_RANGE 81                     // +/- guaranteed minimum range per axis
  75. #define MAX_RANGE 84                     // upper limit
  76. //#define INVX ( !(PINA&(1<<PINA2)) )
  77. //#define INVY ( !(PINA&(1<<PINA3)) )
  78. #define INVX (PINA&(1<<PINA6))           //This pin is now always ground until desolder
  79. #define INVY (PINA&(1<<PINA5))           //This pin is now always ground until desolder
  80. #include <avr/io.h>
  81. #include <util/delay.h>
  82. #include <avr/eeprom.h>
  83.  
  84. uint8_t EEMEM calibrationStep = 5;       // 0 for standard operation, 1-5 for calibration
  85. uint16_t EEMEM leftToNeutral;            // For calculating the Dead Zone and abs. position
  86. uint16_t EEMEM upToNeutral;              
  87. uint16_t EEMEM xAbsolute;                // Absolute neutral position when using Dead Zones
  88. uint16_t EEMEM yAbsolute;              
  89. uint8_t EEMEM dx = 0;                    // Dead Zone
  90. uint8_t EEMEM dy = 0;
  91. uint8_t EEMEM cx = 0;                    // Amplification factors for scaling the ADC values
  92. uint8_t EEMEM cy = 0;        
  93.  
  94. uint16_t GetX(void);                     // Are ADC value for X-axis back (0-1023)
  95. uint16_t GetY(void);                     // Are ADC value for Y-axis back (0-1023)
  96. uint8_t ScaleDown(uint16_t raw16, uint8_t c);   // Scaled to the appropriate range
  97. uint8_t RotateLeft(uint8_t cData);       // Rotated one byte to one character to the left
  98. uint8_t RotateRight(uint8_t cData);      // Rotated one byte to one character to the right
  99. void Calibration(void);                  // Extensive calibration function
  100.  
  101.  
  102. int main(void)
  103. {  
  104.    int16_t xSteps, ySteps;
  105.    uint16_t x, y, xOld, yOld;
  106.    uint8_t xNeutral8, yNeutral8;
  107.    uint8_t xWheel = 0b11001100;
  108.    uint8_t yWheel = 0b00110011;
  109.    uint16_t xNeutral16, yNeutral16;
  110.    uint8_t xFaktor = eeprom_read_byte(&cx);  
  111.    uint8_t yFaktor = eeprom_read_byte(&cy);
  112.    uint8_t xDeadzone = eeprom_read_byte(&dx);
  113.    uint8_t yDeadzone = eeprom_read_byte(&dy);
  114.    uint8_t useDeadzone;
  115.    
  116.    // Setup Ports
  117.    //DDRA = (1<<DDA6)|(1<<DDA7);
  118.    //DDRB = (1<<DDB0)|(1<<DDB1);
  119.    //PORTA = (1<<PORTA2)|(1<<PORTA3)|(1<<PORTA5);
  120.    //PORTB = (1<<PORTB2);
  121.     DDRA = (1<<DDA3)|(1<<DDA4)|(1<<DDA7);
  122.    DDRB = (1<<DDB2);
  123.    PORTA = (1<<PORTA1)|(1<<PORTA5)|(1<<PORTA6);
  124.    PORTB = (1<<PORTB1);
  125.    
  126.    // Disable unneeded modules
  127.    PRR |= (1<<PRTIM0)|(1<<PRTIM1)|(1<<PRUSI);
  128.    
  129.    // 0:25 s wait until voltage stable
  130.    _delay_ms(250);      
  131.    
  132.    // Determine whether Dead Zones are used:
  133.    if ( (xDeadzone==0) && (yDeadzone==0) )
  134.       useDeadzone = 0;
  135.    else
  136.       useDeadzone = 1;
  137.      
  138.    // Setup ADC
  139.    //DIDR0 = (1<<ADC0D)|(1<<ADC1D);       // Digital input disable for PA0 + 1
  140.    DIDR0 = (1<<ADC0D)|(1<<ADC2D);         // Digital input disable for PA0 + 2
  141.    ADMUX = 0x01;                          // Channel 1
  142.    ADCSRA = (1<<ADPS0)|(1<<ADPS1);        // Clock divider = 8 ==> f_ADC = 125 kHz
  143.    ADCSRA |= (1<<ADEN);                   // ADC Activation
  144.  
  145.      
  146.    // 1.  ADC conversion
  147.    xNeutral16 = GetX();  
  148.    
  149.    // X value:
  150.    xNeutral16 = GetX();  
  151.    // If deadzone present, absolute value read from EEPROM
  152.    if (useDeadzone)      
  153.       xNeutral16 = eeprom_read_word(&xAbsolute);            
  154.    xNeutral8 = ScaleDown(xNeutral16, xFaktor);
  155.    xOld = xNeutral8;
  156.    
  157.    // Y value:
  158.    yNeutral16 = GetY();
  159.    // If deadzone present, absolute value read from EEPROM
  160.    if (useDeadzone)      
  161.       yNeutral16 = eeprom_read_word(&yAbsolute);                    
  162.    yNeutral8 = ScaleDown(yNeutral16, yFaktor);
  163.    yOld = yNeutral8;  
  164.    
  165.     // If PINB.1 = 0, then the next time
  166.     // Activate normal calibration mode:
  167.     //if( ((1<<PINB2)&PINB)==0){
  168.    if( ((1<<PINB1)&PINB)==0){
  169.       eeprom_write_byte(&calibrationStep, 5);      
  170.       eeprom_write_byte(&dx, 0);
  171.       eeprom_write_byte(&dy, 0);      
  172.       while(1);
  173.    }
  174.    
  175.     // If PINA.1 = 0, then the next time
  176.     // Activate Calibration Mode with Dead Zone:
  177.     //if( ((1<<PINA5)&PINA)==0){
  178.    if( ((1<<PINA1)&PINA)==0){
  179.       eeprom_write_byte(&calibrationStep, 1);
  180.       while(1);
  181.    }
  182.    
  183.     // If calibration_step != 0, then perform Calibration Mode
  184.    if ( (eeprom_read_byte(&calibrationStep)) > 0x00 ){
  185.       Calibration();
  186.    }
  187.    
  188.    
  189.     while(1)
  190.     {  
  191.      
  192.       // Read X value
  193.       x = GetX();      
  194.       // Deadzone consideration
  195.       if (useDeadzone){        
  196.          if (x > xNeutral16){
  197.             if (x <= (xNeutral16+xDeadzone))
  198.                x = xNeutral16;
  199.             else
  200.                x = x - xDeadzone;
  201.          }
  202.          if (x < xNeutral16){
  203.             if (x >= (xNeutral16-xDeadzone))
  204.                x = xNeutral16;
  205.             else
  206.                x = x + xDeadzone;
  207.          }
  208.       }  
  209.       // Down scale  
  210.       x = ScaleDown( x, xFaktor);
  211.       // Limit on +/- 84
  212.       if (x>xNeutral8)
  213.          if ( (x-xNeutral8) > MAX_RANGE)
  214.             x = xNeutral8 + MAX_RANGE;
  215.       if (x<xNeutral8)
  216.          if ( (xNeutral8-x) > MAX_RANGE)
  217.             x = xNeutral8 - MAX_RANGE;      
  218.                  
  219.       // Read Y value:
  220.       y = GetY();      
  221.       // Deadzone consideration
  222.       if (useDeadzone){        
  223.          if (y > yNeutral16){
  224.             if (y <= (yNeutral16+yDeadzone))
  225.                y = yNeutral16;
  226.             else
  227.                y = y - yDeadzone;
  228.          }
  229.          if (y < yNeutral16){
  230.             if (y >= (yNeutral16-yDeadzone))
  231.                y = yNeutral16;
  232.             else
  233.                y = y + yDeadzone;
  234.          }
  235.       }      
  236.       // Down scale  
  237.       y = ScaleDown(y, yFaktor);      
  238.       // Limit on +/- 84
  239.       if (y>yNeutral8)
  240.          if ( (y-yNeutral8) > MAX_RANGE)
  241.             y = yNeutral8 + MAX_RANGE;
  242.       if (y<yNeutral8)
  243.          if ( (yNeutral8-y) > MAX_RANGE)
  244.             y = yNeutral8 - MAX_RANGE;
  245.    
  246.       //Calculate Number of Steps
  247.       xSteps =  (int16_t) x - xOld;
  248.       ySteps =  (int16_t) y - yOld;
  249.      
  250.       // If corr jumper bridged, invert:
  251.         //
  252.       if (INVX)
  253.          xSteps = -xSteps;
  254.       if (INVY)
  255.          ySteps = -ySteps;
  256.      
  257.       //save old values ​​for the next run
  258.       xOld = x;
  259.       yOld = y;
  260.      
  261.       //long as there is still work through Steps:
  262.       while ( (xSteps!=0) || (ySteps!=0) ){
  263.                        
  264.          if (xSteps<0){
  265.             xWheel = RotateLeft(xWheel);
  266.             xSteps++;            
  267.          }                  
  268.          if (xSteps>0){
  269.             xWheel = RotateRight(xWheel);
  270.             xSteps--;        
  271.          }  
  272.            
  273.          if (ySteps>0){
  274.             yWheel = RotateRight(yWheel);
  275.             ySteps--;            
  276.          }        
  277.          if (ySteps<0){
  278.             yWheel = RotateLeft(yWheel);
  279.             ySteps++;        
  280.          }      
  281.          
  282.         //PORTB = (PORTB&0b11111100)|(xWheel & 0b00000011);
  283.                 //PORTA = (PORTA&0b00111111)|(yWheel & 0b11000000);
  284.            
  285.         //New XA/XB- and YA/YB- values ​​set:
  286.         //Need to bit shift from original code values to keep
  287.         //correct waveform output
  288.         //Pin1 - PA6 -> PA3
  289.         //Pin2 - PA7 -> PA4
  290.         //Pin3 - PB1 -> PB2
  291.         //Pin6 - PB0 -> PA7
  292.                
  293.         PORTB = (PORTB&0b11111011)|((xWheel & 0b00000010) << 1);
  294.         PORTA = (PORTA&0b01111111)|((xWheel & 0b00000001) << 7);
  295.          
  296.                 PORTA = (PORTA&0b11100111)|((yWheel & 0b11000000) >> 3);    
  297.       }  
  298.      
  299.     }  
  300.    
  301. }
  302.  
  303.  
  304. uint16_t GetX(void){  
  305.    //ADMUX = 0x01;               // ADC Channel 1  
  306.     ADMUX = 0x00;               // ADC Channel 0  
  307.    ADCSRA |= (1<<ADSC);         // Start ADC conversion
  308.    while (ADCSRA & (1<<ADSC));      // Wait until finished
  309.    return ADC;
  310. }
  311.  
  312. uint16_t GetY(void){  
  313.    //ADMUX = 0x00;               // ADC Channel 0
  314.    ADMUX = 0x02;               // ADC Channel 2
  315.    ADCSRA |= (1<<ADSC);         // Start ADC conversion
  316.    while (ADCSRA & (1<<ADSC));      // Wait until finished
  317.    return ADC;
  318. }
  319.  
  320. uint8_t RotateLeft (uint8_t cData){
  321.    uint8_t result;
  322.    if ( cData & (1<<7) )
  323.       result = (cData<<1)|(1<<0);
  324.    else
  325.       result = (cData<<1);  
  326.    return result;
  327. }
  328.  
  329. uint8_t RotateRight (uint8_t cData){
  330.    uint8_t result;
  331.    if ( cData & (1<<0) )
  332.       result = (cData>>1)|(1<<7);
  333.    else
  334.       result = (cData>>1);  
  335.    return result;
  336. }
  337.  
  338. void Calibration(void){  
  339.    
  340.    uint16_t temp1, temp2;
  341.    uint16_t xNeutral16, yNeutral16;
  342.    uint16_t xMin, xMax, yMin, yMax;
  343.    uint16_t timerCounter = 0;
  344.    uint16_t xDeadzone, yDeadzone;
  345.    uint16_t xFaktor, yFaktor;
  346.    uint8_t nSchreibzugriffe = 0;
  347.  
  348.    
  349.    switch ( eeprom_read_byte(&calibrationStep) )
  350.    {
  351.      
  352.       case 1:
  353.          //Determination left2neutral
  354.          eeprom_write_word(&leftToNeutral, GetX() );        
  355.          //next step      
  356.          eeprom_write_byte(&calibrationStep, 2);
  357.          break;
  358.          
  359.       case 2:
  360.          //Determination xabsolute and d_x
  361.          temp1 = GetX();                                    // temp1 = right2neutral
  362.          temp2 = (eeprom_read_word(&leftToNeutral) + temp1 ) / 2;   // temp2 = xabsolute
  363.          eeprom_write_word(&xAbsolute, temp2);  
  364.          //X Deadzone determine  
  365.          if (temp1>temp2)
  366.             xDeadzone = temp1 - temp2;
  367.          else
  368.             xDeadzone = temp2 - temp1;
  369.          // Dead Zone available, then increase by 1
  370.          if (yDeadzone > 0)
  371.             yDeadzone++;
  372.                  
  373.          eeprom_write_byte( &dx, (uint8_t) xDeadzone);            
  374.          //next step      
  375.          eeprom_write_byte(&calibrationStep, 3);
  376.          break;
  377.          
  378.       case 3:
  379.          //Determination up2neutral
  380.          eeprom_write_word(&upToNeutral, GetY() );        
  381.          //next step      
  382.          eeprom_write_byte(&calibrationStep, 4);
  383.          break;
  384.          
  385.       case 4:
  386.          //Determination yabsolute and d_y
  387.          temp1 = GetY();                                 // temp1 = down2neutral
  388.          temp2 = (eeprom_read_word(&upToNeutral) + temp1 ) / 2;   // temp2 = yabsolut
  389.          eeprom_write_word(&yAbsolute, temp2);        
  390.          // Y Deadzone determine
  391.          if (temp1>temp2)
  392.             yDeadzone = temp1 - temp2;
  393.          else
  394.             yDeadzone = temp2 - temp1;
  395.          // Dead Zone available, then increase by 1
  396.          if (yDeadzone > 0)
  397.             yDeadzone++;
  398.          eeprom_write_byte( &dy, (uint8_t) yDeadzone);            
  399.          // next step      
  400.          eeprom_write_byte(&calibrationStep, 5);
  401.          
  402.       case 5:  
  403.                
  404.          //Determining factors; Consider Dead Zone!
  405.          xDeadzone = (uint16_t) eeprom_read_byte(&dx);
  406.          yDeadzone = (uint16_t) eeprom_read_byte(&dy);  
  407.                  
  408.             // When both Dead Zones = 0, then neutral
  409.             // Simply read position of ADC
  410.          if ( (xDeadzone==0) && (yDeadzone==0) )              
  411.          {
  412.             xNeutral16 = GetX();      
  413.             yNeutral16 = GetY();
  414.          }
  415.          // otherwise neutral position from EEPROM
  416.          else
  417.          {
  418.             xNeutral16 = eeprom_read_word(&xAbsolute);
  419.             yNeutral16 = eeprom_read_word(&yAbsolute);
  420.          }
  421.          
  422.          // All min and max values ​​reset
  423.          xMin = xNeutral16;                        
  424.          xMax = xNeutral16;
  425.          yMin = yNeutral16;
  426.          yMax = yNeutral16;
  427.          
  428.          while (1)
  429.          {
  430.            
  431.             //determine min and max values ​​for X-axis
  432.             temp1 = GetX();            
  433.             if (temp1 > xMax)
  434.                xMax = temp1;              
  435.             if (temp1 < xMin)
  436.                xMin = temp1;
  437.            
  438.             //determine min and max values ​​for Y-axis  
  439.             temp1 = GetY();            
  440.             if (temp1 > yMax)
  441.                yMax = temp1;              
  442.             if (temp1 < yMin)
  443.                yMin = temp1;  
  444.                
  445.             timerCounter++;
  446.            
  447.             // Approximately every second, but in total not more than 60 times:
  448.             if ( (timerCounter>4000) && (nSchreibzugriffe<60) )
  449.             {  
  450.                // calibration complete
  451.                eeprom_write_byte(&calibrationStep, 0x00);  
  452.                nSchreibzugriffe++;  
  453.                timerCounter = 0;  
  454.                
  455.                // Factor for X-axis:        
  456.                if ( (xMax - xNeutral16) < (xNeutral16 - xMin) )
  457.                   temp1 = xMax - xNeutral16;
  458.                else
  459.                   temp1 = xNeutral16 - xMin;                                    
  460.                // Dead Zone Pull                                
  461.                temp1 = temp1 - xDeadzone;      
  462.                // Calculate gain      
  463.                xFaktor = ((MIN_RANGE*256)/temp1);                    
  464.                // If radical left, go one better!
  465.                if ( ((MIN_RANGE*256)%temp1) > 0  )            
  466.                   xFaktor++;        
  467.                // Store in EEPROM      
  468.                eeprom_write_byte(&cx, (uint8_t) xFaktor);  
  469.                
  470.                // Factor for Y-axis:
  471.                if ( (yMax - yNeutral16) < (yNeutral16 - yMin) )
  472.                   temp1 = yMax - yNeutral16;
  473.                else
  474.                   temp1 = yNeutral16 - yMin;        
  475.                // Dead Zone Pull                                          
  476.                temp1 = temp1 - yDeadzone;      
  477.                // Calculate gain              
  478.                yFaktor = ((MIN_RANGE*256)/temp1);        
  479.                // If radical left, go one better!      
  480.                if ( ((MIN_RANGE*256)%temp1) > 0  )
  481.                   yFaktor++;
  482.                // Store in EEPROM  
  483.                eeprom_write_byte(&cy, (uint8_t) yFaktor);
  484.             }
  485.          }
  486.    }  
  487.    while (1);              
  488. }
  489.  
  490. uint8_t ScaleDown(uint16_t raw16, uint8_t c){
  491.    return  (uint8_t) ( (raw16*c) >> 8);  
  492. }
RAW Paste Data
Pastebin PRO Summer Special!
Get 40% OFF on Pastebin PRO accounts!
Top