main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /*
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2019 Ha Thach (tinyusb.org)
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. *
  24. */
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include "bsp/board.h"
  29. #include "hardware/gpio.h"
  30. #include "hardware/adc.h"
  31. #include "tusb.h"
  32. #include "usb_descriptors.h"
  33. //--------------------------------------------------------------------+
  34. // MACRO CONSTANT TYPEDEF PROTYPES
  35. //--------------------------------------------------------------------+
  36. /* Blink pattern
  37. * - 250 ms : device not mounted
  38. * - 1000 ms : device mounted
  39. * - 2500 ms : device is suspended
  40. */
  41. enum {
  42. BLINK_NOT_MOUNTED = 250,
  43. BLINK_MOUNTED = 1000,
  44. BLINK_SUSPENDED = 2500,
  45. };
  46. #define J_UP 21
  47. #define J_DOWN 20
  48. #define J_LEFT 19
  49. #define J_RIGHT 18
  50. #define J_FIRE1 8 /* south */
  51. #define J_FIRE2 7 /* north */
  52. #define J_FIRE3 10 /* east */
  53. #define J_FIRE4 6 /* west */
  54. #define J_FIRE5 12 /* TL */
  55. #define J_FIRE6 13 /* TR */
  56. #define J_FIRE7 4 /* TL2 */
  57. #define J_FIRE8 9 /* TR2 */
  58. #define J_START 5
  59. #define J_SELECT 11
  60. #define POT 27 /* Sensitivity / mode change */
  61. #define POT_ADC 1 /* ADC1 */
  62. #define LED_DIGITAL 28
  63. static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
  64. void led_blinking_task(void);
  65. void hid_task(void);
  66. /*------------- MAIN -------------*/
  67. int main(void)
  68. {
  69. board_init();
  70. tusb_init();
  71. while (1)
  72. {
  73. tud_task(); // tinyusb device task
  74. led_blinking_task();
  75. hid_task();
  76. }
  77. return 0;
  78. }
  79. //--------------------------------------------------------------------+
  80. // Device callbacks
  81. //--------------------------------------------------------------------+
  82. // Invoked when device is mounted
  83. void tud_mount_cb(void)
  84. {
  85. blink_interval_ms = BLINK_MOUNTED;
  86. }
  87. // Invoked when device is unmounted
  88. void tud_umount_cb(void)
  89. {
  90. blink_interval_ms = BLINK_NOT_MOUNTED;
  91. }
  92. // Invoked when usb bus is suspended
  93. // remote_wakeup_en : if host allow us to perform remote wakeup
  94. // Within 7ms, device must draw an average of current less than 2.5 mA from bus
  95. void tud_suspend_cb(bool remote_wakeup_en)
  96. {
  97. (void) remote_wakeup_en;
  98. blink_interval_ms = BLINK_SUSPENDED;
  99. }
  100. // Invoked when usb bus is resumed
  101. void tud_resume_cb(void)
  102. {
  103. blink_interval_ms = BLINK_MOUNTED;
  104. }
  105. //--------------------------------------------------------------------+
  106. // USB HID
  107. //--------------------------------------------------------------------+
  108. //
  109. static void send_hid_report(uint8_t report_id, uint32_t btn)
  110. {
  111. (void)btn;
  112. // skip if hid is not ready yet
  113. if ( !tud_hid_ready() ) return;
  114. switch(report_id)
  115. {
  116. case REPORT_ID_GAMEPAD:
  117. {
  118. // use to avoid send multiple consecutive zero report for keyboard
  119. static bool has_gamepad_key = false;
  120. static int32_t prog_x = 0, prog_y = 0;
  121. uint32_t pot_val = 0;
  122. uint32_t gpio_mask = gpio_get_all();
  123. hid_gamepad_report_t report =
  124. {
  125. .x = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
  126. .hat = 0, .buttons = 0
  127. };
  128. if ((gpio_mask & (1<<J_UP)) == 0) {
  129. report.y = -1;
  130. } else if ((gpio_mask & (1<<J_DOWN)) == 0) {
  131. report.y = 1;
  132. }
  133. if ((gpio_mask & (1<<J_RIGHT)) == 0) {
  134. report.x = 1;
  135. } else if ((gpio_mask & (1<<J_LEFT)) == 0) {
  136. report.x = -1;
  137. }
  138. adc_select_input(POT_ADC);
  139. pot_val = adc_read();
  140. if (pot_val < 4000) {
  141. if (pot_val > 32) {
  142. /* Tune intensity */
  143. report.x *= (pot_val >> 5);
  144. report.y *= (pot_val >> 5);
  145. }
  146. gpio_put(LED_DIGITAL, 0);
  147. } else {
  148. /* Digital mode */
  149. report.x *= 127;
  150. report.y *= 127;
  151. gpio_put(LED_DIGITAL, 1);
  152. }
  153. report.buttons = 0;
  154. if ((gpio_mask & (1<<J_FIRE1)) == 0) {
  155. report.buttons |= GAMEPAD_BUTTON_SOUTH;
  156. }
  157. if ((gpio_mask & (1<<J_FIRE2)) == 0) {
  158. report.buttons |= GAMEPAD_BUTTON_NORTH;
  159. }
  160. if ((gpio_mask & (1<<J_FIRE3)) == 0) {
  161. report.buttons |= GAMEPAD_BUTTON_EAST;
  162. }
  163. if ((gpio_mask & (1<<J_FIRE4)) == 0) {
  164. report.buttons |= GAMEPAD_BUTTON_WEST;
  165. }
  166. if ((gpio_mask & (1<<J_FIRE5)) == 0) {
  167. report.buttons |= GAMEPAD_BUTTON_TL;
  168. }
  169. if ((gpio_mask & (1<<J_FIRE6)) == 0) {
  170. report.buttons |= GAMEPAD_BUTTON_TR;
  171. }
  172. if ((gpio_mask & (1<<J_FIRE7)) == 0) {
  173. report.buttons |= GAMEPAD_BUTTON_TL2;
  174. }
  175. if ((gpio_mask & (1<<J_FIRE8)) == 0) {
  176. report.buttons |= GAMEPAD_BUTTON_TR2;
  177. }
  178. if ((gpio_mask & (1<<J_START)) == 0) {
  179. report.buttons |= GAMEPAD_BUTTON_START;
  180. }
  181. if ((gpio_mask & (1<<J_SELECT)) == 0) {
  182. report.buttons |= GAMEPAD_BUTTON_SELECT;
  183. }
  184. tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
  185. }
  186. break;
  187. }
  188. }
  189. // Every 5ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
  190. // tud_hid_report_complete_cb() is used to send the next report after previous one is complete
  191. void hid_task(void)
  192. {
  193. // Poll every 10ms
  194. const uint32_t interval_ms = 5;
  195. static uint32_t start_ms = 0;
  196. static int gpio_initialized = 0;
  197. if (!gpio_initialized) {
  198. /* Set up GPIO */
  199. gpio_init(J_UP);
  200. gpio_init(J_DOWN);
  201. gpio_init(J_LEFT);
  202. gpio_init(J_RIGHT);
  203. gpio_init(J_FIRE1);
  204. gpio_init(J_FIRE2);
  205. gpio_init(J_FIRE3);
  206. gpio_init(J_FIRE4);
  207. gpio_init(J_FIRE5);
  208. gpio_init(J_FIRE6);
  209. gpio_init(J_FIRE7);
  210. gpio_init(J_FIRE8);
  211. gpio_init(J_START);
  212. gpio_init(J_SELECT);
  213. gpio_init(POT);
  214. gpio_init(LED_DIGITAL);
  215. gpio_set_dir(J_UP, GPIO_IN);
  216. gpio_set_dir(J_DOWN, GPIO_IN);
  217. gpio_set_dir(J_LEFT, GPIO_IN);
  218. gpio_set_dir(J_RIGHT, GPIO_IN);
  219. gpio_set_dir(J_FIRE1, GPIO_IN);
  220. gpio_set_dir(J_FIRE2, GPIO_IN);
  221. gpio_set_dir(J_FIRE3, GPIO_IN);
  222. gpio_set_dir(J_FIRE4, GPIO_IN);
  223. gpio_set_dir(J_FIRE5, GPIO_IN);
  224. gpio_set_dir(J_FIRE6, GPIO_IN);
  225. gpio_set_dir(J_FIRE7, GPIO_IN);
  226. gpio_set_dir(J_FIRE8, GPIO_IN);
  227. gpio_set_dir(J_START, GPIO_IN);
  228. gpio_set_dir(J_SELECT, GPIO_IN);
  229. gpio_set_dir(LED_DIGITAL, GPIO_OUT);
  230. adc_init();
  231. adc_gpio_init(POT);
  232. gpio_pull_up(J_UP);
  233. gpio_pull_up(J_DOWN);
  234. gpio_pull_up(J_LEFT);
  235. gpio_pull_up(J_RIGHT);
  236. gpio_pull_up(J_FIRE1);
  237. gpio_pull_up(J_FIRE2);
  238. gpio_pull_up(J_FIRE3);
  239. gpio_pull_up(J_FIRE4);
  240. gpio_pull_up(J_FIRE5);
  241. gpio_pull_up(J_FIRE6);
  242. gpio_pull_up(J_FIRE7);
  243. gpio_pull_up(J_FIRE8);
  244. gpio_pull_up(J_START);
  245. gpio_pull_up(J_SELECT);
  246. gpio_initialized++;
  247. }
  248. if ( board_millis() - start_ms < interval_ms) return; // not enough time
  249. start_ms += interval_ms;
  250. // Remote wakeup
  251. if ( tud_suspended())
  252. {
  253. // Wake up host if we are in suspend mode
  254. // and REMOTE_WAKEUP feature is enabled by host
  255. tud_remote_wakeup();
  256. }else
  257. {
  258. // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
  259. send_hid_report(REPORT_ID_GAMEPAD, 0);
  260. }
  261. }
  262. // Invoked when sent REPORT successfully to host
  263. // Application can use this to send the next report
  264. // Note: For composite reports, report[0] is report ID
  265. void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len)
  266. {
  267. (void) instance;
  268. (void) len;
  269. (void) report;
  270. }
  271. // Invoked when received GET_REPORT control request
  272. // Application must fill buffer report's content and return its length.
  273. // Return zero will cause the stack to STALL request
  274. uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
  275. {
  276. // TODO not Implemented
  277. (void) instance;
  278. (void) report_id;
  279. (void) report_type;
  280. (void) buffer;
  281. (void) reqlen;
  282. return 0;
  283. }
  284. // Invoked when received SET_REPORT control request or
  285. // received data on OUT endpoint ( Report ID = 0, Type = 0 )
  286. void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
  287. {
  288. (void) instance;
  289. if (report_type == HID_REPORT_TYPE_OUTPUT)
  290. {
  291. // Set keyboard LED e.g Capslock, Numlock etc...
  292. if (report_id == REPORT_ID_KEYBOARD)
  293. {
  294. // bufsize should be (at least) 1
  295. if ( bufsize < 1 ) return;
  296. uint8_t const kbd_leds = buffer[0];
  297. if (kbd_leds & KEYBOARD_LED_CAPSLOCK)
  298. {
  299. // Capslock On: disable blink, turn led on
  300. blink_interval_ms = 0;
  301. board_led_write(true);
  302. }else
  303. {
  304. // Caplocks Off: back to normal blink
  305. board_led_write(false);
  306. blink_interval_ms = BLINK_MOUNTED;
  307. }
  308. }
  309. }
  310. }
  311. //--------------------------------------------------------------------+
  312. // BLINKING TASK
  313. //--------------------------------------------------------------------+
  314. void led_blinking_task(void)
  315. {
  316. static uint32_t start_ms = 0;
  317. static bool led_state = false;
  318. // blink is disabled
  319. if (!blink_interval_ms) return;
  320. // Blink every interval ms
  321. if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
  322. start_ms += blink_interval_ms;
  323. board_led_write(led_state);
  324. led_state = 1 - led_state; // toggle
  325. }