Advertisement
Guest User

N64_Stick_Converter_PCB_v2.1-mod.c

a guest
Oct 26th, 2014
160
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.21 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. //New XA/XB- and YA/YB- values ​​set:
  283. //PORTB = (PORTB&0b11111100)|(xWheel & 0b00000011);
  284. //PORTA = (PORTA&0b00111111)|(yWheel & 0b11000000);
  285. PORTB = (PORTB&0b11111011)|(xWheel & 0b00000100);
  286. PORTA = (PORTA&0b01111111)|(xWheel & 0b10000000);
  287.  
  288. PORTA = (PORTA&0b11100111)|(yWheel & 0b00011000);
  289. }
  290.  
  291. }
  292.  
  293. }
  294.  
  295.  
  296. uint16_t GetX(void){
  297. //ADMUX = 0x01; // ADC Channel 1
  298. ADMUX = 0x02; // ADC Channel 2
  299. ADCSRA |= (1<<ADSC); // Start ADC conversion
  300. while (ADCSRA & (1<<ADSC)); // Wait until finished
  301. return ADC;
  302. }
  303.  
  304. uint16_t GetY(void){
  305. ADMUX = 0x00; // ADC Channel 0
  306. ADCSRA |= (1<<ADSC); // Start ADC conversion
  307. while (ADCSRA & (1<<ADSC)); // Wait until finished
  308. return ADC;
  309. }
  310.  
  311. uint8_t RotateLeft (uint8_t cData){
  312. uint8_t result;
  313. if ( cData & (1<<7) )
  314. result = (cData<<1)|(1<<0);
  315. else
  316. result = (cData<<1);
  317. return result;
  318. }
  319.  
  320. uint8_t RotateRight (uint8_t cData){
  321. uint8_t result;
  322. if ( cData & (1<<0) )
  323. result = (cData>>1)|(1<<7);
  324. else
  325. result = (cData>>1);
  326. return result;
  327. }
  328.  
  329. void Calibration(void){
  330.  
  331. uint16_t temp1, temp2;
  332. uint16_t xNeutral16, yNeutral16;
  333. uint16_t xMin, xMax, yMin, yMax;
  334. uint16_t timerCounter = 0;
  335. uint16_t xDeadzone, yDeadzone;
  336. uint16_t xFaktor, yFaktor;
  337. uint8_t nSchreibzugriffe = 0;
  338.  
  339.  
  340. switch ( eeprom_read_byte(&calibrationStep) )
  341. {
  342.  
  343. case 1:
  344. //Determination left2neutral
  345. eeprom_write_word(&leftToNeutral, GetX() );
  346. //next step
  347. eeprom_write_byte(&calibrationStep, 2);
  348. break;
  349.  
  350. case 2:
  351. //Determination xabsolute and d_x
  352. temp1 = GetX(); // temp1 = right2neutral
  353. temp2 = (eeprom_read_word(&leftToNeutral) + temp1 ) / 2; // temp2 = xabsolute
  354. eeprom_write_word(&xAbsolute, temp2);
  355. //X Deadzone determine
  356. if (temp1>temp2)
  357. xDeadzone = temp1 - temp2;
  358. else
  359. xDeadzone = temp2 - temp1;
  360. // Dead Zone available, then increase by 1
  361. if (yDeadzone > 0)
  362. yDeadzone++;
  363.  
  364. eeprom_write_byte( &dx, (uint8_t) xDeadzone);
  365. //next step
  366. eeprom_write_byte(&calibrationStep, 3);
  367. break;
  368.  
  369. case 3:
  370. //Determination up2neutral
  371. eeprom_write_word(&upToNeutral, GetY() );
  372. //next step
  373. eeprom_write_byte(&calibrationStep, 4);
  374. break;
  375.  
  376. case 4:
  377. //Determination yabsolute and d_y
  378. temp1 = GetY(); // temp1 = down2neutral
  379. temp2 = (eeprom_read_word(&upToNeutral) + temp1 ) / 2; // temp2 = yabsolut
  380. eeprom_write_word(&yAbsolute, temp2);
  381. // Y Deadzone determine
  382. if (temp1>temp2)
  383. yDeadzone = temp1 - temp2;
  384. else
  385. yDeadzone = temp2 - temp1;
  386. // Dead Zone available, then increase by 1
  387. if (yDeadzone > 0)
  388. yDeadzone++;
  389. eeprom_write_byte( &dy, (uint8_t) yDeadzone);
  390. // next step
  391. eeprom_write_byte(&calibrationStep, 5);
  392.  
  393. case 5:
  394.  
  395. //Determining factors; Consider Dead Zone!
  396. xDeadzone = (uint16_t) eeprom_read_byte(&dx);
  397. yDeadzone = (uint16_t) eeprom_read_byte(&dy);
  398.  
  399. // When both Dead Zones = 0, then neutral
  400. // Simply read position of ADC
  401. if ( (xDeadzone==0) && (yDeadzone==0) )
  402. {
  403. xNeutral16 = GetX();
  404. yNeutral16 = GetY();
  405. }
  406. // otherwise neutral position from EEPROM
  407. else
  408. {
  409. xNeutral16 = eeprom_read_word(&xAbsolute);
  410. yNeutral16 = eeprom_read_word(&yAbsolute);
  411. }
  412.  
  413. // All min and max values ​​reset
  414. xMin = xNeutral16;
  415. xMax = xNeutral16;
  416. yMin = yNeutral16;
  417. yMax = yNeutral16;
  418.  
  419. while (1)
  420. {
  421.  
  422. //determine min and max values ​​for X-axis
  423. temp1 = GetX();
  424. if (temp1 > xMax)
  425. xMax = temp1;
  426. if (temp1 < xMin)
  427. xMin = temp1;
  428.  
  429. //determine min and max values ​​for Y-axis
  430. temp1 = GetY();
  431. if (temp1 > yMax)
  432. yMax = temp1;
  433. if (temp1 < yMin)
  434. yMin = temp1;
  435.  
  436. timerCounter++;
  437.  
  438. // Approximately every second, but in total not more than 60 times:
  439. if ( (timerCounter>4000) && (nSchreibzugriffe<60) )
  440. {
  441. // calibration complete
  442. eeprom_write_byte(&calibrationStep, 0x00);
  443. nSchreibzugriffe++;
  444. timerCounter = 0;
  445.  
  446. // Factor for X-axis:
  447. if ( (xMax - xNeutral16) < (xNeutral16 - xMin) )
  448. temp1 = xMax - xNeutral16;
  449. else
  450. temp1 = xNeutral16 - xMin;
  451. // Dead Zone Pull
  452. temp1 = temp1 - xDeadzone;
  453. // Calculate gain
  454. xFaktor = ((MIN_RANGE*256)/temp1);
  455. // If radical left, go one better!
  456. if ( ((MIN_RANGE*256)%temp1) > 0 )
  457. xFaktor++;
  458. // Store in EEPROM
  459. eeprom_write_byte(&cx, (uint8_t) xFaktor);
  460.  
  461. // Factor for Y-axis:
  462. if ( (yMax - yNeutral16) < (yNeutral16 - yMin) )
  463. temp1 = yMax - yNeutral16;
  464. else
  465. temp1 = yNeutral16 - yMin;
  466. // Dead Zone Pull
  467. temp1 = temp1 - yDeadzone;
  468. // Calculate gain
  469. yFaktor = ((MIN_RANGE*256)/temp1);
  470. // If radical left, go one better!
  471. if ( ((MIN_RANGE*256)%temp1) > 0 )
  472. yFaktor++;
  473. // Store in EEPROM
  474. eeprom_write_byte(&cy, (uint8_t) yFaktor);
  475. }
  476. }
  477. }
  478. while (1);
  479. }
  480.  
  481. uint8_t ScaleDown(uint16_t raw16, uint8_t c){
  482. return (uint8_t) ( (raw16*c) >> 8);
  483. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement