audio_test_freertos & audio_4_channel_mic_freertos
This commit is contained in:
		 Kasper Nyhus Kaae
					Kasper Nyhus Kaae
				
			
				
					committed by
					
						 HiFiPhile
						HiFiPhile
					
				
			
			
				
	
			
			
			 HiFiPhile
						HiFiPhile
					
				
			
						parent
						
							31b559370d
						
					
				
				
					commit
					c917d47e71
				
			| @@ -0,0 +1,27 @@ | ||||
| idf_component_register(SRCS "main.c" "usb_descriptors.c" | ||||
|   INCLUDE_DIRS "." | ||||
|   REQUIRES freertos soc) | ||||
|  | ||||
| file(TO_NATIVE_PATH "${TOP}/hw/bsp/${FAMILY}/boards/${BOARD}/board.cmake" board_cmake) | ||||
|  | ||||
| if(EXISTS ${board_cmake}) | ||||
|   include(${board_cmake}) | ||||
| endif() | ||||
|  | ||||
| target_include_directories(${COMPONENT_TARGET} PUBLIC | ||||
|   "${TOP}/hw" | ||||
|   "${TOP}/src" | ||||
| ) | ||||
|  | ||||
| target_compile_definitions(${COMPONENT_TARGET} PUBLIC | ||||
|   ESP_PLATFORM | ||||
| ) | ||||
|  | ||||
| target_sources(${COMPONENT_TARGET} PUBLIC | ||||
|   "${TOP}/src/tusb.c" | ||||
|   "${TOP}/src/common/tusb_fifo.c" | ||||
|   "${TOP}/src/device/usbd.c" | ||||
|   "${TOP}/src/device/usbd_control.c" | ||||
|   "${TOP}/src/class/audio/audio_device.c" | ||||
|   "${TOP}/src/portable/espressif/esp32sx/dcd_esp32sx.c" | ||||
| ) | ||||
| @@ -0,0 +1,191 @@ | ||||
| /* | ||||
|  * FreeRTOS Kernel V10.0.0 | ||||
|  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||
|  * this software and associated documentation files (the "Software"), to deal in | ||||
|  * the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||
|  * the Software, and to permit persons to whom the Software is furnished to do so, | ||||
|  * subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. If you wish to use our Amazon | ||||
|  * FreeRTOS name, please do so in a fair use way that does not cause confusion. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||
|  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||
|  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  * | ||||
|  * http://www.FreeRTOS.org | ||||
|  * http://aws.amazon.com/freertos | ||||
|  * | ||||
|  * 1 tab == 4 spaces! | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #ifndef FREERTOS_CONFIG_H | ||||
| #define FREERTOS_CONFIG_H | ||||
|  | ||||
| /*----------------------------------------------------------- | ||||
|  * Application specific definitions. | ||||
|  * | ||||
|  * These definitions should be adjusted for your particular hardware and | ||||
|  * application requirements. | ||||
|  * | ||||
|  * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE | ||||
|  * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. | ||||
|  * | ||||
|  * See http://www.freertos.org/a00110.html. | ||||
|  *----------------------------------------------------------*/ | ||||
|  | ||||
| // Include MCU header | ||||
| #include "bsp/board_mcu.h" | ||||
|  | ||||
| #if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 | ||||
|   #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" | ||||
| #endif | ||||
|  | ||||
| // TODO fix later | ||||
| #if CFG_TUSB_MCU == OPT_MCU_MM32F327X | ||||
|   extern u32 SystemCoreClock; | ||||
| #else | ||||
|   extern uint32_t SystemCoreClock; | ||||
| #endif | ||||
|  | ||||
| /* Cortex M23/M33 port configuration. */ | ||||
| #define configENABLE_MPU								        0 | ||||
| #define configENABLE_FPU								        1 | ||||
| #define configENABLE_TRUSTZONE					        0 | ||||
| #define configMINIMAL_SECURE_STACK_SIZE					( 1024 ) | ||||
|  | ||||
| #define configUSE_PREEMPTION                    1 | ||||
| #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 | ||||
| #define configCPU_CLOCK_HZ                      SystemCoreClock | ||||
| #define configTICK_RATE_HZ                      ( 1000 ) | ||||
| #define configMAX_PRIORITIES                    ( 5 ) | ||||
| #define configMINIMAL_STACK_SIZE                ( 128 ) | ||||
| #define configTOTAL_HEAP_SIZE                   ( 0*1024 ) // dynamic is not used | ||||
| #define configMAX_TASK_NAME_LEN                 16 | ||||
| #define configUSE_16_BIT_TICKS                  0 | ||||
| #define configIDLE_SHOULD_YIELD                 1 | ||||
| #define configUSE_MUTEXES                       1 | ||||
| #define configUSE_RECURSIVE_MUTEXES             1 | ||||
| #define configUSE_COUNTING_SEMAPHORES           1 | ||||
| #define configQUEUE_REGISTRY_SIZE               2 | ||||
| #define configUSE_QUEUE_SETS                    0 | ||||
| #define configUSE_TIME_SLICING                  0 | ||||
| #define configUSE_NEWLIB_REENTRANT              0 | ||||
| #define configENABLE_BACKWARD_COMPATIBILITY     1 | ||||
| #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0 | ||||
|  | ||||
| #define configSUPPORT_STATIC_ALLOCATION         1 | ||||
| #define configSUPPORT_DYNAMIC_ALLOCATION        0 | ||||
|  | ||||
| /* Hook function related definitions. */ | ||||
| #define configUSE_IDLE_HOOK                    0 | ||||
| #define configUSE_TICK_HOOK                    0 | ||||
| #define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning | ||||
| #define configCHECK_FOR_STACK_OVERFLOW         2 | ||||
|  | ||||
| /* Run time and task stats gathering related definitions. */ | ||||
| #define configGENERATE_RUN_TIME_STATS          0 | ||||
| #define configUSE_TRACE_FACILITY               1 // legacy trace | ||||
| #define configUSE_STATS_FORMATTING_FUNCTIONS   0 | ||||
|  | ||||
| /* Co-routine definitions. */ | ||||
| #define configUSE_CO_ROUTINES                  0 | ||||
| #define configMAX_CO_ROUTINE_PRIORITIES        2 | ||||
|  | ||||
| /* Software timer related definitions. */ | ||||
| #define configUSE_TIMERS                       1 | ||||
| #define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2) | ||||
| #define configTIMER_QUEUE_LENGTH               32 | ||||
| #define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE | ||||
|  | ||||
| /* Optional functions - most linkers will remove unused functions anyway. */ | ||||
| #define INCLUDE_vTaskPrioritySet               0 | ||||
| #define INCLUDE_uxTaskPriorityGet              0 | ||||
| #define INCLUDE_vTaskDelete                    0 | ||||
| #define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY | ||||
| #define INCLUDE_xResumeFromISR                 0 | ||||
| #define INCLUDE_vTaskDelayUntil                1 | ||||
| #define INCLUDE_vTaskDelay                     1 | ||||
| #define INCLUDE_xTaskGetSchedulerState         0 | ||||
| #define INCLUDE_xTaskGetCurrentTaskHandle      0 | ||||
| #define INCLUDE_uxTaskGetStackHighWaterMark    0 | ||||
| #define INCLUDE_xTaskGetIdleTaskHandle         0 | ||||
| #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 | ||||
| #define INCLUDE_pcTaskGetTaskName              0 | ||||
| #define INCLUDE_eTaskGetState                  0 | ||||
| #define INCLUDE_xEventGroupSetBitFromISR       0 | ||||
| #define INCLUDE_xTimerPendFunctionCall         0 | ||||
|  | ||||
| /* Define to trap errors during development. */ | ||||
| // Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7 | ||||
| #if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) | ||||
|   #define configASSERT(_exp) \ | ||||
|     do {\ | ||||
|       if ( !(_exp) ) { \ | ||||
|         volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \ | ||||
|         if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \ | ||||
|           taskDISABLE_INTERRUPTS(); \ | ||||
|            __asm("BKPT #0\n"); \ | ||||
|         }\ | ||||
|       }\ | ||||
|     } while(0) | ||||
| #else | ||||
|   #define configASSERT( x ) | ||||
| #endif | ||||
|  | ||||
| #ifdef __RX__ | ||||
| /* Renesas RX series */ | ||||
| #define vSoftwareInterruptISR					INT_Excep_ICU_SWINT | ||||
| #define vTickISR								INT_Excep_CMT0_CMI0 | ||||
| #define configPERIPHERAL_CLOCK_HZ				(configCPU_CLOCK_HZ/2) | ||||
| #define configKERNEL_INTERRUPT_PRIORITY			1 | ||||
| #define configMAX_SYSCALL_INTERRUPT_PRIORITY	4 | ||||
|  | ||||
| #else | ||||
|  | ||||
| /* FreeRTOS hooks to NVIC vectors */ | ||||
| #define xPortPendSVHandler    PendSV_Handler | ||||
| #define xPortSysTickHandler   SysTick_Handler | ||||
| #define vPortSVCHandler       SVC_Handler | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Interrupt nesting behavior configuration. | ||||
| //--------------------------------------------------------------------+ | ||||
| #if defined(__NVIC_PRIO_BITS) | ||||
|   // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h | ||||
| 	#define configPRIO_BITS       __NVIC_PRIO_BITS | ||||
| #elif defined(__ECLIC_INTCTLBITS) | ||||
|   // RISC-V Bumblebee core from nuclei | ||||
|   #define configPRIO_BITS       __ECLIC_INTCTLBITS | ||||
| #else | ||||
|   #error "FreeRTOS configPRIO_BITS to be defined" | ||||
| #endif | ||||
|  | ||||
| /* The lowest interrupt priority that can be used in a call to a "set priority" function. */ | ||||
| #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<<configPRIO_BITS) - 1) | ||||
|  | ||||
| /* The highest interrupt priority that can be used by any interrupt service | ||||
| routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL | ||||
| INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER | ||||
| PRIORITY THAN THIS! (higher priorities are lower numeric values. */ | ||||
| #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	2 | ||||
|  | ||||
| /* Interrupt priorities used by the kernel port layer itself.  These are generic | ||||
| to all Cortex-M ports, and do not rely on any particular library functions. */ | ||||
| #define configKERNEL_INTERRUPT_PRIORITY 		          ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) | ||||
|  | ||||
| /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! | ||||
| See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ | ||||
| #define configMAX_SYSCALL_INTERRUPT_PRIORITY 	        ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif /* __FREERTOS_CONFIG__H */ | ||||
							
								
								
									
										488
									
								
								examples/device/audio_4_channel_mic_freertos/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								examples/device/audio_4_channel_mic_freertos/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,488 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Reinhard Panhuber | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /* plot_audio_samples.py requires following modules: | ||||
|  * $ sudo apt install libportaudio | ||||
|  * $ pip3 install sounddevice matplotlib | ||||
|  * | ||||
|  * Then run | ||||
|  * $ python3 plot_audio_samples.py | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
|  | ||||
| // ESP-IDF need "freertos/" prefix in include path. | ||||
| // CFG_TUSB_OS_INC_PATH should be defined accordingly. | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "freertos/semphr.h" | ||||
| #include "freertos/queue.h" | ||||
| #include "freertos/task.h" | ||||
| #include "freertos/timers.h" | ||||
|  | ||||
| #define USBD_STACK_SIZE     4096 | ||||
|  | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| #ifndef AUDIO_SAMPLE_RATE | ||||
| #define AUDIO_SAMPLE_RATE   48000 | ||||
| #endif | ||||
|  | ||||
| /* Blink pattern | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum  { | ||||
|   BLINK_NOT_MOUNTED = 250, | ||||
|   BLINK_MOUNTED = 1000, | ||||
|   BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
|  | ||||
| // static timer | ||||
| StaticTimer_t blinky_tmdef; | ||||
| TimerHandle_t blinky_tm; | ||||
| StackType_t  usb_device_stack[USBD_STACK_SIZE]; | ||||
| StaticTask_t usb_device_taskdef; | ||||
|  | ||||
|  | ||||
| // Audio controls | ||||
| // Current states | ||||
| bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 				          // +1 for master channel 0 | ||||
| uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 					// +1 for master channel 0 | ||||
| uint32_t sampFreq; | ||||
| uint8_t clkValid; | ||||
|  | ||||
| // Range states | ||||
| 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}; | ||||
|  | ||||
| void led_blinky_cb(TimerHandle_t xTimer); | ||||
| void usb_device_task(void* param); | ||||
| void audio_task(void); | ||||
|  | ||||
| /*------------- 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; | ||||
|  | ||||
|   sampleFreqRng.wNumSubRanges = 1; | ||||
|   sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE; | ||||
|   sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE; | ||||
|   sampleFreqRng.subrange[0].bRes = 0; | ||||
|  | ||||
|   // Create a task for tinyusb device stack | ||||
|   (void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); | ||||
|  | ||||
|   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 | ||||
|   #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
|     vTaskStartScheduler(); | ||||
|   #endif | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
| void app_main(void) | ||||
| { | ||||
|   main(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| // USB Device Driver task | ||||
| // This top level thread process all usb events and invoke callbacks | ||||
| void usb_device_task(void* param) | ||||
| { | ||||
|   (void) param; | ||||
|  | ||||
|   // This should be called after scheduler/kernel is started. | ||||
|   // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. | ||||
|   tusb_init(); | ||||
|  | ||||
|   // RTOS forever loop | ||||
|   while (1) | ||||
|   { | ||||
|     // tinyusb device task | ||||
|     tud_task(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Device callbacks | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Invoked when device is mounted | ||||
| void tud_mount_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0); | ||||
| } | ||||
|  | ||||
| // Invoked when device is unmounted | ||||
| void tud_umount_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0); | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is suspended | ||||
| // remote_wakeup_en : if host allow us  to perform remote wakeup | ||||
| // Within 7ms, device must draw an average of current less than 2.5 mA from bus | ||||
| void tud_suspend_cb(bool remote_wakeup_en) | ||||
| { | ||||
|   (void) remote_wakeup_en; | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0); | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is resumed | ||||
| void tud_resume_cb(void) | ||||
| { | ||||
|   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // AUDIO Task | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| void audio_task(void) | ||||
| { | ||||
|   // Yet to be filled - e.g. put meas data into TX FIFOs etc. | ||||
|   // asm("nop"); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Application Callback API Implementations | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Invoked when audio class specific set request received for an EP | ||||
| bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) pBuff; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t ep = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) ep; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific set request received for an interface | ||||
| bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) pBuff; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) itf; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific set request received for an entity | ||||
| bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|   uint8_t entityID = TU_U16_HIGH(p_request->wIndex); | ||||
|  | ||||
|   (void) itf; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // If request is for our feature unit | ||||
|   if ( entityID == 2 ) | ||||
|   { | ||||
|     switch ( ctrlSel ) | ||||
|     { | ||||
|       case AUDIO_FU_CTRL_MUTE: | ||||
|         // Request uses format layout 1 | ||||
|         TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); | ||||
|  | ||||
|         mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; | ||||
|  | ||||
|         TU_LOG2("    Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); | ||||
|       return true; | ||||
|  | ||||
|       case AUDIO_FU_CTRL_VOLUME: | ||||
|         // Request uses format layout 2 | ||||
|         TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); | ||||
|  | ||||
|         volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur; | ||||
|  | ||||
|         TU_LOG2("    Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); | ||||
|       return true; | ||||
|  | ||||
|         // Unknown/Unsupported control | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   return false;    // Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific get request received for an EP | ||||
| bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t ep = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) ep; | ||||
|  | ||||
|   //	return tud_control_xfer(rhport, p_request, &tmp, 1); | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific get request received for an interface | ||||
| bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) itf; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific get request received for an entity | ||||
| bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   // uint8_t itf = TU_U16_LOW(p_request->wIndex); 			// Since we have only one audio function implemented, we do not need the itf value | ||||
|   uint8_t entityID = TU_U16_HIGH(p_request->wIndex); | ||||
|  | ||||
|   // Input terminal (Microphone input) | ||||
|   if (entityID == 1) | ||||
|   { | ||||
|     switch ( ctrlSel ) | ||||
|     { | ||||
|       case AUDIO_TE_CTRL_CONNECTOR: | ||||
|       { | ||||
|         // The terminal connector control only has a get request with only the CUR attribute. | ||||
|         audio_desc_channel_cluster_t ret; | ||||
|  | ||||
|         // Those are dummy values for now | ||||
|         ret.bNrChannels = 1; | ||||
|         ret.bmChannelConfig = 0; | ||||
|         ret.iChannelNames = 0; | ||||
|  | ||||
|         TU_LOG2("    Get terminal connector\r\n"); | ||||
|  | ||||
|         return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); | ||||
|       } | ||||
|       break; | ||||
|  | ||||
|         // Unknown/Unsupported control selector | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|         return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Feature unit | ||||
|   if (entityID == 2) | ||||
|   { | ||||
|     switch ( ctrlSel ) | ||||
|     { | ||||
|       case AUDIO_FU_CTRL_MUTE: | ||||
|         // Audio control mute cur parameter block consists of only one byte - we thus can send it right away | ||||
|         // There does not exist a range parameter block for mute | ||||
|         TU_LOG2("    Get Mute of channel: %u\r\n", channelNum); | ||||
|         return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); | ||||
|  | ||||
|       case AUDIO_FU_CTRL_VOLUME: | ||||
|         switch ( p_request->bRequest ) | ||||
|         { | ||||
|           case AUDIO_CS_REQ_CUR: | ||||
|             TU_LOG2("    Get Volume of channel: %u\r\n", channelNum); | ||||
|             return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); | ||||
|  | ||||
|           case AUDIO_CS_REQ_RANGE: | ||||
|             TU_LOG2("    Get Volume range of channel: %u\r\n", channelNum); | ||||
|  | ||||
|             // Copy values - only for testing - better is version below | ||||
|             audio_control_range_2_n_t(1) | ||||
|             ret; | ||||
|  | ||||
|             ret.wNumSubRanges = 1; | ||||
|             ret.subrange[0].bMin = -90;           // -90 dB | ||||
|             ret.subrange[0].bMax = 90;		// +90 dB | ||||
|             ret.subrange[0].bRes = 1; 		// 1 dB steps | ||||
|  | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); | ||||
|  | ||||
|             // Unknown/Unsupported control | ||||
|           default: | ||||
|             TU_BREAKPOINT(); | ||||
|             return false; | ||||
|         } | ||||
|       break; | ||||
|  | ||||
|         // Unknown/Unsupported control | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|         return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Clock Source unit | ||||
|   if ( entityID == 4 ) | ||||
|   { | ||||
|     switch ( ctrlSel ) | ||||
|     { | ||||
|       case AUDIO_CS_CTRL_SAM_FREQ: | ||||
|         // channelNum is always zero in this case | ||||
|         switch ( p_request->bRequest ) | ||||
|         { | ||||
|           case AUDIO_CS_REQ_CUR: | ||||
|             TU_LOG2("    Get Sample Freq.\r\n"); | ||||
|             return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); | ||||
|  | ||||
|           case AUDIO_CS_REQ_RANGE: | ||||
|             TU_LOG2("    Get Sample Freq. range\r\n"); | ||||
|             return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); | ||||
|  | ||||
|            // Unknown/Unsupported control | ||||
|           default: | ||||
|             TU_BREAKPOINT(); | ||||
|             return false; | ||||
|         } | ||||
|       break; | ||||
|  | ||||
|       case AUDIO_CS_CTRL_CLK_VALID: | ||||
|         // Only cur attribute exists for this request | ||||
|         TU_LOG2("    Get Sample Freq. valid\r\n"); | ||||
|         return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); | ||||
|  | ||||
|       // Unknown/Unsupported control | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|         return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   TU_LOG2("  Unsupported entity: %d\r\n", entityID); | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) itf; | ||||
|   (void) ep_in; | ||||
|   (void) cur_alt_setting; | ||||
|  | ||||
|   tud_audio_write((uint8_t*)i2s_dummy_buffer, CFG_TUD_AUDIO_EP_SZ_IN); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) n_bytes_copied; | ||||
|   (void) itf; | ||||
|   (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; | ||||
| } | ||||
|  | ||||
| bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) p_request; | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| ///--------------------------------------------------------------------+ | ||||
| // BLINKING TASK | ||||
| //--------------------------------------------------------------------+ | ||||
| void led_blinky_cb(TimerHandle_t xTimer) | ||||
| { | ||||
|   (void) xTimer; | ||||
|   static bool led_state = false; | ||||
|  | ||||
|   board_led_write(led_state); | ||||
|   led_state = 1 - led_state; // toggle | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| import sounddevice as sd | ||||
| import matplotlib.pyplot as plt | ||||
| import numpy as np | ||||
| import platform | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|  | ||||
|     # If you got "ValueError: No input device matching", that is because your PC name example device | ||||
|     # differently from tested list below. Uncomment the next line to see full list and try to pick correct one | ||||
|     # print(sd.query_devices()) | ||||
|  | ||||
|     fs = 48000  		# Sample rate | ||||
|     duration = 100e-3   # Duration of recording | ||||
|  | ||||
|     if platform.system() == 'Windows': | ||||
|         # WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows) | ||||
|         device = 'Microphone (MicNode_4_Ch), Windows WDM-KS' | ||||
|     elif platform.system() == 'Darwin': | ||||
|         device = 'MicNode_4_Ch' | ||||
|     else: | ||||
|         device ='default' | ||||
|  | ||||
|     myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=4, dtype='int16', device=device) | ||||
|     print('Waiting...') | ||||
|     sd.wait()  # Wait until recording is finished | ||||
|     print('Done!') | ||||
|  | ||||
|     time = np.arange(0, duration, 1 / fs)  # time vector | ||||
|     plt.plot(time, myrecording) | ||||
|     plt.xlabel('Time [s]') | ||||
|     plt.ylabel('Amplitude') | ||||
|     plt.title('MicNode 4 Channel') | ||||
|     plt.show() | ||||
|      | ||||
							
								
								
									
										128
									
								
								examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // COMMON CONFIGURATION | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| // defined by compiler flags for flexibility | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
|  | ||||
| // RHPort number used for device can be defined by board.mk, default to port 0 | ||||
| #ifndef BOARD_DEVICE_RHPORT_NUM | ||||
|   #define BOARD_DEVICE_RHPORT_NUM     0 | ||||
| #endif | ||||
|  | ||||
| #ifndef BOARD_DEVICE_RHPORT_SPEED | ||||
|   #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED | ||||
| #endif | ||||
|  | ||||
| #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_DEVICE | ||||
|  | ||||
| #ifndef CFG_TUSB_OS | ||||
|   #define CFG_TUSB_OS                 OPT_OS_FREERTOS | ||||
| #endif | ||||
|  | ||||
| // Espressif IDF requires "freertos/" prefix in include path | ||||
| #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
|   #define CFG_TUSB_OS_INC_PATH    freertos/ | ||||
| #endif | ||||
|  | ||||
| #ifndef CFG_TUSB_DEBUG | ||||
|   #define CFG_TUSB_DEBUG              0 | ||||
| #endif | ||||
|  | ||||
| // CFG_TUSB_DEBUG is defined by compiler in DEBUG build | ||||
| // #define CFG_TUSB_DEBUG           0 | ||||
|  | ||||
| /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. | ||||
|  * Tinyusb use follows macros to declare transferring memory so that they can be put | ||||
|  * into those specific section. | ||||
|  * e.g | ||||
|  * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) | ||||
|  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4))) | ||||
|  */ | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
|  | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4))) | ||||
| #endif | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // DEVICE CONFIGURATION | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| #ifndef CFG_TUD_ENDPOINT0_SIZE | ||||
| #define CFG_TUD_ENDPOINT0_SIZE    64 | ||||
| #endif | ||||
|  | ||||
| //------------- CLASS -------------// | ||||
| #define CFG_TUD_CDC               0 | ||||
| #define CFG_TUD_MSC               0 | ||||
| #define CFG_TUD_HID               0 | ||||
| #define CFG_TUD_MIDI              0 | ||||
| #define CFG_TUD_AUDIO             1 | ||||
| #define CFG_TUD_VENDOR            0 | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // AUDIO CLASS DRIVER CONFIGURATION | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| // Have a look into audio_device.h for all configurations | ||||
|  | ||||
| #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_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_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) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
| @@ -0,0 +1,165 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "tusb.h" | ||||
|  | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. | ||||
|  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. | ||||
|  * | ||||
|  * Auto ProductID layout's Bitmap: | ||||
|  *   [MSB]     AUDIO | MIDI | HID | MSC | CDC          [LSB] | ||||
|  */ | ||||
| #define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) ) | ||||
| #define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ | ||||
|     _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Device Descriptors | ||||
| //--------------------------------------------------------------------+ | ||||
| tusb_desc_device_t const desc_device = | ||||
| { | ||||
|     .bLength            = sizeof(tusb_desc_device_t), | ||||
|     .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|     .bcdUSB             = 0x0200, | ||||
|  | ||||
|     // Use Interface Association Descriptor (IAD) for CDC | ||||
|     // 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, | ||||
|     .bDeviceProtocol    = MISC_PROTOCOL_IAD, | ||||
|     .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
|  | ||||
|     .idVendor           = 0xCafe, | ||||
|     .idProduct          = USB_PID, | ||||
|     .bcdDevice          = 0x0100, | ||||
|  | ||||
|     .iManufacturer      = 0x01, | ||||
|     .iProduct           = 0x02, | ||||
|     .iSerialNumber      = 0x03, | ||||
|  | ||||
|     .bNumConfigurations = 0x01 | ||||
| }; | ||||
|  | ||||
| // Invoked when received GET DEVICE DESCRIPTOR | ||||
| // Application return pointer to descriptor | ||||
| uint8_t const * tud_descriptor_device_cb(void) | ||||
| { | ||||
|   return (uint8_t const *) &desc_device; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Configuration Descriptor | ||||
| //--------------------------------------------------------------------+ | ||||
| enum | ||||
| { | ||||
|   ITF_NUM_AUDIO_CONTROL = 0, | ||||
|   ITF_NUM_AUDIO_STREAMING, | ||||
|   ITF_NUM_TOTAL | ||||
| }; | ||||
|  | ||||
| #define CONFIG_TOTAL_LEN    	(TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_FOUR_CH_DESC_LEN) | ||||
|  | ||||
| #if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) | ||||
|   // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number | ||||
|   // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... | ||||
|   #define EPNUM_AUDIO   0x03 | ||||
|  | ||||
| #elif TU_CHECK_MCU(OPT_MCU_NRF5X) | ||||
|   // nRF5x ISO can only be endpoint 8 | ||||
|   #define EPNUM_AUDIO   0x08 | ||||
|  | ||||
| #else | ||||
|   #define EPNUM_AUDIO   0x01 | ||||
| #endif | ||||
|  | ||||
| uint8_t const desc_configuration[] = | ||||
| { | ||||
|   // 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 | ||||
|   TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN) | ||||
| }; | ||||
|  | ||||
| // Invoked when received GET CONFIGURATION DESCRIPTOR | ||||
| // Application return pointer to descriptor | ||||
| // Descriptor contents must exist long enough for transfer to complete | ||||
| uint8_t const * tud_descriptor_configuration_cb(uint8_t index) | ||||
| { | ||||
|   (void) index; // for multiple configurations | ||||
|   return desc_configuration; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // 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 | ||||
| }; | ||||
|  | ||||
| static uint16_t _desc_str[32]; | ||||
|  | ||||
| // 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) | ||||
| { | ||||
|   (void) langid; | ||||
|  | ||||
|   uint8_t chr_count; | ||||
|  | ||||
|   if ( index == 0) | ||||
|   { | ||||
|     memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|     chr_count = 1; | ||||
|   }else | ||||
|   { | ||||
|     // Convert ASCII string into UTF-16 | ||||
|  | ||||
|     if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; | ||||
|  | ||||
|     const char* str = string_desc_arr[index]; | ||||
|  | ||||
|     // Cap at max char | ||||
|     chr_count = strlen(str); | ||||
|     if ( chr_count > 31 ) chr_count = 31; | ||||
|  | ||||
|     for(uint8_t i=0; i<chr_count; i++) | ||||
|     { | ||||
|       _desc_str[1+i] = str[i]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // first byte is length (including header), second byte is string type | ||||
|   _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2); | ||||
|  | ||||
|   return _desc_str; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user