SHARE
TWEET

N64_Stick_Converter_PCB_v2.1-mod-attiny261a.c

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