Guest User

N64_Stick_Converter_PCB_v2.1-mod-attiny261a.c

a guest
Apr 13th, 2016
117
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