initial commit

This commit is contained in:
shell committed 2026-10-11 21:48:59 +08:00
commit b44ce7bd77
2716 files changed
+559005

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../hw/bsp/family_support.cmake)
project(tinyusb_examples C CXX ASM)
set(EXAMPLES_LIST
device
dual
host
typec
)
set(MAPJSON_PATTERNS "")
foreach (example ${EXAMPLES_LIST})
add_subdirectory(${example})
list(APPEND MAPJSON_PATTERNS "${CMAKE_BINARY_DIR}/${example}/*/*.map.json")
endforeach ()
# Post-build: run metrics.py on all map.json files
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_target(tinyusb_metrics
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/metrics.py
combine -f tinyusb/src -j -o ${CMAKE_BINARY_DIR}/metrics
${MAPJSON_PATTERNS}
COMMENT "Generating average code size metrics"
VERBATIM
)
+6
View File
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../hw/bsp/BoardPresets.json"
]
}
@@ -0,0 +1,21 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=arm1176jzf-s
-ffreestanding
)
# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=arm1176jzf-s
-mfpu=none
-mfloat-abi=soft
-ffreestanding
)
#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
endif ()
@@ -0,0 +1,21 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=arm926ej-s
-ffreestanding
)
# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=arm926ej-s
-mfpu=none
-mfloat-abi=soft
-ffreestanding
)
#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
endif ()
@@ -0,0 +1,17 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=cortex-a53
)
# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-a53
)
#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
endif ()
@@ -0,0 +1,17 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=cortex-a72
)
# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-a72
)
#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
endif ()
@@ -0,0 +1,22 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m0
-mfloat-abi=soft
)
set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m0
)
set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m0
)
set(FREERTOS_PORT IAR_ARM_CM0 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,22 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m0plus
-mfloat-abi=soft
)
set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m0plus
)
set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m0
)
set(FREERTOS_PORT IAR_ARM_CM0 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,22 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m23
-mfloat-abi=soft
)
set(FREERTOS_PORT GCC_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m23
)
set(FREERTOS_PORT GCC_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m23
)
set(FREERTOS_PORT IAR_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,21 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m3
)
set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m3
)
set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m3
)
set(FREERTOS_PORT IAR_ARM_CM3 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,18 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m33+nodsp
-mfloat-abi=soft
)
set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
message(FATAL_ERROR "Clang is not supported for this target")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m33+nodsp
)
set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,25 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m33+nodsp
-mfloat-abi=hard
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m33+nodsp
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m33+nodsp
--fpu VFPv5-SP
)
set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,25 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m33
-mfloat-abi=hard
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m33
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m33
--fpu VFPv5-SP
)
set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,30 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m4
-mfloat-abi=soft
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
endif ()
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m4
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
endif ()
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m4
--fpu none
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT IAR_ARM_CM3 CACHE INTERNAL "")
endif ()
endif ()
@@ -0,0 +1,32 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m4
-mfloat-abi=hard
-mfpu=fpv4-sp-d16
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT GCC_ARM_CM4F CACHE INTERNAL "")
endif ()
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m4
-mfpu=fpv4-sp-d16
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT GCC_ARM_CM4F CACHE INTERNAL "")
endif ()
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m4
--fpu VFPv4_sp
)
if (NOT DEFINED FREERTOS_PORT)
set(FREERTOS_PORT IAR_ARM_CM4F CACHE INTERNAL "")
endif ()
endif ()
@@ -0,0 +1,28 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m55
-mfloat-abi=hard
-mfpu=fpv5-d16
-mcmse
)
set(FREERTOS_PORT GCC_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m55
-mfpu=fpv5-d16
-mcmse
)
set(FREERTOS_PORT GCC_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m55
--fpu VFPv5_D16
--cmse
)
set(FREERTOS_PORT IAR_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,25 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m7
-mfloat-abi=hard
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m7
-mfpu=fpv5-sp-d16
)
set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m7
--fpu VFPv5_sp
)
set(FREERTOS_PORT IAR_ARM_CM7 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,25 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m7
-mfloat-abi=hard
-mfpu=fpv5-d16
)
set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m7
-mfpu=fpv5-d16
)
set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m7
--fpu VFPv5_D16
)
set(FREERTOS_PORT IAR_ARM_CM7 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,25 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mthumb
-mcpu=cortex-m85
-mfloat-abi=hard
-mfpu=fpv5-d16
)
set(FREERTOS_PORT GCC_ARM_CM85_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
--target=arm-none-eabi
-mcpu=cortex-m85
-mfpu=fpv5-d16
)
set(FREERTOS_PORT GCC_ARM_CM85_NTZ_NONSECURE CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m85
--fpu VFPv5_D16
)
set(FREERTOS_PORT IAR_ARM_CM85_NTZ_NONSECURE CACHE INTERNAL "")
endif ()
@@ -0,0 +1,13 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-fvar-tracking
-fvar-tracking-assignments
)
elseif (TOOLCHAIN STREQUAL "clang")
message(FATAL_ERROR "Clang is not supported for this target")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR is not supported for this target")
endif ()
@@ -0,0 +1,10 @@
if (TOOLCHAIN STREQUAL "gcc")
set(FREERTOS_PORT GCC_MSP430F449 CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
message(FATAL_ERROR "Clang is not supported for this target")
elseif (TOOLCHAIN STREQUAL "iar")
set(FREERTOS_PORT IAR_MSP430 CACHE INTERNAL "")
endif ()
@@ -0,0 +1,19 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-march=rv32i_zicsr
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
-march=rv32i_zicsr
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
set(FREERTOS_PORT IAR_RISC_V CACHE INTERNAL "")
endif ()
@@ -0,0 +1,18 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-march=rv32imac_zicsr_zifencei
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
-march=rv32imac_zicsr_zifencei
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "iar")
message(FATAL_ERROR "IAR not supported")
set(FREERTOS_PORT IAR_RISC_V CACHE INTERNAL "")
endif ()
@@ -0,0 +1,12 @@
if (NOT DEFINED TOOLCHAIN OR TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=rx610
-misa=v1
-mlittle-endian-data
-fshort-enums
)
set(FREERTOS_PORT GCC_RX600 CACHE INTERNAL "")
else ()
message(FATAL_ERROR "Toolchain ${TOOLCHAIN} is not supported for RX")
endif ()
@@ -0,0 +1,12 @@
if (NOT DEFINED TOOLCHAIN OR TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
-mcpu=rx64m
-misa=v2
-mlittle-endian-data
-fshort-enums
)
set(FREERTOS_PORT GCC_RX600 CACHE INTERNAL "")
else ()
message(FATAL_ERROR "Toolchain ${TOOLCHAIN} is not supported for RX")
endif ()
@@ -0,0 +1,21 @@
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "aarch64-none-elf-gcc")
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "aarch64-none-elf-g++")
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE aarch64-none-elf-size)
find_program(CMAKE_OBJCOPY aarch64-none-elf-objcopy)
find_program(CMAKE_OBJDUMP aarch64-none-elf-objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,22 @@
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "clang")
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "clang++")
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE llvm-size)
find_program(CMAKE_OBJCOPY llvm-objcopy)
find_program(CMAKE_OBJDUMP llvm-objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,25 @@
if (RTOS STREQUAL zephyr)
return()
endif ()
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "arm-none-eabi-gcc")
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "arm-none-eabi-g++")
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE arm-none-eabi-size)
find_program(CMAKE_OBJCOPY arm-none-eabi-objcopy)
find_program(CMAKE_OBJDUMP arm-none-eabi-objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,32 @@
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "iccarm")
endif()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "iccarm")
endif()
if (NOT DEFINED CMAKE_ASM_COMPILER)
set(CMAKE_ASM_COMPILER "iasmarm")
endif()
find_program(CMAKE_SIZE size)
find_program(CMAKE_OBJCOPY ielftool)
find_program(CMAKE_OBJDUMP iefdumparm)
find_program(CMAKE_IAR_CSTAT icstat)
find_program(CMAKE_IAR_CHECKS ichecks)
find_program(CMAKE_IAR_REPORT ireport)
if (IAR_CSTAT)
cmake_minimum_required(VERSION 4.1)
set(CMAKE_C_ICSTAT ${CMAKE_IAR_CSTAT}
--checks=${CMAKE_CURRENT_LIST_DIR}/cstat_sel_checks.txt
--db=${CMAKE_BINARY_DIR}/cstat.db
--sarif_dir=${CMAKE_BINARY_DIR}/cstat_sarif
--exclude=${TOP}/hw/mcu
--exclude=${TOP}/lib
)
endif ()
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
@@ -0,0 +1,51 @@
include(CMakePrintHelpers)
# ----------------------------------------------------------------------------
# Common
# ----------------------------------------------------------------------------
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_CPU})
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
# Look for includes and libraries only in the target system prefix.
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# pass TOOLCHAIN_CPU to
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_SYSTEM_PROCESSOR CMAKE_SYSTEM_CPU)
include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_CPU}.cmake)
# ----------------------------------------------------------------------------
# Compile flags
# ----------------------------------------------------------------------------
set(TOOLCHAIN_C_FLAGS)
set(TOOLCHAIN_ASM_FLAGS)
set(TOOLCHAIN_EXE_LINKER_FLAGS)
if (TOOLCHAIN STREQUAL "gcc" OR TOOLCHAIN STREQUAL "clang")
list(APPEND TOOLCHAIN_COMMON_FLAGS
-fdata-sections
-ffunction-sections
# -fsingle-precision-constant # not supported by clang
-fno-strict-aliasing
-g # include debug info for bloaty
)
set(TOOLCHAIN_EXE_LINKER_FLAGS "-Wl,--print-memory-usage -Wl,--gc-sections -Wl,--cref")
if (TOOLCHAIN STREQUAL clang)
set(TOOLCHAIN_ASM_FLAGS "-x assembler-with-cpp")
endif ()
elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_C_FLAGS --debug)
set(TOOLCHAIN_EXE_LINKER_FLAGS --diag_suppress=Li065)
endif ()
# join the toolchain flags into a single string
list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS)
set(CMAKE_C_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_C_FLAGS}")
set(CMAKE_CXX_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_C_FLAGS}")
set(CMAKE_ASM_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_ASM_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_INIT ${TOOLCHAIN_EXE_LINKER_FLAGS})
@@ -0,0 +1,247 @@
# IAR C-STAT Checks Manifest Handler V2.7.5.562
#
MISRAC2012-Dir-4.3
MISRAC2012-Dir-4.7_c
MISRAC2012-Dir-4.10
MISRAC2012-Dir-4.11_a
MISRAC2012-Dir-4.11_b
MISRAC2012-Dir-4.11_c
MISRAC2012-Dir-4.11_d
MISRAC2012-Dir-4.11_e
MISRAC2012-Dir-4.11_f
MISRAC2012-Dir-4.11_g
MISRAC2012-Dir-4.11_h
MISRAC2012-Dir-4.11_i
MISRAC2012-Dir-4.12
MISRAC2012-Dir-4.14_a
MISRAC2012-Dir-4.14_b
MISRAC2012-Dir-4.14_c
MISRAC2012-Dir-4.14_d
MISRAC2012-Dir-4.14_e
MISRAC2012-Dir-4.14_f
MISRAC2012-Dir-4.14_g
MISRAC2012-Dir-4.14_h
MISRAC2012-Dir-4.14_i
MISRAC2012-Dir-4.14_j
MISRAC2012-Dir-4.14_l
MISRAC2012-Dir-4.14_m
MISRAC2012-Dir-4.15
MISRAC2012-Rule-1.3_a
MISRAC2012-Rule-1.3_b
MISRAC2012-Rule-1.3_c
MISRAC2012-Rule-1.3_d
MISRAC2012-Rule-1.3_e
MISRAC2012-Rule-1.3_f
MISRAC2012-Rule-1.3_g
MISRAC2012-Rule-1.3_h
MISRAC2012-Rule-1.3_i
MISRAC2012-Rule-1.3_j
MISRAC2012-Rule-1.3_k
MISRAC2012-Rule-1.3_l
MISRAC2012-Rule-1.3_m
MISRAC2012-Rule-1.3_n
MISRAC2012-Rule-1.3_o
MISRAC2012-Rule-1.3_p
MISRAC2012-Rule-1.3_q
MISRAC2012-Rule-1.3_r
MISRAC2012-Rule-1.3_s
MISRAC2012-Rule-1.3_t
MISRAC2012-Rule-1.3_u
MISRAC2012-Rule-1.3_v
MISRAC2012-Rule-1.4
MISRAC2012-Rule-1.5_b
MISRAC2012-Rule-1.5_c
MISRAC2012-Rule-1.5_d
MISRAC2012-Rule-1.5_e
MISRAC2012-Rule-1.5_f
MISRAC2012-Rule-1.5_g
MISRAC2012-Rule-2.1_a
MISRAC2012-Rule-2.1_b
MISRAC2012-Rule-2.2_a
MISRAC2012-Rule-2.2_b
MISRAC2012-Rule-2.2_c
MISRAC2012-Rule-3.1
MISRAC2012-Rule-3.2
MISRAC2012-Rule-5.1
MISRAC2012-Rule-5.2_c89
MISRAC2012-Rule-5.2_c99
MISRAC2012-Rule-5.3_c89
MISRAC2012-Rule-5.3_c99
MISRAC2012-Rule-5.4_c89
MISRAC2012-Rule-5.4_c99
MISRAC2012-Rule-5.5_c89
MISRAC2012-Rule-5.5_c99
MISRAC2012-Rule-5.6
MISRAC2012-Rule-5.7
MISRAC2012-Rule-5.8
MISRAC2012-Rule-6.1
MISRAC2012-Rule-6.2
MISRAC2012-Rule-6.3
MISRAC2012-Rule-7.1
MISRAC2012-Rule-7.2
MISRAC2012-Rule-7.3
MISRAC2012-Rule-7.4_a
MISRAC2012-Rule-7.4_b
MISRAC2012-Rule-7.5
MISRAC2012-Rule-7.6
MISRAC2012-Rule-8.1
MISRAC2012-Rule-8.2_a
MISRAC2012-Rule-8.2_b
MISRAC2012-Rule-8.3
MISRAC2012-Rule-8.4
MISRAC2012-Rule-8.5_a
MISRAC2012-Rule-8.5_b
MISRAC2012-Rule-8.10
MISRAC2012-Rule-8.12
MISRAC2012-Rule-8.14
MISRAC2012-Rule-8.15
MISRAC2012-Rule-9.1_a
MISRAC2012-Rule-9.1_b
MISRAC2012-Rule-9.1_d
MISRAC2012-Rule-9.1_e
MISRAC2012-Rule-9.2
MISRAC2012-Rule-9.3
MISRAC2012-Rule-9.4
MISRAC2012-Rule-9.5_a
MISRAC2012-Rule-9.5_b
MISRAC2012-Rule-9.6
MISRAC2012-Rule-9.7
MISRAC2012-Rule-10.1_R2
MISRAC2012-Rule-10.1_R3
MISRAC2012-Rule-10.1_R4
MISRAC2012-Rule-10.1_R5
MISRAC2012-Rule-10.1_R6
MISRAC2012-Rule-10.1_R7
MISRAC2012-Rule-10.1_R8
MISRAC2012-Rule-10.1_R10
MISRAC2012-Rule-10.2
MISRAC2012-Rule-10.3
MISRAC2012-Rule-10.4_a
MISRAC2012-Rule-10.4_b
MISRAC2012-Rule-10.6
MISRAC2012-Rule-10.7
MISRAC2012-Rule-10.8
MISRAC2012-Rule-11.1
MISRAC2012-Rule-11.2
MISRAC2012-Rule-11.3
MISRAC2012-Rule-11.6
MISRAC2012-Rule-11.7
MISRAC2012-Rule-11.8
MISRAC2012-Rule-11.9
MISRAC2012-Rule-11.10
MISRAC2012-Rule-12.2
MISRAC2012-Rule-12.5
MISRAC2012-Rule-12.6
MISRAC2012-Rule-13.1
MISRAC2012-Rule-13.2_a
MISRAC2012-Rule-13.2_b
MISRAC2012-Rule-13.2_c
MISRAC2012-Rule-13.5
MISRAC2012-Rule-13.6
MISRAC2012-Rule-14.1_a
MISRAC2012-Rule-14.1_b
MISRAC2012-Rule-14.2
MISRAC2012-Rule-14.3_a
MISRAC2012-Rule-14.3_b
MISRAC2012-Rule-14.4_a
MISRAC2012-Rule-14.4_b
MISRAC2012-Rule-14.4_c
MISRAC2012-Rule-14.4_d
MISRAC2012-Rule-15.2
MISRAC2012-Rule-15.3
MISRAC2012-Rule-15.6_a
MISRAC2012-Rule-15.6_b
MISRAC2012-Rule-15.6_c
MISRAC2012-Rule-15.6_d
MISRAC2012-Rule-15.6_e
MISRAC2012-Rule-15.7
MISRAC2012-Rule-16.1
MISRAC2012-Rule-16.2
MISRAC2012-Rule-16.3
MISRAC2012-Rule-16.4
MISRAC2012-Rule-16.5
MISRAC2012-Rule-16.6
MISRAC2012-Rule-16.7
MISRAC2012-Rule-17.1
MISRAC2012-Rule-17.2_a
MISRAC2012-Rule-17.2_b
MISRAC2012-Rule-17.3
MISRAC2012-Rule-17.4
MISRAC2012-Rule-17.5
MISRAC2012-Rule-17.6
MISRAC2012-Rule-17.7
MISRAC2012-Rule-17.13
MISRAC2012-Rule-18.1_a
MISRAC2012-Rule-18.1_b
MISRAC2012-Rule-18.1_c
MISRAC2012-Rule-18.1_d
MISRAC2012-Rule-18.2
MISRAC2012-Rule-18.3
MISRAC2012-Rule-18.4
MISRAC2012-Rule-18.6_a
MISRAC2012-Rule-18.6_b
MISRAC2012-Rule-18.6_c
MISRAC2012-Rule-18.6_d
MISRAC2012-Rule-18.7
MISRAC2012-Rule-18.8
MISRAC2012-Rule-18.9
MISRAC2012-Rule-18.10
MISRAC2012-Rule-19.1
MISRAC2012-Rule-20.2
MISRAC2012-Rule-20.4_c89
MISRAC2012-Rule-20.4_c99
MISRAC2012-Rule-20.6_a
MISRAC2012-Rule-20.6_b
MISRAC2012-Rule-20.7
MISRAC2012-Rule-21.1
MISRAC2012-Rule-21.2
MISRAC2012-Rule-21.3
MISRAC2012-Rule-21.4
MISRAC2012-Rule-21.5
MISRAC2012-Rule-21.6
MISRAC2012-Rule-21.7
MISRAC2012-Rule-21.8
MISRAC2012-Rule-21.9
MISRAC2012-Rule-21.10
MISRAC2012-Rule-21.12_a
MISRAC2012-Rule-21.12_b
MISRAC2012-Rule-21.12_c
MISRAC2012-Rule-21.13
MISRAC2012-Rule-21.14
MISRAC2012-Rule-21.15
MISRAC2012-Rule-21.16
MISRAC2012-Rule-21.17_a
MISRAC2012-Rule-21.17_b
MISRAC2012-Rule-21.17_c
MISRAC2012-Rule-21.17_d
MISRAC2012-Rule-21.17_e
MISRAC2012-Rule-21.17_f
MISRAC2012-Rule-21.18_a
MISRAC2012-Rule-21.18_b
MISRAC2012-Rule-21.19_a
MISRAC2012-Rule-21.19_b
MISRAC2012-Rule-21.20
MISRAC2012-Rule-21.21
MISRAC2012-Rule-21.22
MISRAC2012-Rule-21.23
MISRAC2012-Rule-21.24
MISRAC2012-Rule-21.25
MISRAC2012-Rule-22.1_a
MISRAC2012-Rule-22.1_b
MISRAC2012-Rule-22.2_a
MISRAC2012-Rule-22.2_b
MISRAC2012-Rule-22.2_c
MISRAC2012-Rule-22.3
MISRAC2012-Rule-22.4
MISRAC2012-Rule-22.5_a
MISRAC2012-Rule-22.5_b
MISRAC2012-Rule-22.6
MISRAC2012-Rule-22.7_a
MISRAC2012-Rule-22.7_b
MISRAC2012-Rule-22.8
MISRAC2012-Rule-22.9
MISRAC2012-Rule-22.10
MISRAC2012-Rule-23.2
MISRAC2012-Rule-23.4
MISRAC2012-Rule-23.6
MISRAC2012-Rule-23.8
@@ -0,0 +1,22 @@
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "ft32-elf-gcc")
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "ft32-elf-g++")
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE ft32-elf-size)
find_program(CMAKE_OBJCOPY ft32-elf-objcopy)
find_program(CMAKE_OBJDUMP ft32-elf-objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,15 @@
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "msp430-elf-gcc")
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER "msp430-elf-g++")
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE msp430-elf-size)
find_program(CMAKE_OBJCOPY msp430-elf-objcopy)
find_program(CMAKE_OBJDUMP msp430-elf-objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
@@ -0,0 +1,30 @@
# default Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
if (NOT DEFINED CROSS_COMPILE)
set(CROSS_COMPILE "riscv-none-elf-")
endif ()
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
endif ()
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++)
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE ${CROSS_COMPILE}size)
find_program(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy)
find_program(CMAKE_OBJDUMP ${CROSS_COMPILE}objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,26 @@
# Cross Compiler for RX
if (NOT DEFINED CROSS_COMPILE)
set(CROSS_COMPILE "rx-elf-")
endif ()
if (NOT DEFINED CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
endif ()
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++)
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
find_program(CMAKE_SIZE ${CROSS_COMPILE}size)
find_program(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy)
find_program(CMAKE_OBJDUMP ${CROSS_COMPILE}objdump)
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if (IS_IN_TRY_COMPILE)
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
cmake_print_variables(CMAKE_C_LINK_FLAGS)
endif ()
@@ -0,0 +1,5 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=arm1176jzf-s \
endif
@@ -0,0 +1,5 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=arm926ej-s \
endif
@@ -0,0 +1,5 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=cortex-a53 \
endif
@@ -0,0 +1,5 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=cortex-a72 \
endif
@@ -0,0 +1,17 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m0 \
-mfloat-abi=soft \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m0 \
else
$(error "TOOLCHAIN is not supported")
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM0
@@ -0,0 +1,17 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m0plus \
-mfloat-abi=soft \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m0plus \
else
$(error "TOOLCHAIN is not supported")
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM0
@@ -0,0 +1,17 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m23 \
-mfloat-abi=soft \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m23 \
else
$(error "TOOLCHAIN is not supported")
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM23
@@ -0,0 +1,17 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m3 \
-mfloat-abi=soft \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m3 \
else
$(error "TOOLCHAIN is not supported")
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM3
@@ -0,0 +1,17 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m33+nodsp \
-mfloat-abi=soft \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m33 \
-mfpu=softvp \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
@@ -0,0 +1,18 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m33 \
-mfloat-abi=hard \
-mfpu=fpv5-sp-d16 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m33 \
-mfpu=fpv5-sp-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
@@ -0,0 +1,16 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m4 \
-mfloat-abi=soft
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m4
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM3
@@ -0,0 +1,18 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m4 \
-mfloat-abi=hard \
-mfpu=fpv4-sp-d16 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m4 \
-mfpu=fpv4-sp-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM4F
@@ -0,0 +1,19 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m55 \
-mfloat-abi=hard \
-mfpu=fpv5-d16 \
-mcmse
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m55 \
-mfpu=fpv5-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM55_NTZ/non_secure
@@ -0,0 +1,18 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m7 \
-mfloat-abi=hard \
-mfpu=fpv5-sp-d16 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m7 \
-mfpu=fpv5-sp-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM7/r0p1
@@ -0,0 +1,18 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m7 \
-mfloat-abi=hard \
-mfpu=fpv5-d16 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m7 \
-mfpu=fpv5-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM7/r0p1
@@ -0,0 +1,18 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mthumb \
-mcpu=cortex-m85 \
-mfloat-abi=hard \
-mfpu=fpv5-d16 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
--target=arm-none-eabi \
-mcpu=cortex-m85 \
-mfpu=fpv5-d16 \
else
$(error "TOOLCHAIN is not supported")
endif
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM85_NTZ/non_secure
@@ -0,0 +1,10 @@
ifeq ($(TOOLCHAIN),gcc)
# nothing to add
else ifeq ($(TOOLCHAIN),clang)
# nothing to add
else
$(error "TOOLCHAIN is not supported")
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/GCC_MSP430F449
@@ -0,0 +1,14 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-march=rv32i_zicsr \
-mabi=ilp32 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
-march=rv32i_zicsr \
-mabi=ilp32 \
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
@@ -0,0 +1,14 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-march=rv32imac_zicsr_zifencei \
-mabi=ilp32 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
-march=rv32imac_zicsr_zifencei \
-mabi=ilp32 \
endif
# For freeRTOS port source
FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
@@ -0,0 +1,10 @@
CC = clang
CXX = clang++
AS = $(CC) -x assembler-with-cpp
LD = $(CC)
GDB = $(CROSS_COMPILE)gdb
OBJCOPY = llvm-objcopy
SIZE = llvm-size
include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
@@ -0,0 +1,20 @@
# makefile for arm gcc toolchain
# Can be set by family, default to ARM GCC
CROSS_COMPILE ?= arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AS = $(CC) -x assembler-with-cpp
LD = $(CC)
GDB = $(CROSS_COMPILE)gdb
OBJCOPY = $(CROSS_COMPILE)objcopy
SIZE = $(CROSS_COMPILE)size
CFLAGS += \
-fsingle-precision-constant \
LIBS += -lgcc -lm -lnosys
include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
@@ -0,0 +1 @@
include ${TOP}/examples/build_system/make/toolchain/gcc_rules.mk
@@ -0,0 +1,75 @@
# ---------------------------------------
# Compiler Flags
# ---------------------------------------
CFLAGS += \
-MD \
-ggdb \
-fdata-sections \
-ffunction-sections \
-fno-strict-aliasing \
-Wall \
-Wextra \
-Werror \
-Wfatal-errors \
-Wdouble-promotion \
-Wstrict-prototypes \
-Wstrict-overflow \
-Werror-implicit-function-declaration \
-Wfloat-equal \
-Wundef \
-Wshadow \
-Wwrite-strings \
-Wsign-compare \
-Wmissing-format-attribute \
-Wunreachable-code \
-Wcast-align \
-Wcast-function-type \
-Wcast-qual \
-Wnull-dereference \
-Wuninitialized \
-Wunused \
-Wreturn-type \
-Wredundant-decls \
CFLAGS_CLANG += \
-Wno-error=unknown-warning-option
# -Wmissing-prototypes \
# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion
# -Wconversion
# Size Optimization as default
CFLAGS_OPTIMIZED ?= -Os
# Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -O0
NO_LTO = 1
else
CFLAGS += $(CFLAGS_OPTIMIZED)
endif
# ---------------------------------------
# Linker Flags
# ---------------------------------------
LDFLAGS += \
-Wl,-Map=$@.map \
-Wl,--cref \
-Wl,-gc-sections \
# renesas rx does not support --print-memory-usage flags
ifneq ($(FAMILY),rx)
LDFLAGS += -Wl,--print-memory-usage
endif
ifeq ($(TOOLCHAIN),gcc)
CC_VERSION := $(shell $(CC) -dumpversion)
CC_VERSION_MAJOR = $(firstword $(subst ., ,$(CC_VERSION)))
# from version 12
ifeq ($(strip $(if $(CMDEXE),\
$(shell if $(CC_VERSION_MAJOR) geq 12 (echo 1) else (echo 0)),\
$(shell expr $(CC_VERSION_MAJOR) \>= 12))), 1)
LDFLAGS += -Wl,--no-warn-rwx-segment
endif
endif
@@ -0,0 +1,70 @@
# Assembly files can be name with upper case .S, convert it to .s
SRC_S := $(SRC_S:.S=.s)
# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
# assembly file should be placed first in linking order
# '_asm' suffix is added to object of assembly file
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o))
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
CFLAGS += -MD
# LTO makes it difficult to analyze map file for optimizing size purpose
# We will run this option in ci
ifeq ($(NO_LTO),1)
CFLAGS := $(filter-out -flto,$(CFLAGS))
endif
ifneq ($(CFLAGS_SKIP),)
CFLAGS := $(filter-out $(CFLAGS_SKIP),$(CFLAGS))
endif
ifeq ($(TOOLCHAIN),clang)
CFLAGS += $(CFLAGS_CLANG)
LDFLAGS += $(CFLAGS) $(LDFLAGS_CLANG)
else
LDFLAGS += $(CFLAGS)
endif
ifdef LD_FILE
LDFLAGS += -Wl,-T,$(TOP)/$(LD_FILE)
endif
ASFLAGS += $(CFLAGS)
# libc
ifneq ($(BOARD), spresense)
LIBS += -lc
endif
# ---------------------------------------
# Rules
# ---------------------------------------
# Compile .c file
$(BUILD)/obj/%.o: %.c
@echo CC $(notdir $@)
@$(CC) $(CFLAGS) -c -o $@ $<
# ASM sources lower case .s
$(BUILD)/obj/%_asm.o: %.s
@echo AS $(notdir $@)
@$(AS) $(ASFLAGS) -c -o $@ $<
# ASM sources upper case .S
$(BUILD)/obj/%_asm.o: %.S
@echo AS $(notdir $@)
@$(AS) $(ASFLAGS) -c -o $@ $<
OBJCOPY_BIN_OPTION ?=
$(BUILD)/$(PROJECT).bin: $(BUILD)/$(PROJECT).elf
@echo CREATE $@
$(OBJCOPY) -O binary $(OBJCOPY_BIN_OPTION) $^ $@
$(BUILD)/$(PROJECT).hex: $(BUILD)/$(PROJECT).elf
@echo CREATE $@
@$(OBJCOPY) -O ihex $^ $@
$(BUILD)/$(PROJECT).elf: $(OBJ)
@echo LINK $@
@$(LD) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
@@ -0,0 +1,20 @@
# makefile for arm gcc toolchain
# Can be set by family, default to ARM GCC
CROSS_COMPILE ?= riscv-none-elf-
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AS = $(CC) -x assembler-with-cpp
LD = $(CC)
GDB = $(CROSS_COMPILE)gdb
OBJCOPY = $(CROSS_COMPILE)objcopy
SIZE = $(CROSS_COMPILE)size
CFLAGS += \
-fsingle-precision-constant \
LIBS += -lgcc -lm -lnosys
include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
+21
View File
@@ -0,0 +1,21 @@
# Copy this file to the location of your distribution's udev rules, for example on Ubuntu:
# sudo cp 99-tinyusb.rules /etc/udev/rules.d/
# Then reload udev configuration by executing:
# sudo udevadm control --reload-rules
# sudo udevadm trigger
# Check SUBSYSTEM
SUBSYSTEMS=="hidraw", KERNEL=="hidraw*", MODE="0666", GROUP="dialout"
# Rule applies to all TinyUSB example
ATTRS{idVendor}=="cafe", MODE="0666", GROUP="dialout"
# Rule to blacklist TinyUSB example from being manipulated by ModemManager.
SUBSYSTEMS=="usb", ATTRS{idVendor}=="cafe", ENV{ID_MM_DEVICE_IGNORE}="1"
# Xplained Pro SamG55 Device
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2111", MODE="0666", GROUP="users", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEMS=="tty", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2111", MODE="0666", GROUP="users", ENV{ID_MM_DEVICE_IGNORE}="1"
# TI Stellaris/Tiva-C Launchpad ICDI
SUBSYSTEM=="usb", ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="00fd", MODE="0666"
+46
View File
@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/family_support.cmake)
project(tinyusb_device_examples C CXX ASM)
family_initialize_project(tinyusb_device_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
set(EXAMPLE_LIST
audio_4_channel_mic
audio_4_channel_mic_freertos
audio_test
audio_test_freertos
audio_test_multi_rate
board_test
cdc_dual_ports
cdc_msc
cdc_msc_freertos
cdc_msc_throughput
cdc_uac2
dfu
dfu_runtime
dynamic_configuration
hid_boot_interface
hid_composite
hid_composite_freertos
hid_generic_inout
hid_multiple_interface
midi_test
midi_test_freertos
midi2_device
msc_dual_lun
mtp
net_lwip_webserver
printer_to_cdc
uac2_headset
uac2_speaker_fb
usbtmc
video_capture
video_capture_2ch
webusb_serial
)
foreach (example ${EXAMPLE_LIST})
family_add_subdirectory(${example})
endforeach ()
+6
View File
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../../hw/bsp/BoardPresets.json"
]
}
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(audio_4_channel_mic C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Add libm for GCC
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_link_libraries(${PROJECT_NAME} PUBLIC m)
endif()
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT_NAME} noos)
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}
@@ -0,0 +1,14 @@
include ../../../hw/bsp/family_support.mk
INC += \
src \
# Example source
EXAMPLE_SOURCE += \
src/main.c \
src/usb_descriptors.c \
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
include ../../../hw/bsp/family_rules.mk
@@ -0,0 +1,56 @@
# Audio 4-Channel Microphone
A USB Audio Class 2.0 (UAC2) microphone that streams four channels of locally generated test audio to the host.
## What it does
- Enumerates as a UAC2 microphone with 4 input channels at 48 kHz, 16-bit.
- On startup fills a 1 ms buffer with four distinct waveforms — channel 0 a sawtooth, channel 1 an inverted sawtooth, channel 2 a square wave, channel 3 a sine wave.
- An audio task writes that buffer to the IN endpoint every 1 ms, simulating data arriving from an I2S source.
- Handles UAC2 control requests: feature-unit mute/volume, clock-source sample-rate and clock-validity, and input-terminal connector. The endpoint uses IN flow control.
- Blinks the on-board LED to indicate USB state (not mounted / mounted / suspended).
## USB Descriptors
| Interface | Class driver |
|-----------|--------------|
| 0–1 | UAC2 audio (control + streaming), 4-channel microphone input |
## Configuration
Notable `tusb_config.h` settings:
```c
#define CFG_TUD_AUDIO 1
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // 16-bit
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
```
## Building
CMake:
```bash
mkdir build && cd build
cmake -DBOARD=raspberry_pi_pico ..
cmake --build .
```
Make:
```bash
make BOARD=raspberry_pi_pico all
```
## FreeRTOS variant
A FreeRTOS build is in `examples/device/audio_4_channel_mic_freertos` — identical UAC2 4-channel microphone, with the device, audio (1 ms IN-endpoint writes), and LED-blink work split across FreeRTOS tasks.
## Try it
The device appears as a USB microphone with four channels. On Linux, list it with `arecord -l` and capture with `arecord` (for example `arecord -D hw:CARD=MicNode4Ch -c 4 -f S16_LE -r 48000 test.wav`). The included `src/plot_audio_samples.py` records and plots the generated waveforms (requires `sounddevice` and `matplotlib`).
@@ -0,0 +1,6 @@
mcu:SAMD11
mcu:SAME5X
mcu:SAMG
family:broadcom_64bit
family:espressif
mcu:CH583
@@ -0,0 +1,418 @@
/*
* 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
#include "tusb_config.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
/* 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 uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// 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
audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// 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];
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
// 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;
// Generate dummy data
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);
}
while (1) {
tud_task();// tinyusb device task
led_blinking_task();
audio_task();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// 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;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
// This task simulates an audio receive callback, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = tusb_time_millis_api();
if (start_ms == curr_ms) {
return; // not enough time
}
start_ms = curr_ms;
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);
}
//--------------------------------------------------------------------+
// 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 == AUDIO20_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 == AUDIO20_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 == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio20_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 AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = (audio20_channel_config_t) 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 AUDIO20_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 AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
case AUDIO20_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 AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio20_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 AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
// 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 AUDIO20_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 AUDIO20_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
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return; // not enough time
}
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state;// toggle
}
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
def find_windows_input_device(name_hint, channels, preferred_apis=None):
"""Pick a Windows input device index from query_devices() output."""
preferred_apis = preferred_apis or []
name_hint = name_hint.lower()
candidates = []
for index in range(len(sd.query_devices())):
device_info = sd.query_devices(index)
max_input_channels = int(device_info.get('max_input_channels', 0))
if max_input_channels < channels:
continue
device_name = str(device_info.get('name', '')).lower()
if name_hint not in device_name:
continue
score = 0
# Prefer exact channel matches to avoid selecting a different stream layout.
if max_input_channels == channels:
score += 100
else:
score += 10
hostapi_index = int(device_info.get('hostapi', -1))
api_name = ''
if hostapi_index >= 0:
api_name = str(sd.query_hostapis(hostapi_index)).lower()
for priority, api_hint in enumerate(preferred_apis):
if api_hint in api_name:
score += 50 - priority
break
candidates.append((score, index))
if not candidates:
raise ValueError(
'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
)
return max(candidates, key=lambda item: item[0])[1]
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 = 1 # Duration of recording
if platform.system() == 'Windows':
# Match by substring to support names like "Microphone (2- MicNode_4_Ch)".
device = find_windows_input_device('micnode_4_ch', channels=4, preferred_apis=['wasapi', 'wdm-ks', 'mme'])
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
# strip starting zero
plt.plot(time, myrecording)
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('MicNode 4 Channel')
plt.legend(['CH-1', 'CH-2', 'CH-3','CH-4'])
plt.show()
@@ -0,0 +1,122 @@
/*
* 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
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* 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_AUDIO 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#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_SAMPLE_RATE 48000
#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(TUD_OPT_HIGH_SPEED, 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_EP_IN_FLOW_CONTROL 1
#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
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */
@@ -0,0 +1,191 @@
/*
* 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 "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.
* 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) ? (1 << (n)) : 0)
#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
//--------------------------------------------------------------------+
static 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 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,
.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_AUDIO20_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
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
// Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
#define EPNUM_AUDIO 0x0A
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V307)
// Only EP3 is available for ISO
#define EPNUM_AUDIO 0x03
#else
#define EPNUM_AUDIO 0x01
#endif
uint8_t const desc_configuration[] =
{
// 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
TUD_AUDIO20_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
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// array of pointer to string descriptors
static 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) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
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
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);
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] = (uint16_t) str[i];
}
break;
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
# Need to set Espressif defaults before project() is called
if(FAMILY STREQUAL "espressif")
list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
endif()
project(audio_4_channel_mic_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Add libm for GCC
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_link_libraries(${PROJECT_NAME} PUBLIC m)
endif()
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT_NAME} freertos)
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}
@@ -0,0 +1,15 @@
RTOS = freertos
include ../../../hw/bsp/family_support.mk
INC += \
src \
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
include ../../../hw/bsp/family_rules.mk
@@ -0,0 +1,4 @@
CONFIG_IDF_CMAKE=y
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
@@ -0,0 +1,22 @@
mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
mcu:FT90X
mcu:SAMD11
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
mcu:STM32L0
board:lpcxpresso11u37
board:lpcxpresso1347
family:broadcom_32bit
family:broadcom_64bit
family:nuc121_125
family:hpmicro
@@ -0,0 +1,4 @@
# This file is for ESP-IDF only
idf_component_register(SRCS "main.c" "usb_descriptors.c"
INCLUDE_DIRS "."
REQUIRES boards tinyusb_src)
@@ -0,0 +1,486 @@
/*
* 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
#ifdef ESP_PLATFORM
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#define USBD_STACK_SIZE 4096
#else
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"
#include "timers.h"
// Increase stack size when debug log is enabled
#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
//--------------------------------------------------------------------+
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
/* 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 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
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
audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// 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];
void led_blinking_task(void *param);
void usb_device_task(void *param);
void audio_isr_task(void *param);
/*------------- MAIN -------------*/
int main(void) {
board_init();
// 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;
// Generate dummy data
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);
}
#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 - 2, usb_device_stack, &usb_device_taskdef);
// Audio receive (I2S) ISR simulation
// To simulate a ISR the priority is set to the highest
xTaskCreateStatic(audio_isr_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 - 2, NULL);
xTaskCreate(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
return 0;
}
#ifdef ESP_PLATFORM
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;
// init device stack on configured roothub port
// 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_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
// RTOS forever loop
while (1) {
// tinyusb device task
tud_task();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// 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;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
// This task simulates an audio receive ISR, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_isr_task(void *param) {
(void) param;
while (1) {
vTaskDelay(1);
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);
}
}
//--------------------------------------------------------------------+
// 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 == AUDIO20_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 == AUDIO20_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 == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" 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 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 AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = 0;
ret.iChannelNames = 0;
TU_LOG1(" 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 AUDIO20_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_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio20_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 AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Sample Freq.\r\n");
// 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 AUDIO20_CS_REQ_RANGE:
TU_LOG1(" 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 AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG1(" 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_LOG1(" Unsupported entity: %d\r\n", entityID);
return false;// Yet not implemented
}
///--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void *param) {
(void) param;
static bool led_state = false;
while (1) {
// Blink every interval ms
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
board_led_write(led_state);
led_state = 1 - led_state;// toggle
}
}
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
def find_windows_input_device(name_hint, channels, preferred_apis=None):
"""Pick a Windows input device index from query_devices() output."""
preferred_apis = preferred_apis or []
name_hint = name_hint.lower()
candidates = []
for index in range(len(sd.query_devices())):
device_info = sd.query_devices(index)
max_input_channels = int(device_info.get('max_input_channels', 0))
if max_input_channels < channels:
continue
device_name = str(device_info.get('name', '')).lower()
if name_hint not in device_name:
continue
score = 0
# Prefer exact channel matches to avoid selecting a different stream layout.
if max_input_channels == channels:
score += 100
else:
score += 10
hostapi_index = int(device_info.get('hostapi', -1))
api_name = ''
if hostapi_index >= 0:
hostapi_info = sd.query_hostapis(hostapi_index)
api_name = str(hostapi_info.get('name', '')).lower()
for priority, api_hint in enumerate(preferred_apis):
if api_hint in api_name:
score += 50 - priority
break
candidates.append((score, index))
if not candidates:
raise ValueError(
'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
)
return max(candidates, key=lambda item: item[0])[1]
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':
# Match by substring to support names like "Microphone (2- MicNode_4_Ch)".
device = find_windows_input_device('micnode_4_ch', channels=4, preferred_apis=['wdm-ks', 'wasapi', 'mme'])
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()
@@ -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
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
// This examples use FreeRTOS
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_FREERTOS
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* 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_AUDIO 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#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_SAMPLE_RATE 48000
#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(TUD_OPT_HIGH_SPEED, 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_EP_IN_FLOW_CONTROL 1
#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
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */
@@ -0,0 +1,187 @@
/*
* 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 "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.
* 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) ? (1 << (n)) : 0)
#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
//--------------------------------------------------------------------+
static 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 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,
.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_AUDIO20_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
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
// Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
#define EPNUM_AUDIO 0x0A
#else
#define EPNUM_AUDIO 0x01
#endif
uint8_t const desc_configuration[] =
{
// 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
TUD_AUDIO20_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
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// 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) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
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
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);
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] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(audio_test C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT_NAME} noos)
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}
+11
View File
@@ -0,0 +1,11 @@
include ../../../hw/bsp/family_support.mk
INC += \
src \
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
include ../../../hw/bsp/family_rules.mk
@@ -0,0 +1,54 @@
# Audio Test Microphone
A minimal USB Audio Class 2.0 (UAC2) microphone that streams a generated test signal, useful for verifying the audio device stack end to end.
## What it does
- Enumerates as a UAC2 microphone with 1 input channel at 48 kHz, 16-bit.
- An audio task fills a buffer with a continuously incrementing 16-bit ramp counter and writes it to the IN endpoint every 1 ms, simulating data from an I2S source. The counter resets when the streaming interface is closed.
- Handles UAC2 control requests: feature-unit mute/volume, clock-source sample-rate and clock-validity, and input-terminal connector.
- Blinks the on-board LED to indicate USB state (not mounted / mounted / suspended).
## USB Descriptors
| Interface | Class driver |
|-----------|--------------|
| 0–1 | UAC2 audio (control + streaming), 1-channel microphone input |
## Configuration
Notable `tusb_config.h` settings:
```c
#define CFG_TUD_AUDIO 1
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // 16-bit
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
```
## Building
CMake:
```bash
mkdir build && cd build
cmake -DBOARD=raspberry_pi_pico ..
cmake --build .
```
Make:
```bash
make BOARD=raspberry_pi_pico all
```
## FreeRTOS variant
A FreeRTOS build is in `examples/device/audio_test_freertos` — identical single-channel UAC2 test microphone, with the device, audio, and LED-blink work split across FreeRTOS tasks.
## Try it
The device appears as a single-channel USB microphone. On Linux, list it with `arecord -l` and capture with `arecord` (for example `arecord -D hw:CARD=MicNode -c 1 -f S16_LE -r 48000 test.wav`); the recorded samples form a rising ramp. The included `src/plot_audio_samples.py` records and plots the signal (requires `sounddevice` and `matplotlib`).
@@ -0,0 +1,5 @@
mcu:SAMD11
mcu:SAME5X
mcu:SAMG
family:espressif
mcu:CH583
@@ -0,0 +1,412 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
/* 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 uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// 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
audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
uint16_t startVal = 0;
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
// Init values
sampFreq = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
clkValid = 1;
sampleFreqRng.wNumSubRanges = 1;
sampleFreqRng.subrange[0].bMin = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
sampleFreqRng.subrange[0].bRes = 0;
while (1) {
tud_task();// tinyusb device task
led_blinking_task();
audio_task();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// 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;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
// This task simulates an audio receive callback, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = tusb_time_millis_api();
if (start_ms == curr_ms) {
return; // not enough time
}
start_ms = curr_ms;
for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) {
test_buffer_audio[cnt] = startVal++;
}
tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio));
}
//--------------------------------------------------------------------+
// 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 == AUDIO20_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 == AUDIO20_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 == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio20_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 AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = (audio20_channel_config_t) 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 AUDIO20_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_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio20_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 AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_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_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
startVal = 0;
return true;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return; // not enough time
}
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state;// toggle
}
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
import csv
def find_windows_input_device(name_hint, channels, preferred_apis=None):
"""Pick a Windows input device index from query_devices() output."""
preferred_apis = preferred_apis or []
name_hint = name_hint.lower()
candidates = []
for index in range(len(sd.query_devices())):
device_info = sd.query_devices(index)
max_input_channels = int(device_info.get('max_input_channels', 0))
if max_input_channels < channels:
continue
device_name = str(device_info.get('name', '')).lower()
if name_hint not in device_name:
continue
score = 0
# Prefer exact channel matches (for example 1ch source over a 4ch source).
if max_input_channels == channels:
score += 100
else:
score += 10
hostapi_index = int(device_info.get('hostapi', -1))
api_name = ''
if hostapi_index >= 0:
hostapi_info = sd.query_hostapis(hostapi_index)
api_name = str(hostapi_info.get('name', '')).lower()
for priority, api_hint in enumerate(preferred_apis):
if api_hint in api_name:
score += 50 - priority
break
candidates.append((score, index))
if not candidates:
raise ValueError(
'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
)
return max(candidates, key=lambda item: item[0])[1]
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 = 3 # Duration of recording
if platform.system() == 'Windows':
# Match by substring to support names like "Microphone (2- MicNode)".
device = find_windows_input_device('micnode', channels=1, preferred_apis=['wasapi', 'mme', 'wdm-ks'])
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
device ='default'
myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=1, 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')
plt.show()
samples = np.array(myrecording)
np.savetxt('Output.csv', samples, delimiter=",", fmt='%s')
@@ -0,0 +1,122 @@
/*
* 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
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
// 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_AUDIO 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#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_SAMPLE_RATE 48000
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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_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
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */
@@ -0,0 +1,193 @@
/*
* 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 "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.
* 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) ? (1 << (n)) : 0)
#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
//--------------------------------------------------------------------+
static 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 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,
.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_AUDIO20_MIC_ONE_CH_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == 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
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
// Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
#define EPNUM_AUDIO 0x0A
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V307)
// Only EP3 is available for ISO
#define EPNUM_AUDIO 0x03
#else
#define EPNUM_AUDIO 0x01
#endif
uint8_t const desc_configuration[] =
{
// 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
TUD_AUDIO20_MIC_ONE_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
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// 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", // 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) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
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
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);
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] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
# Need to set Espressif defaults before project() is called
if(FAMILY STREQUAL "espressif")
list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
endif()
project(audio_test_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT_NAME} freertos)
@@ -0,0 +1,6 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}
@@ -0,0 +1,15 @@
RTOS = freertos
include ../../../hw/bsp/family_support.mk
INC += \
src \
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
include ../../../hw/bsp/family_rules.mk
@@ -0,0 +1,6 @@
CONFIG_IDF_CMAKE=y
# 1000 Hz tick: the UAC iso IN endpoint must be serviced every 1 ms frame;
# ESP-IDF's default 100 Hz starves the audio task -> host capture fails (arecord EIO).
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
@@ -0,0 +1,20 @@
mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
mcu:FT90X
mcu:SAMD11
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
family:broadcom_32bit
family:broadcom_64bit
board:stm32l0538disco
family:nuc121_125
family:hpmicro
@@ -0,0 +1,4 @@
# This file is for ESP-IDF only
idf_component_register(SRCS "main.c" "usb_descriptors.c"
INCLUDE_DIRS "."
REQUIRES boards tinyusb_src)
@@ -0,0 +1,480 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
#ifdef ESP_PLATFORM
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#define USBD_STACK_SIZE 4096
#else
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"
#include "timers.h"
// Increase stack size when debug log is enabled
#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
//--------------------------------------------------------------------+
/* 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 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
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
audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
uint16_t startVal = 0;
void led_blinking_task(void *param);
void usb_device_task(void *param);
void audio_isr_task(void *param);
/*------------- MAIN -------------*/
int main(void) {
board_init();
// Init values
sampFreq = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
clkValid = 1;
sampleFreqRng.wNumSubRanges = 1;
sampleFreqRng.subrange[0].bMin = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
sampleFreqRng.subrange[0].bRes = 0;
#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);
// Audio receive (I2S) ISR simulation
// To simulate a ISR the priority is set to the highest
xTaskCreateStatic(audio_isr_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_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
return 0;
}
#ifdef ESP_PLATFORM
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;
// init device stack on configured roothub port
// 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_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
// RTOS forever loop
while (1) {
// tinyusb device task
tud_task();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// 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;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
// This task simulates an audio receive ISR, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_isr_task(void *param) {
(void) param;
while (1) {
vTaskDelay(1);
for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) {
test_buffer_audio[cnt] = startVal++;
}
tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio));
}
}
//--------------------------------------------------------------------+
// 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 == AUDIO20_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 == AUDIO20_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 == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" 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 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 AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = (audio20_channel_config_t) 0;
ret.iChannelNames = 0;
TU_LOG1(" 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 AUDIO20_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_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio20_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 AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Sample Freq. range\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG1(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
TU_LOG1(" Unsupported entity: %d\r\n", entityID);
return false;// Yet not implemented
}
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
startVal = 0;
return true;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void *param) {
(void) param;
static bool led_state = false;
while (1) {
// Blink every interval ms
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
board_led_write(led_state);
led_state = 1 - led_state;// toggle
}
}
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
def find_windows_input_device(name_hint, channels, preferred_apis=None):
"""Pick a Windows input device index from query_devices() output."""
preferred_apis = preferred_apis or []
name_hint = name_hint.lower()
candidates = []
for index in range(len(sd.query_devices())):
device_info = sd.query_devices(index)
max_input_channels = int(device_info.get('max_input_channels', 0))
if max_input_channels < channels:
continue
device_name = str(device_info.get('name', '')).lower()
if name_hint not in device_name:
continue
score = 0
# Prefer exact channel matches (for example 1ch source over a 4ch source).
if max_input_channels == channels:
score += 100
else:
score += 10
hostapi_index = int(device_info.get('hostapi', -1))
api_name = ''
if hostapi_index >= 0:
hostapi_info = sd.query_hostapis(hostapi_index)
api_name = str(hostapi_info.get('name', '')).lower()
for priority, api_hint in enumerate(preferred_apis):
if api_hint in api_name:
score += 50 - priority
break
candidates.append((score, index))
if not candidates:
raise ValueError(
'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
)
return max(candidates, key=lambda item: item[0])[1]
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 = 3 # Duration of recording
if platform.system() == 'Windows':
# Match by substring to support names like "Microphone (2- MicNode)".
device = find_windows_input_device('micnode', channels=1, preferred_apis=['wasapi', 'mme', 'wdm-ks'])
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
device ='default'
myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=1, 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')
plt.show()
@@ -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
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
// This examples use FreeRTOS
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_FREERTOS
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
// 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_AUDIO 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#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_SAMPLE_RATE 48000
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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_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
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */
Loaded 100 of 2716 files, more files were not shown because too many files have changed in this diff. Show more