audio_4_channel_mic_freertos : merge changes from audio_4_channel_mic.
This commit is contained in:
		| @@ -34,6 +34,7 @@ | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <math.h> | ||||
|  | ||||
| #include "bsp/board_api.h" | ||||
| #include "tusb.h" | ||||
| @@ -60,13 +61,13 @@ | ||||
|   #define USBD_STACK_SIZE    (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) | ||||
| #endif | ||||
|  | ||||
| #define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE | ||||
| #define AUDIO_STACK_SIZE    configMINIMAL_STACK_SIZE | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| #ifndef AUDIO_SAMPLE_RATE | ||||
| #define AUDIO_SAMPLE_RATE   48000 | ||||
| #endif | ||||
| #define AUDIO_SAMPLE_RATE   CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE | ||||
|  | ||||
| /* Blink pattern | ||||
|  * - 250 ms  : device not mounted | ||||
| @@ -79,12 +80,19 @@ enum  { | ||||
|   BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
|  | ||||
| // static timer | ||||
| StaticTimer_t blinky_tmdef; | ||||
| TimerHandle_t blinky_tm; | ||||
| // static task | ||||
| #if configSUPPORT_STATIC_ALLOCATION | ||||
| StackType_t  blinky_stack[BLINKY_STACK_SIZE]; | ||||
| StaticTask_t blinky_taskdef; | ||||
|  | ||||
| StackType_t  usb_device_stack[USBD_STACK_SIZE]; | ||||
| StaticTask_t usb_device_taskdef; | ||||
|  | ||||
| StackType_t  audio_stack[AUDIO_STACK_SIZE]; | ||||
| StaticTask_t audio_taskdef; | ||||
| #endif | ||||
|  | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
|  | ||||
| // Audio controls | ||||
| // Current states | ||||
| @@ -97,23 +105,23 @@ uint8_t clkValid; | ||||
| audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; 			// Volume range state | ||||
| audio_control_range_4_n_t(1) sampleFreqRng; 						// Sample frequency range state | ||||
|  | ||||
| // Audio test data | ||||
| uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_EP_SZ_IN/2];   // Ensure half word aligned | ||||
| uint16_t samples[] = {0, 0, 0, 0}; | ||||
| #if CFG_TUD_AUDIO_ENABLE_ENCODING | ||||
| // Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2 | ||||
| uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; | ||||
| #else | ||||
| // Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3 | ||||
| uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000]; | ||||
| #endif | ||||
|  | ||||
| void led_blinky_cb(TimerHandle_t xTimer); | ||||
| void led_blinking_task(void* param); | ||||
| void usb_device_task(void* param); | ||||
| void audio_task(void); | ||||
| void audio_task(void* param); | ||||
|  | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) | ||||
| { | ||||
|   board_init(); | ||||
|  | ||||
|   // soft timer for blinky | ||||
|   blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef); | ||||
|   xTimerStart(blinky_tm, 0); | ||||
|  | ||||
|   // Init values | ||||
|   sampFreq = AUDIO_SAMPLE_RATE; | ||||
|   clkValid = 1; | ||||
| @@ -123,11 +131,58 @@ int main(void) | ||||
|   sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE; | ||||
|   sampleFreqRng.subrange[0].bRes = 0; | ||||
|  | ||||
|   // Generate dummy data | ||||
| #if CFG_TUD_AUDIO_ENABLE_ENCODING | ||||
|   uint16_t * p_buff = i2s_dummy_buffer[0]; | ||||
|   uint16_t dataVal = 0; | ||||
|   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) | ||||
|   { | ||||
|     // CH0 saw wave | ||||
|     *p_buff++ = dataVal; | ||||
|     // CH1 inverted saw wave | ||||
|     *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; | ||||
|     dataVal+= 32; | ||||
|   } | ||||
|   p_buff = i2s_dummy_buffer[1]; | ||||
|   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) | ||||
|   { | ||||
|     // CH3 square wave | ||||
|     *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; | ||||
|     // CH4 sinus wave | ||||
|     float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); | ||||
|     *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); | ||||
|   } | ||||
| #else | ||||
|   uint16_t * p_buff = i2s_dummy_buffer; | ||||
|   uint16_t dataVal = 0; | ||||
|   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) | ||||
|   { | ||||
|     // CH0 saw wave | ||||
|     *p_buff++ = dataVal; | ||||
|     // CH1 inverted saw wave | ||||
|     *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; | ||||
|     dataVal+= 32; | ||||
|     // CH3 square wave | ||||
|     *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; | ||||
|     // CH4 sinus wave | ||||
|     float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); | ||||
|     *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #if configSUPPORT_STATIC_ALLOCATION | ||||
|   // blinky task | ||||
|   xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); | ||||
|  | ||||
|   // Create a task for tinyusb device stack | ||||
|   xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); | ||||
|  | ||||
|    // Create a task for audio | ||||
|   xTaskCreateStatic(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES-1, audio_stack, &audio_taskdef); | ||||
| #else | ||||
|   xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); | ||||
|   xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); | ||||
|   xTaskCreate(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); | ||||
| #endif | ||||
|  | ||||
|   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 | ||||
| @@ -175,13 +230,13 @@ void usb_device_task(void* param) | ||||
| // Invoked when device is mounted | ||||
| void tud_mount_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0); | ||||
|   blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
|  | ||||
| // Invoked when device is unmounted | ||||
| void tud_umount_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0); | ||||
|   blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is suspended | ||||
| @@ -190,23 +245,36 @@ void tud_umount_cb(void) | ||||
| void tud_suspend_cb(bool remote_wakeup_en) | ||||
| { | ||||
|   (void) remote_wakeup_en; | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0); | ||||
|   blink_interval_ms = BLINK_SUSPENDED; | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is resumed | ||||
| void tud_resume_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0); | ||||
|   blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // AUDIO Task | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| void audio_task(void) | ||||
| void audio_task(void* param) | ||||
| { | ||||
|   // Yet to be filled - e.g. put meas data into TX FIFOs etc. | ||||
|   // asm("nop"); | ||||
|   (void) param; | ||||
|   // Yet to be filled - e.g. read audio from I2S buffer. | ||||
|   // Here we simulate a I2S receive callback every 1ms. | ||||
|   while (1) { | ||||
|     vTaskDelay(1); | ||||
| #if CFG_TUD_AUDIO_ENABLE_ENCODING | ||||
|   // Write I2S buffer into FIFO | ||||
|     for (uint8_t cnt=0; cnt < 2; cnt++) | ||||
|     { | ||||
|       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); | ||||
|     } | ||||
| #else | ||||
|     tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX); | ||||
| #endif | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| @@ -427,7 +495,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * | ||||
|         { | ||||
|           case AUDIO_CS_REQ_CUR: | ||||
|             TU_LOG2("    Get Sample Freq.\r\n"); | ||||
|             return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); | ||||
|             // Buffered control transfer is needed for IN flow control to work | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); | ||||
|  | ||||
|           case AUDIO_CS_REQ_RANGE: | ||||
|             TU_LOG2("    Get Sample Freq. range\r\n"); | ||||
| @@ -463,7 +532,14 @@ bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, u | ||||
|   (void) ep_in; | ||||
|   (void) cur_alt_setting; | ||||
|  | ||||
|   tud_audio_write((uint8_t*)i2s_dummy_buffer, CFG_TUD_AUDIO_EP_SZ_IN); | ||||
|  | ||||
|   // In read world application data flow is driven by I2S clock, | ||||
|   // both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used. | ||||
|   // For example in your I2S receive callback: | ||||
|   // void I2S_Rx_Callback(int channel, const void* data, uint16_t samples) | ||||
|   // { | ||||
|   //    tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO); | ||||
|   // } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
| @@ -476,14 +552,6 @@ bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uin | ||||
|   (void) ep_in; | ||||
|   (void) cur_alt_setting; | ||||
|  | ||||
|   uint16_t* p_buff = i2s_dummy_buffer; | ||||
|   for (int samples_num = 0; samples_num < AUDIO_SAMPLE_RATE/1000; samples_num++) { | ||||
|     for (int ch=0; ch < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX; ch++) { | ||||
|       *p_buff++ = samples[ch]; | ||||
|       samples[ch] = samples[ch]+(ch+1); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| @@ -498,11 +566,17 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const | ||||
| ///--------------------------------------------------------------------+ | ||||
| // BLINKING TASK | ||||
| //--------------------------------------------------------------------+ | ||||
| void led_blinky_cb(TimerHandle_t xTimer) | ||||
| { | ||||
|   (void) xTimer; | ||||
| void led_blinking_task(void* param) { | ||||
|   (void) param; | ||||
|   static uint32_t start_ms = 0; | ||||
|   static bool led_state = false; | ||||
|  | ||||
|   board_led_write(led_state); | ||||
|   led_state = 1 - led_state; // toggle | ||||
|   while (1) { | ||||
|     // Blink every interval ms | ||||
|     vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); | ||||
|     start_ms += blink_interval_ms; | ||||
|  | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; // toggle | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -109,24 +109,37 @@ extern "C" { | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| // Have a look into audio_device.h for all configurations | ||||
| #define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE              48000 | ||||
|  | ||||
| #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN                                 TUD_AUDIO_MIC_FOUR_CH_DESC_LEN | ||||
| #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN                 TUD_AUDIO_MIC_FOUR_CH_DESC_LEN | ||||
|  | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT                                 1 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ                              64 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT                 1 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ              64 | ||||
|  | ||||
| #define CFG_TUD_AUDIO_ENABLE_EP_IN                                    1 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX                    2         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX                            4         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup | ||||
| #define CFG_TUD_AUDIO_EP_SZ_IN                                        48 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX      // 48 Samples (48 kHz) x 2 Bytes/Sample x CFG_TUD_AUDIO_N_CHANNELS_TX Channels - the Windows driver always needs an extra sample per channel of space more, otherwise it complains... found by trial and error | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX                             CFG_TUD_AUDIO_EP_SZ_IN | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ                          CFG_TUD_AUDIO_EP_SZ_IN | ||||
| #define CFG_TUD_AUDIO_ENABLE_EP_IN                    1 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX    2         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX            4         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup | ||||
| #define CFG_TUD_AUDIO_EP_SZ_IN                        TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) | ||||
|  | ||||
| #define CFG_TUD_AUDIO_ENABLE_ENCODING                                 0 | ||||
| #define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING                          0 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX                      2         // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO                        (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX) | ||||
| #define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ                       (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) | ||||
| #define CFG_TUD_AUDIO_ENABLE_ENCODING                 1 | ||||
| #define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL              1 | ||||
|  | ||||
| #if CFG_TUD_AUDIO_ENABLE_ENCODING | ||||
|  | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX             CFG_TUD_AUDIO_EP_SZ_IN | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ          CFG_TUD_AUDIO_EP_SZ_IN | ||||
|  | ||||
| #define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING          1 | ||||
| #define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX      2         // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value | ||||
| #define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO        (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX) | ||||
| #define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ       (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device | ||||
|  | ||||
| #else | ||||
|  | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX             CFG_TUD_AUDIO_EP_SZ_IN | ||||
| #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ          (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "bsp/board_api.h" | ||||
| #include "tusb.h" | ||||
|  | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. | ||||
| @@ -44,7 +45,7 @@ tusb_desc_device_t const desc_device = | ||||
|     .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|     .bcdUSB             = 0x0200, | ||||
|  | ||||
|     // Use Interface Association Descriptor (IAD) for CDC | ||||
|     // Use Interface Association Descriptor (IAD) for Audio | ||||
|     // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) | ||||
|     .bDeviceClass       = TUSB_CLASS_MISC, | ||||
|     .bDeviceSubClass    = MISC_SUBCLASS_COMMON, | ||||
| @@ -96,7 +97,7 @@ enum | ||||
|  | ||||
| uint8_t const desc_configuration[] = | ||||
| { | ||||
|   // Interface count, string index, total length, attribute, power in mA | ||||
|   // Config number, interface count, string index, total length, attribute, power in mA | ||||
|   TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), | ||||
|  | ||||
|   // Interface number, string index, EP Out & EP In address, EP size | ||||
| @@ -116,50 +117,63 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) | ||||
| // String Descriptors | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // array of pointer to string descriptors | ||||
| char const* string_desc_arr [] = | ||||
| { | ||||
|     (const char[]) { 0x09, 0x04 }, 	// 0: is supported language is English (0x0409) | ||||
|     "PaniRCorp",                   	// 1: Manufacturer | ||||
|     "MicNode_4_Ch",    		          // 2: Product | ||||
|     "123458",                      	// 3: Serials, should use chip ID | ||||
|     "UAC2",                     	 	// 4: Audio Interface | ||||
| // String Descriptor Index | ||||
| enum { | ||||
|   STRID_LANGID = 0, | ||||
|   STRID_MANUFACTURER, | ||||
|   STRID_PRODUCT, | ||||
|   STRID_SERIAL, | ||||
| }; | ||||
|  | ||||
| static uint16_t _desc_str[32]; | ||||
| // array of pointer to string descriptors | ||||
| char const* string_desc_arr [] = { | ||||
|     (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) | ||||
|     "PaniRCorp",                   // 1: Manufacturer | ||||
|     "MicNode_4_Ch",                // 2: Product | ||||
|     NULL,                          // 3: Serials will use unique ID if possible | ||||
|     "UAC2",                        // 4: Audio Interface | ||||
| }; | ||||
|  | ||||
| static uint16_t _desc_str[32 + 1]; | ||||
|  | ||||
| // Invoked when received GET STRING DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete | ||||
| uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) | ||||
| { | ||||
| uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { | ||||
|   (void) langid; | ||||
|   size_t chr_count; | ||||
|  | ||||
|   uint8_t chr_count; | ||||
|   switch ( index ) { | ||||
|     case STRID_LANGID: | ||||
|       memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|       chr_count = 1; | ||||
|       break; | ||||
|  | ||||
|   if ( index == 0) | ||||
|   { | ||||
|     memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|     chr_count = 1; | ||||
|   }else | ||||
|   { | ||||
|     // Convert ASCII string into UTF-16 | ||||
|     case STRID_SERIAL: | ||||
|       chr_count = board_usb_get_serial(_desc_str + 1, 32); | ||||
|       break; | ||||
|  | ||||
|     if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; | ||||
|     default: | ||||
|       // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. | ||||
|       // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors | ||||
|  | ||||
|     const char* str = string_desc_arr[index]; | ||||
|       if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; | ||||
|  | ||||
|     // Cap at max char | ||||
|     chr_count = strlen(str); | ||||
|     if ( chr_count > 31 ) chr_count = 31; | ||||
|       const char *str = string_desc_arr[index]; | ||||
|  | ||||
|     for(uint8_t i=0; i<chr_count; i++) | ||||
|     { | ||||
|       _desc_str[1+i] = str[i]; | ||||
|     } | ||||
|       // Cap at max char | ||||
|       chr_count = strlen(str); | ||||
|       size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type | ||||
|       if ( chr_count > max_count ) chr_count = max_count; | ||||
|  | ||||
|       // Convert ASCII string into UTF-16 | ||||
|       for ( size_t i = 0; i < chr_count; i++ ) { | ||||
|         _desc_str[1 + i] = str[i]; | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   // first byte is length (including header), second byte is string type | ||||
|   _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2); | ||||
|   _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); | ||||
|  | ||||
|   return _desc_str; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 HiFiPhile
					HiFiPhile