Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * N64_Stick_Converter_PCB_v2.1-mod-attiny261a.c
- *
- * Created: 30.06.2013 10:31:30
- * Author: Jakob SchΓ€fer
- * Modified: 22.03.2015 18:00:00
- * Modified By: blecky
- *
- * ONLY FOR YOUR OWN PERSONAL USE! COMMERCIAL USE PROHIBITED!
- * NUR FΓR DEN EIGENGEBRAUCH! GEWERBLICHE NUTZUNG VERBOTEN!
- *
- * fusebyte low: 0x42
- * fusebyte high: 0xdf
- *
- * -------------------------------------------------------------|
- * ATtiny261A pin | Function |
- * (PDIP / SOIC) | |
- * -------------------------------------------------------------|
- * 1 | Invert Y axis (active high); lift pin |
- * | to invert - MOSI for programming |
- * -------------------------------------------------------------|
- * 2 | Invert X axis (active high); lift pin |
- * | to invert - MISO for programming |
- * -------------------------------------------------------------|
- * 3 | N64 controller PCB pin no. 2 |
- * | SCK for programming |
- * -------------------------------------------------------------|
- * 4 | N64 controller PCB pin no. 1 |
- * -------------------------------------------------------------|
- * 5 | VCC = |
- * | Disconnect from up/down analogue |
- * | Jumper to VCC |
- * -------------------------------------------------------------|
- * 6 | GND = |
- * | Jumper to ground |
- * -------------------------------------------------------------|
- * 7 | X axis of the stick potentiometer |
- * -------------------------------------------------------------|
- * 8 | Y axis of the stick potentiometer |
- * -------------------------------------------------------------|
- * 9 | GND |
- * -------------------------------------------------------------|
- * 10 | RESET for programming |
- * | Connect with 10 kOhm resistor to VCC |
- * -------------------------------------------------------------|
- * 11 | Enable normal calibration (active low) |
- * | short to ground to enable calibration |
- * -------------------------------------------------------------|
- * 12 | GND |
- * -------------------------------------------------------------|
- * 13 | Enable calibration with dead zones |
- * | (active low); short to ground to enable|
- * | calibration |
- * -------------------------------------------------------------|
- * 14 | VCC = |
- * | N64 controller PCB pin no. 5. |
- * | Bypass to GND with 100 nF capacitor |
- * -------------------------------------------------------------|
- * 15 | VCC = |
- * | Jumper to VCC |
- * -------------------------------------------------------------|
- * 16 | GND = |
- * | Jumper to ground |
- * -------------------------------------------------------------|
- * 17 | N64 controller PCB pin no. 3 |
- * -------------------------------------------------------------|
- * 18 | N64 controller PCB pin no. 6 |
- * -------------------------------------------------------------|
- * 19 | GND |
- * -------------------------------------------------------------|
- * 20 | GND |
- * -------------------------------------------------------------|
- *
- * This is modified from the very same program that's running on
- * these PCB's:
- * http://nfggames.com/forum2/index.php?topic=5023.0
- * http://www.mediafire.com/?encle2go54et96l
- *
- * Yes, English comments only. Deal with it ;)
- *
- * If you want to increase/decrease the range of the stick, then try
- * out new values for the MIN_RANGE and MAX_RANGE constants below:
- */
- #define F_CPU 1000000UL
- #define MIN_RANGE 81 // +/- guaranteed minimum range per axis
- #define MAX_RANGE 84 // upper limit
- //#define INVX ( !(PINA&(1<<PINA2)) )
- //#define INVY ( !(PINA&(1<<PINA3)) )
- #define INVX (PINB&(1<<PINB0)) //This pin is now always ground until desolder
- #define INVY (PINB&(1<<PINB1)) //This pin is now always ground until desolder
- #include <avr/io.h>
- #include <util/delay.h>
- #include <avr/eeprom.h>
- uint8_t EEMEM calibrationStep = 5; // 0 for standard operation, 1-5 for calibration
- uint16_t EEMEM leftToNeutral; // For calculating the Dead Zone and abs. position
- uint16_t EEMEM upToNeutral;
- uint16_t EEMEM xAbsolute; // Absolute neutral position when using Dead Zones
- uint16_t EEMEM yAbsolute;
- uint8_t EEMEM dx = 0; // Dead Zone
- uint8_t EEMEM dy = 0;
- uint8_t EEMEM cx = 0; // Amplification factors for scaling the ADC values
- uint8_t EEMEM cy = 0;
- uint16_t GetX(void); // Are ADC value for X-axis back (0-1023)
- uint16_t GetY(void); // Are ADC value for Y-axis back (0-1023)
- uint8_t ScaleDown(uint16_t raw16, uint8_t c); // Scaled to the appropriate range
- uint8_t RotateLeft(uint8_t cData); // Rotated one byte to one character to the left
- uint8_t RotateRight(uint8_t cData); // Rotated one byte to one character to the right
- void Calibration(void); // Extensive calibration function
- int main(void)
- {
- int16_t xSteps, ySteps;
- uint16_t x, y, xOld, yOld;
- uint8_t xNeutral8, yNeutral8;
- uint8_t xWheel = 0b11001100;
- uint8_t yWheel = 0b00110011;
- uint16_t xNeutral16, yNeutral16;
- uint8_t xFaktor = eeprom_read_byte(&cx);
- uint8_t yFaktor = eeprom_read_byte(&cy);
- uint8_t xDeadzone = eeprom_read_byte(&dx);
- uint8_t yDeadzone = eeprom_read_byte(&dy);
- uint8_t useDeadzone;
- // Setup Ports
- //DDRA = (1<<DDA6)|(1<<DDA7);
- //DDRB = (1<<DDB0)|(1<<DDB1);
- //PORTA = (1<<PORTA2)|(1<<PORTA3)|(1<<PORTA5);
- //PORTB = (1<<PORTB2);
- //Configure outputs
- DDRA = (1<<DDA3)|(1<<DDA4);
- DDRB = (1<<DDB2)|(1<<DDB3);
- //Configure Inputs
- PORTA = (1<<PORTA5)|(1<<PORTA7);
- PORTB = (1<<PORTB0)|(1<<PORTB1);
- // Disable unneeded modules
- PRR |= (1<<PRTIM0)|(1<<PRTIM1)|(1<<PRUSI);
- // 0:25 s wait until voltage stable
- _delay_ms(250);
- // Determine whether Dead Zones are used:
- if ( (xDeadzone==0) && (yDeadzone==0) )
- useDeadzone = 0;
- else
- useDeadzone = 1;
- // Setup ADC
- //DIDR0 = (1<<ADC0D)|(1<<ADC1D); // Digital input disable for PA0 + 1
- DIDR0 = (1<<ADC7D)|(1<<ADC8D); // Digital input disable for PA0 + 2
- ADMUX = 0x01; // Channel 1
- ADCSRA = (1<<ADPS0)|(1<<ADPS1); // Clock divider = 8 ==> f_ADC = 125 kHz
- ADCSRA |= (1<<ADEN); // ADC Activation
- // 1. ADC conversion
- xNeutral16 = GetX();
- // X value:
- xNeutral16 = GetX();
- // If deadzone present, absolute value read from EEPROM
- if (useDeadzone)
- xNeutral16 = eeprom_read_word(&xAbsolute);
- xNeutral8 = ScaleDown(xNeutral16, xFaktor);
- xOld = xNeutral8;
- // Y value:
- yNeutral16 = GetY();
- // If deadzone present, absolute value read from EEPROM
- if (useDeadzone)
- yNeutral16 = eeprom_read_word(&yAbsolute);
- yNeutral8 = ScaleDown(yNeutral16, yFaktor);
- yOld = yNeutral8;
- // If PINA.7 = 0, then the next time
- // Activate normal calibration mode:
- //if( ((1<<PINB2)&PINB)==0){
- if( ((1<<PINA7)&PINA)==0){
- eeprom_write_byte(&calibrationStep, 5);
- eeprom_write_byte(&dx, 0);
- eeprom_write_byte(&dy, 0);
- while(1);
- }
- // If PINA.5 = 0, then the next time
- // Activate Calibration Mode with Dead Zone:
- //if( ((1<<PINA5)&PINA)==0){
- if( ((1<<PINA5)&PINA)==0){
- eeprom_write_byte(&calibrationStep, 1);
- while(1);
- }
- // If calibration_step != 0, then perform Calibration Mode
- if ( (eeprom_read_byte(&calibrationStep)) > 0x00 ){
- Calibration();
- }
- while(1)
- {
- // Read X value
- x = GetX();
- // Deadzone consideration
- if (useDeadzone){
- if (x > xNeutral16){
- if (x <= (xNeutral16+xDeadzone))
- x = xNeutral16;
- else
- x = x - xDeadzone;
- }
- if (x < xNeutral16){
- if (x >= (xNeutral16-xDeadzone))
- x = xNeutral16;
- else
- x = x + xDeadzone;
- }
- }
- // Down scale
- x = ScaleDown( x, xFaktor);
- // Limit on +/- 84
- if (x>xNeutral8)
- if ( (x-xNeutral8) > MAX_RANGE)
- x = xNeutral8 + MAX_RANGE;
- if (x<xNeutral8)
- if ( (xNeutral8-x) > MAX_RANGE)
- x = xNeutral8 - MAX_RANGE;
- // Read Y value:
- y = GetY();
- // Deadzone consideration
- if (useDeadzone){
- if (y > yNeutral16){
- if (y <= (yNeutral16+yDeadzone))
- y = yNeutral16;
- else
- y = y - yDeadzone;
- }
- if (y < yNeutral16){
- if (y >= (yNeutral16-yDeadzone))
- y = yNeutral16;
- else
- y = y + yDeadzone;
- }
- }
- // Down scale
- y = ScaleDown(y, yFaktor);
- // Limit on +/- 84
- if (y>yNeutral8)
- if ( (y-yNeutral8) > MAX_RANGE)
- y = yNeutral8 + MAX_RANGE;
- if (y<yNeutral8)
- if ( (yNeutral8-y) > MAX_RANGE)
- y = yNeutral8 - MAX_RANGE;
- //Calculate Number of Steps
- xSteps = (int16_t) x - xOld;
- ySteps = (int16_t) y - yOld;
- // If corr jumper bridged, invert:
- //
- if (INVX)
- xSteps = -xSteps;
- if (INVY)
- ySteps = -ySteps;
- //save old values for the next run
- xOld = x;
- yOld = y;
- //long as there is still work through Steps:
- while ( (xSteps!=0) || (ySteps!=0) ){
- if (xSteps<0){
- xWheel = RotateLeft(xWheel);
- xSteps++;
- }
- if (xSteps>0){
- xWheel = RotateRight(xWheel);
- xSteps--;
- }
- if (ySteps>0){
- yWheel = RotateRight(yWheel);
- ySteps--;
- }
- if (ySteps<0){
- yWheel = RotateLeft(yWheel);
- ySteps++;
- }
- //PORTB = (PORTB&0b11111100)|(xWheel & 0b00000011);
- //PORTA = (PORTA&0b00111111)|(yWheel & 0b11000000);
- //New XA/XB- and YA/YB- values set:
- //Need to bit shift from original code values to keep
- //correct waveform output, the way the original pins
- //were selected meant this didn't need to happen.
- //
- // Orig New
- //Pin1 (YA) - PA6 -> PB3
- //Pin2 (YB) - PA7 -> PB2
- //Pin3 (XA) - PB1 -> PA3
- //Pin6 (XB) - PB0 -> PA2
- //Seperated out to make it easier to understand
- PORTA = (PORTA&0b11111011)|((xWheel & 0b00000001) << 2);
- PORTA = (PORTA&0b11110111)|((xWheel & 0b00000010) << 2);
- PORTB = (PORTB&0b11110111)|((yWheel & 0b01000000) >> 3);
- PORTB = (PORTB&0b11111011)|((yWheel & 0b10000000) >> 5);
- }
- }
- }
- uint16_t GetX(void){
- //ADMUX = 0x01; // ADC Channel 1
- ADMUX = 0x07; // ADC Channel 7
- ADCSRA |= (1<<ADSC); // Start ADC conversion
- while (ADCSRA & (1<<ADSC)); // Wait until finished
- return ADC;
- }
- uint16_t GetY(void){
- //ADMUX = 0x00; // ADC Channel 0
- ADMUX = 0x08; // ADC Channel 8
- ADCSRA |= (1<<ADSC); // Start ADC conversion
- while (ADCSRA & (1<<ADSC)); // Wait until finished
- return ADC;
- }
- uint8_t RotateLeft (uint8_t cData){
- uint8_t result;
- if ( cData & (1<<7) )
- result = (cData<<1)|(1<<0);
- else
- result = (cData<<1);
- return result;
- }
- uint8_t RotateRight (uint8_t cData){
- uint8_t result;
- if ( cData & (1<<0) )
- result = (cData>>1)|(1<<7);
- else
- result = (cData>>1);
- return result;
- }
- void Calibration(void){
- uint16_t temp1, temp2;
- uint16_t xNeutral16, yNeutral16;
- uint16_t xMin, xMax, yMin, yMax;
- uint16_t timerCounter = 0;
- uint16_t xDeadzone, yDeadzone;
- uint16_t xFaktor, yFaktor;
- uint8_t nSchreibzugriffe = 0;
- switch ( eeprom_read_byte(&calibrationStep) )
- {
- case 1:
- //Determination left2neutral
- eeprom_write_word(&leftToNeutral, GetX() );
- //next step
- eeprom_write_byte(&calibrationStep, 2);
- break;
- case 2:
- //Determination xabsolute and d_x
- temp1 = GetX(); // temp1 = right2neutral
- temp2 = (eeprom_read_word(&leftToNeutral) + temp1 ) / 2; // temp2 = xabsolute
- eeprom_write_word(&xAbsolute, temp2);
- //X Deadzone determine
- if (temp1>temp2)
- xDeadzone = temp1 - temp2;
- else
- xDeadzone = temp2 - temp1;
- // Dead Zone available, then increase by 1
- if (yDeadzone > 0)
- yDeadzone++;
- eeprom_write_byte( &dx, (uint8_t) xDeadzone);
- //next step
- eeprom_write_byte(&calibrationStep, 3);
- break;
- case 3:
- //Determination up2neutral
- eeprom_write_word(&upToNeutral, GetY() );
- //next step
- eeprom_write_byte(&calibrationStep, 4);
- break;
- case 4:
- //Determination yabsolute and d_y
- temp1 = GetY(); // temp1 = down2neutral
- temp2 = (eeprom_read_word(&upToNeutral) + temp1 ) / 2; // temp2 = yabsolut
- eeprom_write_word(&yAbsolute, temp2);
- // Y Deadzone determine
- if (temp1>temp2)
- yDeadzone = temp1 - temp2;
- else
- yDeadzone = temp2 - temp1;
- // Dead Zone available, then increase by 1
- if (yDeadzone > 0)
- yDeadzone++;
- eeprom_write_byte( &dy, (uint8_t) yDeadzone);
- // next step
- eeprom_write_byte(&calibrationStep, 5);
- case 5:
- //Determining factors; Consider Dead Zone!
- xDeadzone = (uint16_t) eeprom_read_byte(&dx);
- yDeadzone = (uint16_t) eeprom_read_byte(&dy);
- // When both Dead Zones = 0, then neutral
- // Simply read position of ADC
- if ( (xDeadzone==0) && (yDeadzone==0) )
- {
- xNeutral16 = GetX();
- yNeutral16 = GetY();
- }
- // otherwise neutral position from EEPROM
- else
- {
- xNeutral16 = eeprom_read_word(&xAbsolute);
- yNeutral16 = eeprom_read_word(&yAbsolute);
- }
- // All min and max values reset
- xMin = xNeutral16;
- xMax = xNeutral16;
- yMin = yNeutral16;
- yMax = yNeutral16;
- while (1)
- {
- //determine min and max values for X-axis
- temp1 = GetX();
- if (temp1 > xMax)
- xMax = temp1;
- if (temp1 < xMin)
- xMin = temp1;
- //determine min and max values for Y-axis
- temp1 = GetY();
- if (temp1 > yMax)
- yMax = temp1;
- if (temp1 < yMin)
- yMin = temp1;
- timerCounter++;
- // Approximately every second, but in total not more than 60 times:
- if ( (timerCounter>4000) && (nSchreibzugriffe<60) )
- {
- // calibration complete
- eeprom_write_byte(&calibrationStep, 0x00);
- nSchreibzugriffe++;
- timerCounter = 0;
- // Factor for X-axis:
- if ( (xMax - xNeutral16) < (xNeutral16 - xMin) )
- temp1 = xMax - xNeutral16;
- else
- temp1 = xNeutral16 - xMin;
- // Dead Zone Pull
- temp1 = temp1 - xDeadzone;
- // Calculate gain
- xFaktor = ((MIN_RANGE*256)/temp1);
- // If radical left, go one better!
- if ( ((MIN_RANGE*256)%temp1) > 0 )
- xFaktor++;
- // Store in EEPROM
- eeprom_write_byte(&cx, (uint8_t) xFaktor);
- // Factor for Y-axis:
- if ( (yMax - yNeutral16) < (yNeutral16 - yMin) )
- temp1 = yMax - yNeutral16;
- else
- temp1 = yNeutral16 - yMin;
- // Dead Zone Pull
- temp1 = temp1 - yDeadzone;
- // Calculate gain
- yFaktor = ((MIN_RANGE*256)/temp1);
- // If radical left, go one better!
- if ( ((MIN_RANGE*256)%temp1) > 0 )
- yFaktor++;
- // Store in EEPROM
- eeprom_write_byte(&cy, (uint8_t) yFaktor);
- }
- }
- }
- while (1);
- }
- uint8_t ScaleDown(uint16_t raw16, uint8_t c){
- return (uint8_t) ( (raw16*c) >> 8);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement