Advertisement
Guest User

N64_Stick_Converter_PCB_v2.1-mod.c

a guest
Nov 9th, 2014
362
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.48 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement