main.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2020 Reinhard Panhuber
  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. /* plot_audio_samples.py requires following modules:
  26. * $ sudo apt install libportaudio
  27. * $ pip3 install sounddevice matplotlib
  28. *
  29. * Then run
  30. * $ python3 plot_audio_samples.py
  31. */
  32. #include <stdlib.h>
  33. #include <stdio.h>
  34. #include <string.h>
  35. #include "bsp/board.h"
  36. #include "tusb.h"
  37. //--------------------------------------------------------------------+
  38. // MACRO CONSTANT TYPEDEF PROTYPES
  39. //--------------------------------------------------------------------+
  40. #ifndef AUDIO_SAMPLE_RATE
  41. #define AUDIO_SAMPLE_RATE 48000
  42. #endif
  43. /* Blink pattern
  44. * - 250 ms : device not mounted
  45. * - 1000 ms : device mounted
  46. * - 2500 ms : device is suspended
  47. */
  48. enum {
  49. BLINK_NOT_MOUNTED = 250,
  50. BLINK_MOUNTED = 1000,
  51. BLINK_SUSPENDED = 2500,
  52. };
  53. static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
  54. // Audio controls
  55. // Current states
  56. bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
  57. uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
  58. uint32_t sampFreq;
  59. uint8_t clkValid;
  60. // Range states
  61. audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
  62. audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
  63. // Audio test data
  64. uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ/2]; // Ensure half word aligned
  65. void led_blinking_task(void);
  66. void audio_task(void);
  67. /*------------- MAIN -------------*/
  68. int main(void)
  69. {
  70. board_init();
  71. tusb_init();
  72. // Init values
  73. sampFreq = AUDIO_SAMPLE_RATE;
  74. clkValid = 1;
  75. sampleFreqRng.wNumSubRanges = 1;
  76. sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
  77. sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
  78. sampleFreqRng.subrange[0].bRes = 0;
  79. while (1)
  80. {
  81. tud_task(); // tinyusb device task
  82. led_blinking_task();
  83. audio_task();
  84. }
  85. return 0;
  86. }
  87. //--------------------------------------------------------------------+
  88. // Device callbacks
  89. //--------------------------------------------------------------------+
  90. // Invoked when device is mounted
  91. void tud_mount_cb(void)
  92. {
  93. blink_interval_ms = BLINK_MOUNTED;
  94. }
  95. // Invoked when device is unmounted
  96. void tud_umount_cb(void)
  97. {
  98. blink_interval_ms = BLINK_NOT_MOUNTED;
  99. }
  100. // Invoked when usb bus is suspended
  101. // remote_wakeup_en : if host allow us to perform remote wakeup
  102. // Within 7ms, device must draw an average of current less than 2.5 mA from bus
  103. void tud_suspend_cb(bool remote_wakeup_en)
  104. {
  105. (void) remote_wakeup_en;
  106. blink_interval_ms = BLINK_SUSPENDED;
  107. }
  108. // Invoked when usb bus is resumed
  109. void tud_resume_cb(void)
  110. {
  111. blink_interval_ms = BLINK_MOUNTED;
  112. }
  113. //--------------------------------------------------------------------+
  114. // AUDIO Task
  115. //--------------------------------------------------------------------+
  116. void audio_task(void)
  117. {
  118. // Yet to be filled - e.g. put meas data into TX FIFOs etc.
  119. asm("nop");
  120. }
  121. //--------------------------------------------------------------------+
  122. // Application Callback API Implementations
  123. //--------------------------------------------------------------------+
  124. // Invoked when audio class specific set request received for an EP
  125. bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
  126. {
  127. (void) rhport;
  128. (void) pBuff;
  129. // We do not support any set range requests here, only current value requests
  130. TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
  131. // Page 91 in UAC2 specification
  132. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  133. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  134. uint8_t ep = TU_U16_LOW(p_request->wIndex);
  135. (void) channelNum; (void) ctrlSel; (void) ep;
  136. return false; // Yet not implemented
  137. }
  138. // Invoked when audio class specific set request received for an interface
  139. bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
  140. {
  141. (void) rhport;
  142. (void) pBuff;
  143. // We do not support any set range requests here, only current value requests
  144. TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
  145. // Page 91 in UAC2 specification
  146. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  147. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  148. uint8_t itf = TU_U16_LOW(p_request->wIndex);
  149. (void) channelNum; (void) ctrlSel; (void) itf;
  150. return false; // Yet not implemented
  151. }
  152. // Invoked when audio class specific set request received for an entity
  153. bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
  154. {
  155. (void) rhport;
  156. // Page 91 in UAC2 specification
  157. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  158. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  159. uint8_t itf = TU_U16_LOW(p_request->wIndex);
  160. uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
  161. (void) itf;
  162. // We do not support any set range requests here, only current value requests
  163. TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
  164. // If request is for our feature unit
  165. if ( entityID == 2 )
  166. {
  167. switch ( ctrlSel )
  168. {
  169. case AUDIO_FU_CTRL_MUTE:
  170. // Request uses format layout 1
  171. TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
  172. mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
  173. TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
  174. return true;
  175. case AUDIO_FU_CTRL_VOLUME:
  176. // Request uses format layout 2
  177. TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
  178. volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
  179. TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
  180. return true;
  181. // Unknown/Unsupported control
  182. default:
  183. TU_BREAKPOINT();
  184. return false;
  185. }
  186. }
  187. return false; // Yet not implemented
  188. }
  189. // Invoked when audio class specific get request received for an EP
  190. bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
  191. {
  192. (void) rhport;
  193. // Page 91 in UAC2 specification
  194. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  195. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  196. uint8_t ep = TU_U16_LOW(p_request->wIndex);
  197. (void) channelNum; (void) ctrlSel; (void) ep;
  198. // return tud_control_xfer(rhport, p_request, &tmp, 1);
  199. return false; // Yet not implemented
  200. }
  201. // Invoked when audio class specific get request received for an interface
  202. bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
  203. {
  204. (void) rhport;
  205. // Page 91 in UAC2 specification
  206. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  207. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  208. uint8_t itf = TU_U16_LOW(p_request->wIndex);
  209. (void) channelNum; (void) ctrlSel; (void) itf;
  210. return false; // Yet not implemented
  211. }
  212. // Invoked when audio class specific get request received for an entity
  213. bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
  214. {
  215. (void) rhport;
  216. // Page 91 in UAC2 specification
  217. uint8_t channelNum = TU_U16_LOW(p_request->wValue);
  218. uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
  219. // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since we have only one audio function implemented, we do not need the itf value
  220. uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
  221. // Input terminal (Microphone input)
  222. if (entityID == 1)
  223. {
  224. switch ( ctrlSel )
  225. {
  226. case AUDIO_TE_CTRL_CONNECTOR:
  227. {
  228. // The terminal connector control only has a get request with only the CUR attribute.
  229. audio_desc_channel_cluster_t ret;
  230. // Those are dummy values for now
  231. ret.bNrChannels = 1;
  232. ret.bmChannelConfig = 0;
  233. ret.iChannelNames = 0;
  234. TU_LOG2(" Get terminal connector\r\n");
  235. return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
  236. }
  237. break;
  238. // Unknown/Unsupported control selector
  239. default:
  240. TU_BREAKPOINT();
  241. return false;
  242. }
  243. }
  244. // Feature unit
  245. if (entityID == 2)
  246. {
  247. switch ( ctrlSel )
  248. {
  249. case AUDIO_FU_CTRL_MUTE:
  250. // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
  251. // There does not exist a range parameter block for mute
  252. TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
  253. return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
  254. case AUDIO_FU_CTRL_VOLUME:
  255. switch ( p_request->bRequest )
  256. {
  257. case AUDIO_CS_REQ_CUR:
  258. TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
  259. return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
  260. case AUDIO_CS_REQ_RANGE:
  261. TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
  262. // Copy values - only for testing - better is version below
  263. audio_control_range_2_n_t(1)
  264. ret;
  265. ret.wNumSubRanges = 1;
  266. ret.subrange[0].bMin = -90; // -90 dB
  267. ret.subrange[0].bMax = 90; // +90 dB
  268. ret.subrange[0].bRes = 1; // 1 dB steps
  269. return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
  270. // Unknown/Unsupported control
  271. default:
  272. TU_BREAKPOINT();
  273. return false;
  274. }
  275. break;
  276. // Unknown/Unsupported control
  277. default:
  278. TU_BREAKPOINT();
  279. return false;
  280. }
  281. }
  282. // Clock Source unit
  283. if ( entityID == 4 )
  284. {
  285. switch ( ctrlSel )
  286. {
  287. case AUDIO_CS_CTRL_SAM_FREQ:
  288. // channelNum is always zero in this case
  289. switch ( p_request->bRequest )
  290. {
  291. case AUDIO_CS_REQ_CUR:
  292. TU_LOG2(" Get Sample Freq.\r\n");
  293. return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
  294. case AUDIO_CS_REQ_RANGE:
  295. TU_LOG2(" Get Sample Freq. range\r\n");
  296. return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
  297. // Unknown/Unsupported control
  298. default:
  299. TU_BREAKPOINT();
  300. return false;
  301. }
  302. break;
  303. case AUDIO_CS_CTRL_CLK_VALID:
  304. // Only cur attribute exists for this request
  305. TU_LOG2(" Get Sample Freq. valid\r\n");
  306. return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
  307. // Unknown/Unsupported control
  308. default:
  309. TU_BREAKPOINT();
  310. return false;
  311. }
  312. }
  313. TU_LOG2(" Unsupported entity: %d\r\n", entityID);
  314. return false; // Yet not implemented
  315. }
  316. bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
  317. {
  318. (void) rhport;
  319. (void) itf;
  320. (void) ep_in;
  321. (void) cur_alt_setting;
  322. for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
  323. {
  324. tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
  325. }
  326. return true;
  327. }
  328. bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
  329. {
  330. (void) rhport;
  331. (void) n_bytes_copied;
  332. (void) itf;
  333. (void) ep_in;
  334. (void) cur_alt_setting;
  335. uint16_t dataVal;
  336. // Generate dummy data
  337. for (uint16_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
  338. {
  339. uint16_t * p_buff = i2s_dummy_buffer[cnt]; // 2 bytes per sample
  340. dataVal = 1;
  341. for (uint16_t cnt2 = 0; cnt2 < AUDIO_SAMPLE_RATE/1000; cnt2++)
  342. {
  343. for (uint8_t cnt3 = 0; cnt3 < CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX; cnt3++)
  344. {
  345. *p_buff++ = dataVal;
  346. }
  347. dataVal++;
  348. }
  349. }
  350. return true;
  351. }
  352. bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
  353. {
  354. (void) rhport;
  355. (void) p_request;
  356. return true;
  357. }
  358. //--------------------------------------------------------------------+
  359. // BLINKING TASK
  360. //--------------------------------------------------------------------+
  361. void led_blinking_task(void)
  362. {
  363. static uint32_t start_ms = 0;
  364. static bool led_state = false;
  365. // Blink every interval ms
  366. if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
  367. start_ms += blink_interval_ms;
  368. board_led_write(led_state);
  369. led_state = 1 - led_state; // toggle
  370. }