MNT Reform: Open Source Portable Computer https://mntre.com/reform
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

660 lines
17KB

  1. /*
  2. MNT Reform v2 Keyboard Firmware
  3. Copyright 2019 Lukas F. Hartmann / MNT Research GmbH, Berlin
  4. lukas@mntmn.com
  5. */
  6. /*
  7. LUFA Library
  8. Copyright (C) Dean Camera, 2018.
  9. dean [at] fourwalledcubicle [dot] com
  10. www.lufa-lib.org
  11. */
  12. /*
  13. Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com)
  14. Permission to use, copy, modify, distribute, and sell this
  15. software and its documentation for any purpose is hereby granted
  16. without fee, provided that the above copyright notice appear in
  17. all copies and that both that the copyright notice and this
  18. permission notice and warranty disclaimer appear in supporting
  19. documentation, and that the name of the author not be used in
  20. advertising or publicity pertaining to distribution of the
  21. software without specific, written prior permission.
  22. The author disclaims all warranties with regard to this
  23. software, including all implied warranties of merchantability
  24. and fitness. In no event shall the author be liable for any
  25. special, indirect or consequential damages or any damages
  26. whatsoever resulting from loss of use, data or profits, whether
  27. in an action of contract, negligence or other tortious action,
  28. arising out of or in connection with the use or performance of
  29. this software.
  30. */
  31. #include "Config/LUFAConfig.h"
  32. #include "Keyboard.h"
  33. #include <avr/io.h>
  34. #include "LUFA/Drivers/Peripheral/Serial.h"
  35. #include "ssd1306.h"
  36. #include "scancodes.h"
  37. #include <stdlib.h>
  38. /** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
  39. static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];
  40. /** LUFA HID Class driver interface configuration and state information. This structure is
  41. * passed to all HID Class driver functions, so that multiple instances of the same class
  42. * within a device can be differentiated from one another.
  43. */
  44. USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
  45. {
  46. .Config =
  47. {
  48. .InterfaceNumber = INTERFACE_ID_Keyboard,
  49. .ReportINEndpoint =
  50. {
  51. .Address = KEYBOARD_EPADDR,
  52. .Size = KEYBOARD_EPSIZE,
  53. .Banks = 1,
  54. },
  55. .PrevReportINBuffer = PrevKeyboardHIDReportBuffer,
  56. .PrevReportINBufferSize = sizeof(PrevKeyboardHIDReportBuffer),
  57. },
  58. };
  59. #define output_low(port,pin) port &= ~(1<<pin)
  60. #define output_high(port,pin) port |= (1<<pin)
  61. #define set_input(portdir,pin) portdir &= ~(1<<pin)
  62. #define set_output(portdir,pin) portdir |= (1<<pin)
  63. const uint8_t matrix[15*6] = {
  64. KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, HID_KEYBOARD_SC_EXSEL, HID_KEYBOARD_SC_EXSEL,
  65. KEY_GRAVE_ACCENT_AND_TILDE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS_AND_UNDERSCORE, KEY_EQUAL_AND_PLUS, KEY_BACKSPACE, 0,
  66. KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_OPENING_BRACKET_AND_OPENING_BRACE, KEY_CLOSING_BRACKET_AND_CLOSING_BRACE, KEY_BACKSLASH_AND_PIPE, 0,
  67. HID_KEYBOARD_SC_LEFT_CONTROL, HID_KEYBOARD_SC_APPLICATION, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON_AND_COLON, KEY_APOSTROPHE_AND_QUOTE, KEY_ENTER, 0,
  68. HID_KEYBOARD_SC_LEFT_SHIFT, KEY_DELETE, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN, HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN, KEY_SLASH_AND_QUESTION_MARK, HID_KEYBOARD_SC_UP_ARROW, HID_KEYBOARD_SC_RIGHT_SHIFT, 0,
  69. HID_KEYBOARD_SC_EXSEL, HID_KEYBOARD_SC_LEFT_GUI, HID_KEYBOARD_SC_LEFT_CONTROL, KEY_SPACE, HID_KEYBOARD_SC_LEFT_ALT, HID_KEYBOARD_SC_RIGHT_ALT, KEY_SPACE, HID_KEYBOARD_SC_PAGE_UP, HID_KEYBOARD_SC_PAGE_DOWN, HID_KEYBOARD_SC_LEFT_ARROW, HID_KEYBOARD_SC_DOWN_ARROW, HID_KEYBOARD_SC_RIGHT_ARROW, 0,0,0
  70. };
  71. uint8_t matrix_debounce[15*6];
  72. // f8 = sleep
  73. // 49 = mute
  74. // 84 = scroll lock
  75. char r_inbuf[10];
  76. void gfx_clear(void) {
  77. for (int y=0; y<4; y++) {
  78. for (int x=0; x<21; x++) {
  79. gfx_poke(x,y,' ');
  80. }
  81. }
  82. }
  83. void empty_serial(void) {
  84. int clock = 0;
  85. while (Serial_ReceiveByte()>=0 && clock<1000) {
  86. // flush serial
  87. clock++;
  88. }
  89. }
  90. int term_x = 0;
  91. int term_y = 0;
  92. char response[64];
  93. int remote_receive_string(int print) {
  94. char done = 0;
  95. int32_t clock = 0;
  96. int res_x = 0;
  97. response[0] = 0;
  98. while (!done) {
  99. int16_t chr = -1;
  100. clock = 0;
  101. while (chr==-1 || chr==0) {
  102. chr=Serial_ReceiveByte();
  103. clock++;
  104. if (clock>500000) goto timeout;
  105. }
  106. int poke_chr = chr;
  107. if (chr=='\n') poke_chr=' ';
  108. if (chr!='\r') {
  109. if (print) {
  110. gfx_poke(term_x,term_y,poke_chr);
  111. gfx_poke(term_x+1,term_y,' ');
  112. term_x++;
  113. if (term_x>=20) {
  114. term_x=0;
  115. term_y++;
  116. if (term_y>=3) {
  117. term_y=0;
  118. }
  119. }
  120. }
  121. if (res_x<63) {
  122. response[res_x++] = chr;
  123. response[res_x] = 0;
  124. }
  125. }
  126. if (chr=='\r') done = 1;
  127. }
  128. timeout:
  129. if (!done && print) gfx_poke(20,0,'T');
  130. empty_serial();
  131. if (print) {
  132. iota_gfx_flush();
  133. }
  134. return done;
  135. }
  136. void anim_hello(void) {
  137. gfx_clear();
  138. iota_gfx_on();
  139. for (int y=0; y<3; y++) {
  140. for (int x=0; x<12; x++) {
  141. gfx_poke(x+4,y+1,(5+y)*32+x);
  142. iota_gfx_flush();
  143. }
  144. }
  145. for (int y=0; y<0xff; y++) {
  146. iota_gfx_contrast(y);
  147. Delay_MS(2);
  148. }
  149. for (int y=0; y<0xff; y++) {
  150. iota_gfx_contrast(0xff-y);
  151. Delay_MS(2);
  152. }
  153. }
  154. void anim_goodbye(void) {
  155. gfx_clear();
  156. iota_gfx_on();
  157. for (int y=0; y<3; y++) {
  158. for (int x=0; x<12; x++) {
  159. gfx_poke(x+4,y+1,(5+y)*32+x);
  160. }
  161. }
  162. for (int y=0; y<3; y++) {
  163. for (int x=0; x<12; x++) {
  164. gfx_poke(x+4,y+1,' ');
  165. iota_gfx_flush();
  166. }
  167. }
  168. iota_gfx_off();
  169. }
  170. float voltages[8];
  171. void insert_bat_icon(char* str, int x, float v) {
  172. char icon = 0;
  173. if (v>=3.3) {
  174. icon = 8;
  175. } else if (v>=3.1) {
  176. icon = 6;
  177. } else if (v>=3.0) {
  178. icon = 4;
  179. } else if (v>=2.9) {
  180. icon = 2;
  181. } else {
  182. icon = 0;
  183. }
  184. str[x] = 4*32+icon;
  185. str[x+1] = 4*32+icon+1;
  186. }
  187. void remote_get_voltages(void) {
  188. empty_serial();
  189. term_x = 0;
  190. term_y = 0;
  191. float bat_volts = 0;
  192. float bat_amps = 0;
  193. char bat_gauge[5] = {0,0,0,0,0};
  194. Serial_SendByte('V');
  195. Serial_SendByte('\r');
  196. Delay_MS(1);
  197. remote_receive_string(0);
  198. bat_volts = ((float)atoi(response))/1000.0;
  199. Serial_SendByte('a');
  200. Serial_SendByte('\r');
  201. Delay_MS(1);
  202. remote_receive_string(0);
  203. bat_amps = ((float)atoi(response))/1000.0;
  204. Serial_SendByte('g');
  205. Serial_SendByte('\r');
  206. Delay_MS(1);
  207. remote_receive_string(0);
  208. strncpy(bat_gauge, response, 4);
  209. float sum_volts = 0;
  210. for (int i=0; i<8; i++) {
  211. // progress anim
  212. gfx_poke(0,0,32*4+((2*i)%10));
  213. gfx_poke(1,0,32*4+1+((2*i)%10));
  214. iota_gfx_flush();
  215. Serial_SendByte('0'+i);
  216. Serial_SendByte('v');
  217. Serial_SendByte('\r');
  218. Delay_MS(1);
  219. remote_receive_string(0);
  220. voltages[i] = ((float)atoi(response))/1000.0;
  221. if (voltages[i]<0 || voltages[i]>=5) voltages[i]=0;
  222. sum_volts += voltages[i];
  223. }
  224. //plot voltages
  225. gfx_clear();
  226. char str[32];
  227. sprintf(str,"[] %.1f [] %.1f %s",voltages[0],voltages[4],bat_gauge);
  228. insert_bat_icon(str,0,voltages[0]);
  229. insert_bat_icon(str,8,voltages[4]);
  230. gfx_poke_str(0,0,str);
  231. sprintf(str,"[] %.1f [] %.1f ",voltages[1],voltages[5]);
  232. insert_bat_icon(str,0,voltages[1]);
  233. insert_bat_icon(str,8,voltages[5]);
  234. gfx_poke_str(0,1,str);
  235. sprintf(str,"[] %.1f [] %.1f %.2fA",voltages[2],voltages[6],-bat_amps);
  236. insert_bat_icon(str,0,voltages[2]);
  237. insert_bat_icon(str,8,voltages[6]);
  238. gfx_poke_str(0,2,str);
  239. sprintf(str,"[] %.1f [] %.1f %.1fV",voltages[3],voltages[7],bat_volts);
  240. insert_bat_icon(str,0,voltages[3]);
  241. insert_bat_icon(str,8,voltages[7]);
  242. gfx_poke_str(0,3,str);
  243. iota_gfx_flush();
  244. }
  245. void remote_get_status(void) {
  246. gfx_clear();
  247. empty_serial();
  248. term_x = 0;
  249. term_y = 0;
  250. Serial_SendByte('s');
  251. Serial_SendByte('\r');
  252. Delay_MS(1);
  253. remote_receive_string(1);
  254. }
  255. void remote_get_cells(void) {
  256. gfx_clear();
  257. empty_serial();
  258. term_x = 0;
  259. term_y = 0;
  260. for (int i=0; i<8; i++) {
  261. Serial_SendByte('0'+i);
  262. Serial_SendByte('c');
  263. Serial_SendByte('\r');
  264. Delay_MS(1);
  265. remote_receive_string(1);
  266. }
  267. }
  268. void remote_get_sys_voltage(void) {
  269. gfx_clear();
  270. empty_serial();
  271. term_x = 0;
  272. term_y = 0;
  273. Serial_SendByte('V');
  274. Serial_SendByte('\r');
  275. Delay_MS(1);
  276. remote_receive_string(1);
  277. Serial_SendByte('a');
  278. Serial_SendByte('\r');
  279. Delay_MS(1);
  280. remote_receive_string(1);
  281. Serial_SendByte('C');
  282. Serial_SendByte('\r');
  283. Delay_MS(1);
  284. remote_receive_string(1);
  285. }
  286. int oledbrt=0;
  287. void oled_brightness_inc(void) {
  288. oledbrt+=10;
  289. if (oledbrt>=0xff) oledbrt = 0xff;
  290. iota_gfx_contrast(oledbrt);
  291. }
  292. void oled_brightness_dec(void) {
  293. oledbrt-=10;
  294. if (oledbrt<0) oledbrt = 0;
  295. iota_gfx_contrast(oledbrt);
  296. }
  297. int16_t pwmval = 8;
  298. void kbd_brightness_init(void) {
  299. // initial brightness
  300. OCR0A = pwmval;
  301. // clear/set, WGM1:0 set (Phase correct PWM)
  302. TCCR0A = (1 << 7) | (0 << 6) | (0<<1) | 1;
  303. // 3=WGM02, (cs02 2:0 -> clock/256 = 100)
  304. TCCR0B = /*(1 << 3) |*/ (1 << 0) | (0 << 1) | 1;
  305. }
  306. void kbd_brightness_inc(void) {
  307. pwmval+=2;
  308. if (pwmval>=10) pwmval = 10;
  309. OCR0A = pwmval;
  310. gfx_poke(0,4,'0'+pwmval/2);
  311. iota_gfx_flush();
  312. }
  313. void kbd_brightness_dec(void) {
  314. pwmval-=2;
  315. if (pwmval<0) pwmval = 0;
  316. OCR0A = pwmval;
  317. gfx_poke(0,4,'0'+pwmval/2);
  318. iota_gfx_flush();
  319. }
  320. void remote_turn_on_som(void) {
  321. gfx_clear();
  322. empty_serial();
  323. term_x = 0;
  324. term_y = 0;
  325. Serial_SendByte('1');
  326. Serial_SendByte('p');
  327. Serial_SendByte('\r');
  328. Delay_MS(1);
  329. empty_serial();
  330. //remote_receive_string();
  331. anim_hello();
  332. kbd_brightness_init();
  333. }
  334. void remote_turn_off_som(void) {
  335. anim_goodbye();
  336. empty_serial();
  337. term_x = 0;
  338. term_y = 0;
  339. Serial_SendByte('0');
  340. Serial_SendByte('p');
  341. Serial_SendByte('\r');
  342. Delay_MS(1);
  343. empty_serial();
  344. //remote_receive_string();
  345. }
  346. char metaPressed = 0;
  347. uint8_t lastMetaKey = 0;
  348. #define DEBOUNCE_CYCLES 5
  349. void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardReport) {
  350. uint8_t metaPressedNow = 0;
  351. // how many keys are pressed this round
  352. uint8_t usedKeyCodes = 0;
  353. uint8_t totalPressed = 0;
  354. // pull ROWs low one after the other
  355. for (int y=0; y<6; y++) {
  356. switch (y) {
  357. case 0: output_low(PORTB, 6); break;
  358. case 1: output_low(PORTB, 5); break;
  359. case 2: output_low(PORTB, 4); break;
  360. case 3: output_low(PORTD, 7); break;
  361. case 4: output_low(PORTD, 6); break;
  362. case 5: output_low(PORTD, 4); break;
  363. }
  364. // wait for signal to stabilize
  365. _delay_us(10);
  366. // check input COLs
  367. for (int x=0; x<14; x++) {
  368. uint16_t loc = y*15+x;
  369. uint16_t keycode = matrix[loc];
  370. uint8_t pressed = 0;
  371. // column pins are all over the place
  372. switch (x) {
  373. case 0: pressed = !(PIND&(1<<5)); break;
  374. case 1: pressed = !(PINF&(1<<7)); break;
  375. case 2: pressed = !(PINE&(1<<6)); break;
  376. case 3: pressed = !(PINC&(1<<7)); break;
  377. case 4: pressed = !(PINB&(1<<3)); break;
  378. case 5: pressed = !(PINB&(1<<2)); break;
  379. case 6: pressed = !(PINB&(1<<1)); break;
  380. case 7: pressed = !(PINB&(1<<0)); break;
  381. case 8: pressed = !(PINF&(1<<0)); break;
  382. case 9: pressed = !(PINF&(1<<1)); break;
  383. case 10: pressed = !(PINF&(1<<4)); break;
  384. case 11: pressed = !(PINF&(1<<5)); break;
  385. case 12: pressed = !(PINF&(1<<6)); break;
  386. case 13: pressed = !(PINC&(1<<6)); break;
  387. }
  388. if (pressed || matrix_debounce[loc] > 0) {
  389. if (matrix_debounce[loc] > 0) {
  390. matrix_debounce[loc]--;
  391. } else if (pressed) {
  392. matrix_debounce[loc] = DEBOUNCE_CYCLES;
  393. }
  394. totalPressed++;
  395. if (keycode == HID_KEYBOARD_SC_EXSEL) {
  396. metaPressedNow = 1;
  397. } else {
  398. // 6 keys is a hard limit in the HID descriptor :/
  399. if (usb_report_mode && KeyboardReport && !metaPressed && usedKeyCodes<6) {
  400. KeyboardReport->KeyCode[usedKeyCodes++] = keycode;
  401. }
  402. if (metaPressed && lastMetaKey!=keycode) {
  403. if (keycode == KEY_0) {
  404. remote_turn_off_som();
  405. }
  406. else if (keycode == KEY_1) {
  407. remote_turn_on_som();
  408. }
  409. else if (keycode == KEY_V) {
  410. remote_get_voltages();
  411. }
  412. else if (keycode == KEY_C) {
  413. remote_get_cells();
  414. }
  415. else if (keycode == KEY_S) {
  416. remote_get_status();
  417. }
  418. else if (keycode == KEY_Y) {
  419. remote_get_sys_voltage();
  420. }
  421. else if (keycode == KEY_2) {
  422. iota_gfx_off();
  423. }
  424. else if (keycode == KEY_3) {
  425. iota_gfx_on();
  426. }
  427. else if (keycode == KEY_F1) {
  428. kbd_brightness_dec();
  429. }
  430. else if (keycode == KEY_F2) {
  431. kbd_brightness_inc();
  432. }
  433. else if (keycode == KEY_F3) {
  434. oled_brightness_dec();
  435. }
  436. else if (keycode == KEY_F4) {
  437. oled_brightness_inc();
  438. }
  439. lastMetaKey = keycode;
  440. }
  441. }
  442. }
  443. }
  444. switch (y) {
  445. case 0: output_high(PORTB, 6); break;
  446. case 1: output_high(PORTB, 5); break;
  447. case 2: output_high(PORTB, 4); break;
  448. case 3: output_high(PORTD, 7); break;
  449. case 4: output_high(PORTD, 6); break;
  450. case 5: output_high(PORTD, 4); break;
  451. }
  452. }
  453. metaPressed = metaPressedNow;
  454. if (totalPressed<2) lastMetaKey = 0;
  455. }
  456. int main(void)
  457. {
  458. SetupHardware();
  459. GlobalInterruptEnable();
  460. for (;;)
  461. {
  462. process_keyboard(0, NULL);
  463. HID_Device_USBTask(&Keyboard_HID_Interface);
  464. USB_USBTask();
  465. }
  466. }
  467. /** Configures the board hardware and chip peripherals for the demo's functionality. */
  468. void SetupHardware()
  469. {
  470. // Disable watchdog if enabled by bootloader/fuses
  471. MCUSR &= ~(1 << WDRF);
  472. wdt_disable();
  473. // Disable clock division
  474. clock_prescale_set(clock_div_1);
  475. // declare port pins as inputs (0) and outputs (1)
  476. DDRB = 0b11110000;
  477. DDRC = 0b00000000;
  478. DDRD = 0b11011001;
  479. DDRE = 0b00000000;
  480. DDRF = 0b00000000;
  481. // initial pin states
  482. PORTB = 0b10001111;
  483. PORTC = 0b11000000;
  484. PORTD = 0b00100000;
  485. PORTE = 0b01000000;
  486. PORTF = 0b11111111;
  487. // disable JTAG
  488. MCUCR |=(1<<JTD);
  489. MCUCR |=(1<<JTD);
  490. iota_gfx_init(false);
  491. anim_hello();
  492. Serial_Init(57600, false);
  493. kbd_brightness_init();
  494. USB_Init();
  495. }
  496. /** Event handler for the library USB Connection event. */
  497. void EVENT_USB_Device_Connect(void)
  498. {
  499. }
  500. /** Event handler for the library USB Disconnection event. */
  501. void EVENT_USB_Device_Disconnect(void)
  502. {
  503. }
  504. /** Event handler for the library USB Configuration Changed event. */
  505. void EVENT_USB_Device_ConfigurationChanged(void)
  506. {
  507. bool ConfigSuccess = true;
  508. ConfigSuccess &= HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface);
  509. USB_Device_EnableSOFEvents();
  510. }
  511. /** Event handler for the library USB Control Request reception event. */
  512. void EVENT_USB_Device_ControlRequest(void)
  513. {
  514. HID_Device_ProcessControlRequest(&Keyboard_HID_Interface);
  515. }
  516. /** Event handler for the USB device Start Of Frame event. */
  517. void EVENT_USB_Device_StartOfFrame(void)
  518. {
  519. HID_Device_MillisecondElapsed(&Keyboard_HID_Interface);
  520. }
  521. /** HID class driver callback function for the creation of HID reports to the host.
  522. *
  523. * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
  524. * \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
  525. * \param[in] ReportType Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
  526. * \param[out] ReportData Pointer to a buffer where the created report should be stored
  527. * \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent)
  528. *
  529. * \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
  530. */
  531. bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
  532. uint8_t* const ReportID,
  533. const uint8_t ReportType,
  534. void* ReportData,
  535. uint16_t* const ReportSize)
  536. {
  537. USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
  538. process_keyboard(1, KeyboardReport);
  539. *ReportSize = sizeof(USB_KeyboardReport_Data_t);
  540. return false;
  541. }
  542. /** HID class driver callback function for the processing of HID reports from the host.
  543. *
  544. * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
  545. * \param[in] ReportID Report ID of the received report from the host
  546. * \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
  547. * \param[in] ReportData Pointer to a buffer where the received report has been stored
  548. * \param[in] ReportSize Size in bytes of the received HID report
  549. */
  550. void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
  551. const uint8_t ReportID,
  552. const uint8_t ReportType,
  553. const void* ReportData,
  554. const uint16_t ReportSize)
  555. {
  556. }