mirror of
https://github.com/hathach/tinyusb.git
synced 2026-02-18 05:05:47 +00:00
Merge branch 'master' into fork/ennebi/mtp
This commit is contained in:
@ -19,13 +19,12 @@ jobs:
|
||||
echo "MATRIX_JSON=$MATRIX_JSON"
|
||||
|
||||
BUILDSYSTEM_TOOLCHAIN=(
|
||||
"cmake aarch64-gcc"
|
||||
"cmake arm-clang"
|
||||
"make aarch64-gcc"
|
||||
"make arm-gcc"
|
||||
"make msp430-gcc"
|
||||
"make riscv-gcc"
|
||||
"make rx-gcc"
|
||||
"cmake arm-gcc"
|
||||
"cmake esp-idf"
|
||||
"cmake msp430-gcc"
|
||||
"cmake riscv-gcc"
|
||||
)
|
||||
|
||||
# only build IAR if not forked PR, since IAR token is not shared
|
||||
@ -33,7 +32,7 @@ jobs:
|
||||
BUILDSYSTEM_TOOLCHAIN+=("cmake arm-iar")
|
||||
fi
|
||||
|
||||
RESOURCE_LARGE='["nrf", "imxrt", "stm32f4", "stm32h7"]'
|
||||
RESOURCE_LARGE='["nrf", "imxrt", "stm32f4", "stm32h7 stm32h7rs"]'
|
||||
|
||||
gen_build_entry() {
|
||||
local build_system="$1"
|
||||
@ -67,7 +66,7 @@ jobs:
|
||||
FAMILY_LARGE=$(jq -n --argjson family "$FAMILY" --argjson resource "$RESOURCE_LARGE" '$family | map(select(IN($resource[])))')
|
||||
FAMILY=$(jq -n --argjson family "$FAMILY" --argjson resource "$RESOURCE_LARGE" '$family | map(select(IN($resource[]) | not))')
|
||||
|
||||
if [[ $toolchain == esp-idf ]]; then
|
||||
if [[ $toolchain == esp-idf || $toolchain == arm-iar ]]; then
|
||||
gen_build_entry "$build_system" "$toolchain" "$FAMILY" "large"
|
||||
else
|
||||
gen_build_entry "$build_system" "$toolchain" "$FAMILY" "medium+"
|
||||
|
||||
@ -10,17 +10,7 @@ commands:
|
||||
- run:
|
||||
name: Set toolchain url and key
|
||||
command: |
|
||||
TOOLCHAIN_JSON='{
|
||||
"aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz",
|
||||
"arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-19.1.1/LLVM-ET-Arm-19.1.1-Linux-x86_64.tar.xz",
|
||||
"arm-gcc": "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-linux-x64.tar.gz",
|
||||
"msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2",
|
||||
"riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz",
|
||||
"rx-gcc": "https://github.com/hathach/rx_device/releases/download/0.0.1/gcc-8.3.0.202411-GNURX-ELF.run",
|
||||
"arm-iar": "https://updates.iar.com/FileStore/STANDARD/001/003/322/cxarm-9.60.3.deb"
|
||||
}'
|
||||
toolchain_url=$(echo $TOOLCHAIN_JSON | jq -r '.["<< parameters.toolchain >>"]')
|
||||
|
||||
toolchain_url=$(jq -r '."<< parameters.toolchain >>"' .github/actions/setup_toolchain/toolchain.json)
|
||||
# only cache if not a github link
|
||||
if [[ $toolchain_url != "https://github.com"* ]]; then
|
||||
echo "<< parameters.toolchain >>-$toolchain_url" > toolchain_key
|
||||
@ -121,7 +111,6 @@ commands:
|
||||
TOOLCHAIN_OPTION="--toolchain clang"
|
||||
elif [ << parameters.toolchain >> == arm-iar ]; then
|
||||
TOOLCHAIN_OPTION="--toolchain iar"
|
||||
echo IAR_LMS_CLOUD_URL=$IAR_LMS_CLOUD_URL
|
||||
iccarm --version
|
||||
elif [ << parameters.toolchain >> == arm-gcc ]; then
|
||||
TOOLCHAIN_OPTION="--toolchain gcc"
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -22,10 +22,17 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Commit SHA
|
||||
placeholder: e.g 3a042b37da28d0ba1e5593eb1068ca5645d77b56 or version bundled by esp-idf or pico-sdk
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Board
|
||||
placeholder: e.g Feather nRF52840 Express
|
||||
placeholder: e.g Adafruit Feather nRF52840 Express
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -79,8 +86,8 @@ body:
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: I have checked existing issues, dicussion and documentation
|
||||
label: I have checked existing issues, discussion and documentation
|
||||
description: You agree to check all the resources above before opening a new issue.
|
||||
options:
|
||||
- label: I confirm I have checked existing issues, dicussion and documentation.
|
||||
- label: I confirm I have checked existing issues, discussion and documentation.
|
||||
required: true
|
||||
|
||||
13
.github/actions/setup_toolchain/action.yml
vendored
13
.github/actions/setup_toolchain/action.yml
vendored
@ -17,7 +17,7 @@ runs:
|
||||
if: inputs.toolchain == 'arm-gcc'
|
||||
uses: carlosperate/arm-none-eabi-gcc-action@v1
|
||||
with:
|
||||
release: '13.2.Rel1'
|
||||
release: '14.2.Rel1'
|
||||
|
||||
- name: Pull ESP-IDF docker
|
||||
if: inputs.toolchain == 'esp-idf'
|
||||
@ -28,18 +28,10 @@ runs:
|
||||
- name: Get Toolchain URL
|
||||
if: >-
|
||||
inputs.toolchain != 'arm-gcc' &&
|
||||
inputs.toolchain != 'arm-iar' &&
|
||||
inputs.toolchain != 'esp-idf'
|
||||
id: set-toolchain-url
|
||||
run: |
|
||||
TOOLCHAIN_JSON='{
|
||||
"aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz",
|
||||
"arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-19.1.1/LLVM-ET-Arm-19.1.1-Linux-x86_64.tar.xz",
|
||||
"msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2",
|
||||
"riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz",
|
||||
"rx-gcc": "https://github.com/hathach/rx_device/releases/download/0.0.1/gcc-8.3.0.202411-GNURX-ELF.run"
|
||||
}'
|
||||
TOOLCHAIN_URL=$(echo $TOOLCHAIN_JSON | jq -r '.["${{ inputs.toolchain }}"]')
|
||||
TOOLCHAIN_URL=$(jq -r '."${{ inputs.toolchain }}"' .github/actions/setup_toolchain/toolchain.json)
|
||||
echo "toolchain_url=$TOOLCHAIN_URL"
|
||||
echo "toolchain_url=$TOOLCHAIN_URL" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
@ -47,7 +39,6 @@ runs:
|
||||
- name: Download Toolchain
|
||||
if: >-
|
||||
inputs.toolchain != 'arm-gcc' &&
|
||||
inputs.toolchain != 'arm-iar' &&
|
||||
inputs.toolchain != 'esp-idf'
|
||||
uses: ./.github/actions/setup_toolchain/download
|
||||
with:
|
||||
|
||||
@ -23,17 +23,25 @@ runs:
|
||||
if: steps.cache-toolchain-download.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p ~/cache/${{ inputs.toolchain }}
|
||||
wget --progress=dot:giga ${{ inputs.toolchain_url }} -O toolchain.tar.gz
|
||||
|
||||
if [[ ${{ inputs.toolchain }} == rx-gcc ]]; then
|
||||
mv toolchain.tar.gz toolchain.run
|
||||
wget --progress=dot:giga ${{ inputs.toolchain_url }} -O toolchain.run
|
||||
chmod +x toolchain.run
|
||||
./toolchain.run -p ~/cache/${{ inputs.toolchain }}/gnurx -y
|
||||
elif [[ ${{ inputs.toolchain }} == arm-iar ]]; then
|
||||
wget --progress=dot:giga ${{ inputs.toolchain_url }} -O ~/cache/${{ inputs.toolchain }}/cxarm.deb
|
||||
else
|
||||
wget --progress=dot:giga ${{ inputs.toolchain_url }} -O toolchain.tar.gz
|
||||
tar -C ~/cache/${{ inputs.toolchain }} -xaf toolchain.tar.gz
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Set Toolchain Path
|
||||
- name: Setup Toolchain
|
||||
run: |
|
||||
echo >> $GITHUB_PATH `echo ~/cache/${{ inputs.toolchain }}/*/bin`
|
||||
if [[ ${{ inputs.toolchain }} == arm-iar ]]; then
|
||||
sudo apt-get install -y ~/cache/${{ inputs.toolchain }}/cxarm.deb
|
||||
echo >> $GITHUB_PATH "/opt/iar/cxarm/arm/bin"
|
||||
else
|
||||
echo >> $GITHUB_PATH `echo ~/cache/${{ inputs.toolchain }}/*/bin`
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
9
.github/actions/setup_toolchain/toolchain.json
vendored
Normal file
9
.github/actions/setup_toolchain/toolchain.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz",
|
||||
"arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-19.1.1/LLVM-ET-Arm-19.1.1-Linux-x86_64.tar.xz",
|
||||
"arm-gcc": "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v14.2.1-1.1/xpack-arm-none-eabi-gcc-14.2.1-1.1-linux-x64.tar.gz",
|
||||
"msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2",
|
||||
"riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz",
|
||||
"rx-gcc": "https://github.com/hathach/rx_device/releases/download/0.0.1/gcc-8.3.0.202411-GNURX-ELF.run",
|
||||
"arm-iar": "https://netstorage.iar.com/FileStore/STANDARD/001/003/583/cxarm-9.60.4.deb"
|
||||
}
|
||||
133
.github/copilot-instructions.md
vendored
Normal file
133
.github/copilot-instructions.md
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
# TinyUSB
|
||||
TinyUSB is an open-source cross-platform USB Host/Device stack for embedded systems, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events deferred to non-ISR task functions.
|
||||
|
||||
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
|
||||
|
||||
## Working Effectively
|
||||
|
||||
### Bootstrap and Build Setup
|
||||
- Install ARM GCC toolchain: `sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi`
|
||||
- Fetch core dependencies: `python3 tools/get_deps.py` -- takes <1 second. NEVER CANCEL.
|
||||
- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4)
|
||||
- Dependencies are cached in `lib/` and `hw/mcu/` directories
|
||||
|
||||
### Build Examples
|
||||
Choose ONE of these approaches:
|
||||
|
||||
**Option 1: Individual Example with CMake (RECOMMENDED)**
|
||||
```bash
|
||||
cd examples/device/cdc_msc
|
||||
mkdir -p build && cd build
|
||||
cmake -DBOARD=stm32f407disco -DCMAKE_BUILD_TYPE=MinSizeRel ..
|
||||
cmake --build . -j4
|
||||
```
|
||||
-- takes 1-2 seconds. NEVER CANCEL. Set timeout to 5+ minutes.
|
||||
|
||||
**Option 2: Individual Example with Make**
|
||||
```bash
|
||||
cd examples/device/cdc_msc
|
||||
make BOARD=stm32f407disco all
|
||||
```
|
||||
-- takes 2-3 seconds. NEVER CANCEL. Set timeout to 5+ minutes.
|
||||
|
||||
**Option 3: All Examples for a Board**
|
||||
```bash
|
||||
python3 tools/build.py -b BOARD_NAME
|
||||
```
|
||||
-- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes.
|
||||
|
||||
### Unit Testing
|
||||
- Install Ceedling: `sudo gem install ceedling`
|
||||
- Run all unit tests: `cd test/unit-test && ceedling` -- takes 4 seconds. NEVER CANCEL. Set timeout to 10+ minutes.
|
||||
- Tests use Unity framework with CMock for mocking
|
||||
|
||||
### Documentation
|
||||
- Install requirements: `pip install -r docs/requirements.txt`
|
||||
- Build docs: `cd docs && sphinx-build -b html . _build` -- takes 2-3 seconds. NEVER CANCEL. Set timeout to 10+ minutes.
|
||||
|
||||
### Code Quality and Validation
|
||||
- Format code: `clang-format -i path/to/file.c` (uses `.clang-format` config)
|
||||
- Check spelling: `pip install codespell && codespell` (uses `.codespellrc` config)
|
||||
- Pre-commit hooks validate unit tests and code quality automatically
|
||||
|
||||
## Validation
|
||||
|
||||
### ALWAYS Run These After Making Changes
|
||||
1. **Pre-commit validation** (RECOMMENDED): `pre-commit run --all-files`
|
||||
- Install pre-commit: `pip install pre-commit && pre-commit install`
|
||||
- Runs all quality checks, unit tests, spell checking, and formatting
|
||||
- Takes 10-15 seconds. NEVER CANCEL. Set timeout to 15+ minutes.
|
||||
2. **Build validation**: Build at least one example that exercises your changes
|
||||
```bash
|
||||
cd examples/device/cdc_msc
|
||||
make BOARD=stm32f407disco all
|
||||
```
|
||||
|
||||
### Manual Testing Scenarios
|
||||
- **Device examples**: Cannot be fully tested without real hardware, but must build successfully
|
||||
- **Unit tests**: Exercise core stack functionality - ALL tests must pass
|
||||
- **Build system**: Must be able to build examples for multiple board families
|
||||
|
||||
### Board Selection for Testing
|
||||
- **STM32F4**: `stm32f407disco` - no external SDK required, good for testing
|
||||
- **RP2040**: `pico_sdk` - requires Pico SDK, commonly used
|
||||
- **Other families**: Check `hw/bsp/FAMILY/boards/` for available boards
|
||||
|
||||
## Common Tasks and Time Expectations
|
||||
|
||||
### Repository Structure Quick Reference
|
||||
```
|
||||
├── src/ # Core TinyUSB stack
|
||||
│ ├── class/ # USB device classes (CDC, HID, MSC, Audio, etc.)
|
||||
│ ├── portable/ # MCU-specific drivers (organized by vendor)
|
||||
│ ├── device/ # USB device stack core
|
||||
│ ├── host/ # USB host stack core
|
||||
│ └── common/ # Shared utilities (FIFO, etc.)
|
||||
├── examples/ # Example applications
|
||||
│ ├── device/ # Device examples (cdc_msc, hid_generic, etc.)
|
||||
│ ├── host/ # Host examples
|
||||
│ └── dual/ # Dual-role examples
|
||||
├── hw/bsp/ # Board Support Packages
|
||||
│ └── FAMILY/boards/ # Board-specific configurations
|
||||
├── test/unit-test/ # Unit tests using Ceedling
|
||||
├── tools/ # Build and utility scripts
|
||||
└── docs/ # Sphinx documentation
|
||||
```
|
||||
|
||||
### Build Time Reference
|
||||
- **Dependency fetch**: <1 second
|
||||
- **Single example build**: 1-3 seconds
|
||||
- **Unit tests**: ~4 seconds
|
||||
- **Documentation build**: ~2.5 seconds
|
||||
- **Full board examples**: 15-20 seconds
|
||||
- **Toolchain installation**: 2-5 minutes (one-time)
|
||||
|
||||
### Key Files to Know
|
||||
- `tools/get_deps.py`: Manages dependencies for MCU families
|
||||
- `tools/build.py`: Builds multiple examples, supports make/cmake
|
||||
- `src/tusb.h`: Main TinyUSB header file
|
||||
- `src/tusb_config.h`: Configuration template
|
||||
- `examples/device/cdc_msc/`: Most commonly used example for testing
|
||||
- `test/unit-test/project.yml`: Ceedling test configuration
|
||||
|
||||
### Debugging Build Issues
|
||||
- **Missing compiler**: Install `gcc-arm-none-eabi` package
|
||||
- **Missing dependencies**: Run `python3 tools/get_deps.py FAMILY`
|
||||
- **Board not found**: Check `hw/bsp/FAMILY/boards/` for valid board names
|
||||
- **objcopy errors**: Often non-critical in full builds, try individual example builds
|
||||
|
||||
### Working with USB Device Classes
|
||||
- **CDC (Serial)**: `src/class/cdc/` - Virtual serial port
|
||||
- **HID**: `src/class/hid/` - Human Interface Device (keyboard, mouse, etc.)
|
||||
- **MSC**: `src/class/msc/` - Mass Storage Class (USB drive)
|
||||
- **Audio**: `src/class/audio/` - USB Audio Class
|
||||
- Each class has device (`*_device.c`) and host (`*_host.c`) implementations
|
||||
|
||||
### MCU Family Support
|
||||
- **STM32**: Largest support (F0, F1, F2, F3, F4, F7, G0, G4, H7, L4, U5, etc.)
|
||||
- **Raspberry Pi**: RP2040, RP2350 with PIO-USB host support
|
||||
- **NXP**: iMXRT, Kinetis, LPC families
|
||||
- **Microchip**: SAM D/E/G/L families
|
||||
- Check `hw/bsp/` for complete list and `docs/reference/boards.rst` for details
|
||||
|
||||
Remember: TinyUSB is designed for embedded systems - builds are fast, tests are focused, and the codebase is optimized for resource-constrained environments.
|
||||
195
.github/workflows/build.yml
vendored
195
.github/workflows/build.yml
vendored
@ -8,8 +8,8 @@ on:
|
||||
- 'examples/**'
|
||||
- 'lib/**'
|
||||
- 'hw/**'
|
||||
- 'tools/get_deps.py'
|
||||
- 'tools/build.py'
|
||||
- 'tools/get_deps.py'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/build.yml'
|
||||
- '.github/workflows/build_util.yml'
|
||||
@ -21,8 +21,9 @@ on:
|
||||
- 'examples/**'
|
||||
- 'lib/**'
|
||||
- 'hw/**'
|
||||
- 'tools/get_deps.py'
|
||||
- 'test/hil/**'
|
||||
- 'tools/build.py'
|
||||
- 'tools/get_deps.py'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/build.yml'
|
||||
- '.github/workflows/build_util.yml'
|
||||
@ -31,11 +32,15 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HIL_JSON: test/hil/tinyusb.json
|
||||
|
||||
jobs:
|
||||
set-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
json: ${{ steps.set-matrix-json.outputs.matrix }}
|
||||
hil_json: ${{ steps.set-matrix-json.outputs.hil_matrix }}
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
@ -43,33 +48,41 @@ jobs:
|
||||
- name: Generate matrix json
|
||||
id: set-matrix-json
|
||||
run: |
|
||||
# build matrix
|
||||
MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)
|
||||
echo "matrix=$MATRIX_JSON"
|
||||
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
# hil matrix
|
||||
HIL_MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py ${{ env.HIL_JSON }})
|
||||
echo "hil_matrix=$HIL_MATRIX_JSON"
|
||||
echo "hil_matrix=$HIL_MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
# ---------------------------------------
|
||||
# Build CMake
|
||||
# Build CMake: only build on push with one-per-family.
|
||||
# Full built is done by CircleCI in PR
|
||||
# ---------------------------------------
|
||||
cmake:
|
||||
if: github.event_name == 'push'
|
||||
needs: set-matrix
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
# - 'arm-clang' is built by circle-ci in PR
|
||||
- 'aarch64-gcc'
|
||||
#- 'arm-clang'
|
||||
- 'arm-gcc'
|
||||
- 'esp-idf'
|
||||
- 'msp430-gcc'
|
||||
- 'riscv-gcc'
|
||||
with:
|
||||
build-system: 'cmake'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
|
||||
one-per-family: ${{ github.event_name == 'push' }}
|
||||
one-per-family: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Build Make (built by circle-ci in PR, only build on push here)
|
||||
# Build Make: only build on push with one-per-family
|
||||
# ---------------------------------------
|
||||
make:
|
||||
if: github.event_name == 'push'
|
||||
@ -79,19 +92,40 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
# 'arm-clang'
|
||||
- 'arm-gcc'
|
||||
- 'aarch64-gcc'
|
||||
#- 'arm-clang'
|
||||
- 'arm-gcc'
|
||||
- 'msp430-gcc'
|
||||
- 'riscv-gcc'
|
||||
- 'rx-gcc'
|
||||
- 'esp-idf' # build-system is ignored
|
||||
with:
|
||||
build-system: 'make'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
|
||||
one-per-family: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Build IAR
|
||||
# Since IAR Token secret is not passed to forked PR, only build non-forked PR with make.
|
||||
# cmake is built by circle-ci. Due to IAR limit capacity, only build oe per family
|
||||
# ---------------------------------------
|
||||
arm-iar:
|
||||
if: false # disable for now since we got reach capacity limit too often
|
||||
#if: github.event_name == 'push' && github.repository_owner == 'hathach'
|
||||
needs: set-matrix
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
secrets: inherit
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-system:
|
||||
- 'make'
|
||||
with:
|
||||
build-system: ${{ matrix.build-system }}
|
||||
toolchain: 'arm-iar'
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)['arm-iar']) }}
|
||||
one-per-family: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Build Make on Windows/MacOS
|
||||
# ---------------------------------------
|
||||
@ -109,39 +143,6 @@ jobs:
|
||||
build-args: '["stm32h7"]'
|
||||
one-per-family: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Build IAR on HFP self-hosted
|
||||
# Since IAR Token secret is not passed to forked PR, only build on PR from the same repo
|
||||
# ---------------------------------------
|
||||
arm-iar:
|
||||
if: github.repository_owner == 'hathach' && github.event_name == 'push'
|
||||
needs: set-matrix
|
||||
runs-on: [self-hosted, Linux, X64, hifiphile]
|
||||
env:
|
||||
BUILD_ARGS: ${{ join(fromJSON(needs.set-matrix.outputs.json)['arm-iar'], ' ') }}
|
||||
IAR_LMS_CLOUD_URL: ${{ vars.IAR_LMS_CLOUD_URL }}
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up previous run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Toolchain version
|
||||
run: |
|
||||
echo IAR_LMS_CLOUD_URL=$IAR_LMS_CLOUD_URL
|
||||
iccarm --version
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get Dependencies
|
||||
run: python3 tools/get_deps.py $BUILD_ARGS
|
||||
|
||||
- name: Build
|
||||
run: python3 tools/build.py --one-per-family --toolchain iar $BUILD_ARGS
|
||||
|
||||
# ---------------------------------------
|
||||
# Zephyr
|
||||
# ---------------------------------------
|
||||
@ -162,3 +163,113 @@ jobs:
|
||||
run: |
|
||||
west build -b pca10056 -d examples/device/cdc_msc/build examples/device/cdc_msc -- -DRTOS=zephyr
|
||||
west build -b pca10056 -d examples/device/msc_dual_lun/build examples/device/msc_dual_lun -- -DRTOS=zephyr
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# Run on PR only (hil-tinyusb), hil-hfp only run on non-forked PR
|
||||
# ---------------------------------------
|
||||
hil-build:
|
||||
if: |
|
||||
github.repository_owner == 'hathach' &&
|
||||
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
|
||||
needs: set-matrix
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- 'arm-gcc'
|
||||
- 'esp-idf'
|
||||
with:
|
||||
build-system: 'cmake'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.hil_json)[matrix.toolchain]) }}
|
||||
one-per-family: true
|
||||
upload-artifacts: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted on local VM, for attached hardware checkout HIL_JSON
|
||||
# ---------------------------------------
|
||||
hil-tinyusb:
|
||||
if: |
|
||||
github.repository_owner == 'hathach' &&
|
||||
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
|
||||
needs: hil-build
|
||||
runs-on: [self-hosted, X64, hathach, hardware-in-the-loop]
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
if: github.run_attempt == '1'
|
||||
run: |
|
||||
echo "Cleaning up for the first run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
if: github.run_attempt == '1'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: test/hil
|
||||
|
||||
- name: Download Artifacts
|
||||
if: github.run_attempt == '1'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: cmake-build
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test on actual hardware
|
||||
run: |
|
||||
ls cmake-build/
|
||||
|
||||
# Skip boards that passed with previous run, file is generated by hil_test.py
|
||||
SKIP_BOARDS=""
|
||||
if [ -f ${{ env.HIL_JSON }}.skip ]; then
|
||||
SKIP_BOARDS=$(cat "${HIL_JSON}.skip")
|
||||
fi
|
||||
echo "SKIP_BOARDS=$SKIP_BOARDS"
|
||||
|
||||
python3 test/hil/hil_test.py ${{ env.HIL_JSON }} $SKIP_BOARDS
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted by HFP, build with IAR toolchain, for attached hardware checkout test/hil/hfp.json
|
||||
# Since IAR Token secret is not passed to forked PR, only build non-forked PR
|
||||
# ---------------------------------------
|
||||
hil-hfp:
|
||||
if: |
|
||||
github.repository_owner == 'hathach' &&
|
||||
github.event.pull_request.head.repo.fork == false &&
|
||||
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
|
||||
runs-on: [self-hosted, Linux, X64, hifiphile]
|
||||
env:
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up previous run"
|
||||
rm -rf "${{ github.workspace }}"3
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Toolchain version
|
||||
run: |
|
||||
iccarm --version
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get build boards
|
||||
run: |
|
||||
MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py test/hil/hfp.json)
|
||||
BUILD_ARGS=$(echo $MATRIX_JSON | jq -r '.["arm-gcc"] | join(" ")')
|
||||
echo "BUILD_ARGS=$BUILD_ARGS"
|
||||
echo "BUILD_ARGS=$BUILD_ARGS" >> $GITHUB_ENV
|
||||
|
||||
- name: Get Dependencies
|
||||
run: python3 tools/get_deps.py $BUILD_ARGS
|
||||
|
||||
- name: Build
|
||||
run: python3 tools/build.py -j 4 --toolchain iar $BUILD_ARGS
|
||||
|
||||
- name: Test on actual hardware (hardware in the loop)
|
||||
run: python3 test/hil/hil_test.py hfp.json
|
||||
|
||||
2
.github/workflows/build_util.yml
vendored
2
.github/workflows/build_util.yml
vendored
@ -58,6 +58,8 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
run: |
|
||||
if [ "${{ inputs.toolchain }}" == "esp-idf" ]; then
|
||||
docker run --rm -v $PWD:/project -w /project espressif/idf:tinyusb python tools/build.py ${{ matrix.arg }}
|
||||
|
||||
16
.github/workflows/ci_set_matrix.py
vendored
16
.github/workflows/ci_set_matrix.py
vendored
@ -15,16 +15,17 @@ toolchain_list = [
|
||||
|
||||
# family: [supported toolchain]
|
||||
family_list = {
|
||||
"at32f402_405 at32f403a_407 at32f413 at32f415 at32f423 at32f425 at32f435_437": ["arm-gcc"],
|
||||
"broadcom_32bit": ["arm-gcc"],
|
||||
"broadcom_64bit": ["aarch64-gcc"],
|
||||
"ch32v10x ch32v20x ch32v307 fomu gd32vf103": ["riscv-gcc"],
|
||||
"ch32v10x ch32v20x ch32v30x fomu gd32vf103": ["riscv-gcc"],
|
||||
"da1469x": ["arm-gcc"],
|
||||
"imxrt": ["arm-gcc", "arm-clang"],
|
||||
"kinetis_k kinetis_kl kinetis_k32l2": ["arm-gcc", "arm-clang"],
|
||||
"lpc11 lpc13 lpc15": ["arm-gcc", "arm-clang"],
|
||||
"lpc17 lpc18 lpc40 lpc43": ["arm-gcc", "arm-clang"],
|
||||
"lpc51 lpc54 lpc55": ["arm-gcc", "arm-clang"],
|
||||
"max32650 max32666 max32690 max78002": ["arm-gcc"],
|
||||
"maxim": ["arm-gcc"],
|
||||
"mcx": ["arm-gcc"],
|
||||
"mm32": ["arm-gcc"],
|
||||
"msp430": ["msp430-gcc"],
|
||||
@ -40,13 +41,16 @@ family_list = {
|
||||
"stm32f4": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f7": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32g0 stm32g4 stm32h5": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32h7": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32h7 stm32h7rs": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32l0 stm32l4": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32n6": ["arm-gcc"],
|
||||
"stm32u5 stm32wb": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32wba": ["arm-gcc", "arm-clang"],
|
||||
"xmc4000": ["arm-gcc"],
|
||||
"-bespressif_kaluga_1": ["esp-idf"],
|
||||
"-bespressif_s3_devkitm": ["esp-idf"],
|
||||
"-bespressif_p4_function_ev": ["esp-idf"],
|
||||
"-bespressif_s2_devkitc": ["esp-idf"],
|
||||
# S3, P4 will be built by hil test
|
||||
# "-bespressif_s3_devkitm": ["esp-idf"],
|
||||
# "-bespressif_p4_function_ev": ["esp-idf"],
|
||||
}
|
||||
|
||||
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@ -93,7 +93,7 @@ jobs:
|
||||
./.github/workflows/codeql-buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
upload: false
|
||||
@ -124,12 +124,12 @@ jobs:
|
||||
output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: ${{ steps.step1.outputs.sarif-output }}
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
- name: Archive CodeQL results
|
||||
- name: Upload CodeQL results as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-results
|
||||
|
||||
130
.github/workflows/hil_test.yml
vendored
130
.github/workflows/hil_test.yml
vendored
@ -1,130 +0,0 @@
|
||||
name: Hardware Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'examples/**'
|
||||
- 'lib/**'
|
||||
- 'hw/**'
|
||||
- 'test/hil/**'
|
||||
- 'tools/get_deps.py'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/hil_test.yml'
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HIL_JSON: test/hil/tinyusb.json
|
||||
|
||||
jobs:
|
||||
set-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
json: ${{ steps.set-matrix-json.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate matrix json
|
||||
id: set-matrix-json
|
||||
run: |
|
||||
MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py ${{ env.HIL_JSON }})
|
||||
echo "matrix=$MATRIX_JSON"
|
||||
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
# ---------------------------------------
|
||||
# Build arm-gcc
|
||||
# ---------------------------------------
|
||||
build:
|
||||
if: github.repository_owner == 'hathach'
|
||||
needs: set-matrix
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- 'arm-gcc'
|
||||
- 'esp-idf'
|
||||
with:
|
||||
build-system: 'cmake'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
|
||||
one-per-family: true
|
||||
upload-artifacts: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted on local VM, for attached hardware checkout HIL_JSON
|
||||
# ---------------------------------------
|
||||
hil-tinyusb:
|
||||
if: github.repository_owner == 'hathach'
|
||||
needs: build
|
||||
runs-on: [self-hosted, X64, hathach, hardware-in-the-loop]
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up previous run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: test/hil
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: cmake-build
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test on actual hardware
|
||||
run: |
|
||||
ls cmake-build/
|
||||
python3 test/hil/hil_test.py ${{ env.HIL_JSON }}
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted by HFP, build with IAR toolchain, for attached hardware checkout test/hil/hfp.json
|
||||
# Since IAR Token secret is not passed to forked PR, only build on PR from the same repo
|
||||
# ---------------------------------------
|
||||
hil-hfp:
|
||||
if: github.repository_owner == 'hathach' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: [self-hosted, Linux, X64, hifiphile]
|
||||
env:
|
||||
IAR_LMS_CLOUD_URL: ${{ vars.IAR_LMS_CLOUD_URL }}
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up previous run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Toolchain version
|
||||
run: |
|
||||
echo IAR_LMS_CLOUD_URL=$IAR_LMS_CLOUD_URL
|
||||
iccarm --version
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get build boards
|
||||
run: |
|
||||
MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py test/hil/hfp.json)
|
||||
BUILD_ARGS=$(echo $MATRIX_JSON | jq -r '.["arm-gcc"] | join(" ")')
|
||||
echo "BUILD_ARGS=$BUILD_ARGS"
|
||||
echo "BUILD_ARGS=$BUILD_ARGS" >> $GITHUB_ENV
|
||||
|
||||
- name: Get Dependencies
|
||||
run: python3 tools/get_deps.py $BUILD_ARGS
|
||||
|
||||
- name: Build
|
||||
run: python3 tools/build.py --toolchain iar $BUILD_ARGS
|
||||
|
||||
- name: Test on actual hardware (hardware in the loop)
|
||||
run: python3 test/hil/hil_test.py hfp.json
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -15,6 +15,14 @@ latex
|
||||
*.jlink
|
||||
*.emSession
|
||||
*.ninja*
|
||||
*.eww
|
||||
*.ewp
|
||||
*.ewt
|
||||
*.ewd
|
||||
*.hex
|
||||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
settings/
|
||||
.settings/
|
||||
.vscode/
|
||||
.gdb_history
|
||||
@ -44,3 +52,4 @@ RelWithDebInfo
|
||||
Release
|
||||
BrowseInfo
|
||||
.cmake_build
|
||||
README_processed.rst
|
||||
|
||||
19
.idea/cmake.xml
generated
19
.idea/cmake.xml
generated
@ -6,13 +6,13 @@
|
||||
<configuration PROFILE_NAME="raspberrypi_zero2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberrypi_zero2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberrypi_cm4" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberrypi_cm4 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2_riscv" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2_riscv -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico-pio_host" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="raspberry_pi_pico2-pio_host" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=raspberry_pi_pico2 -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="feather_rp2040" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pico_sdk -DPICO_BOARD=adafruit_feather_rp2040 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="feather_rp2040_max3421" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=feather_rp2040_max3421 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="metro_rp2040" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=pico_sdk -DPICO_BOARD=adafruit_metro_rp2040 -DLOG=1 -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="adafruit_metro_rp2350" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=adafruit_metro_rp2350 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="adafruit_metro_rp2350" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=adafruit_metro_rp2350 -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="adafruit_fruit_jam" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=adafruit_fruit_jam -DLOG=1 -DCFLAGS_CLI="-DCFG_TUH_RPI_PIO_USB=1"" />
|
||||
<configuration PROFILE_NAME="adafruit_feather_esp32_v2" ENABLED="false" TOOLCHAIN_NAME="ESP-IDF" GENERATION_OPTIONS="-DBOARD=adafruit_feather_esp32_v2 -DMAX3421_HOST=1 -DLOG=1">
|
||||
<ADDITIONAL_GENERATION_ENVIRONMENT>
|
||||
@ -78,11 +78,13 @@
|
||||
</ADDITIONAL_GENERATION_ENVIRONMENT>
|
||||
</configuration>
|
||||
<configuration PROFILE_NAME="feather_m0_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=feather_m0_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="metro_m0_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m0_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="metro_m0_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m0_express -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="metro_m0_express-max3421" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m0_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="samd11_xplained" ENABLED="false" CONFIG_NAME="MinSizeRel" GENERATION_OPTIONS="-DBOARD=samd11_xplained" />
|
||||
<configuration PROFILE_NAME="atsaml21_xpro" ENABLED="false" GENERATION_OPTIONS="-DBOARD=atsaml21_xpro" />
|
||||
<configuration PROFILE_NAME="feather_m4_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=feather_m4_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="metro_m4_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m4_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="metro_m4_express" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m4_express -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="metro_m4_express-max3421" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m4_express -DLOG=1 -DLOGGER=RTT -DMAX3421_HOST=1" />
|
||||
<configuration PROFILE_NAME="feather_m4_express-zephyr" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=feather_m4_express -DLOG=1 -DMAX3421_HOST=1 -DRTOS=zephyr" />
|
||||
<configuration PROFILE_NAME="itsybitsy_m4" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=itsybitsy_m4" />
|
||||
<configuration PROFILE_NAME="same54_xplained" ENABLED="false" GENERATION_OPTIONS="-DBOARD=same54_xplained -DLOG=1 -DLOGGER=RTT" />
|
||||
@ -94,7 +96,8 @@
|
||||
<configuration PROFILE_NAME="metro m7 1011 sd" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m7_1011_sd -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1" />
|
||||
<configuration PROFILE_NAME="metro_m7_1011" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=metro_m7_1011 -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="rt1010 evk" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1010_evk -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="mimxrt1060_evk" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1060_evk -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="mimxrt1060_evk" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1060_evk -DLOG=1" />
|
||||
<configuration PROFILE_NAME="mimxrt1064_evk" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1064_evk" />
|
||||
<configuration PROFILE_NAME="rt1170 evkb" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1170_evkb -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1" />
|
||||
<configuration PROFILE_NAME="stm32f072disco" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32f072disco -DLOG=0 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="stm32f103_mini_2" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32f103_mini_2 -DLOG=1 -DLOGGGER=RTT" />
|
||||
@ -160,13 +163,15 @@
|
||||
<configuration PROFILE_NAME="ch32v203c_r0_1v0" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ch32v203c_r0_1v0 -DLOG=0" />
|
||||
<configuration PROFILE_NAME="ch32v203c_r0_1v0 USBFS" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ch32v203c_r0_1v0 -DPORT=1" />
|
||||
<configuration PROFILE_NAME="ch32v203g_r0_1v0" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=ch32v203g_r0_1v0" />
|
||||
<configuration PROFILE_NAME="nanoch32v305" ENABLED="false" GENERATION_OPTIONS="-DBOARD=nanoch32v305 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="ch32v307v_r1_1v0" ENABLED="false" GENERATION_OPTIONS="-DBOARD=ch32v307v_r1_1v0 -DLOG=1" />
|
||||
<configuration PROFILE_NAME="ch32v307v_r1_1v0 USBFS" ENABLED="false" GENERATION_OPTIONS="-DBOARD=ch32v307v_r1_1v0 -DSPEED=full" />
|
||||
<configuration PROFILE_NAME="da14695_dk_usb" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=da14695_dk_usb" />
|
||||
<configuration PROFILE_NAME="max32650fthr" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=max32650fthr -DLOG=0 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="max32666fthr" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=max32666fthr -DLOG=0 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="max32690evkit" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=max32690evkit -DLOG=1 -DLOGGER=RTT" />
|
||||
<configuration PROFILE_NAME="mimxrt1064_evk" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=mimxrt1064_evk" />
|
||||
<configuration PROFILE_NAME="at_start_f403a" ENABLED="false" GENERATION_OPTIONS="-DBOARD=at_start_f403a -DLOG=1" />
|
||||
<configuration PROFILE_NAME="at_start_f423" ENABLED="false" GENERATION_OPTIONS="-DBOARD=at_start_f423 -DLOG=1" />
|
||||
</configurations>
|
||||
</component>
|
||||
</project>
|
||||
13
.idea/debugServers/AT32F423VCT7.xml
generated
Normal file
13
.idea/debugServers/AT32F423VCT7.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="AT32F423VCT7" uniqueID="de4ea1de-6dcf-413e-a21f-aaeaeb0f3dbc">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="AT32F423VCT7" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/ST_LINK.xml
generated
Normal file
13
.idea/debugServers/ST_LINK.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<stlink-debug-target name="ST-LINK" uniqueID="300d1a6f-85c0-4eb3-9753-f033d01e2eff">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="$USER_HOME$/st/stm32cubeide_1.16.1/plugins/com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.linux64_2.1.400.202404281720/tools/bin/ST-LINK_gdbserver" programmer="$USER_HOME$/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin" />
|
||||
<st-link />
|
||||
<device interface="SWD" />
|
||||
<connection port="61234" warmup-ms="500" />
|
||||
<swo enabled="false" port="61235" />
|
||||
</stlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/at32f403acgu7.xml
generated
Normal file
13
.idea/debugServers/at32f403acgu7.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="at32f403acgu7" uniqueID="13a1c815-97d7-4b16-8fcc-564bddfe2270">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="AT32F403ACGU7" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
14
.idea/debugServers/esp32s2.xml
generated
Normal file
14
.idea/debugServers/esp32s2.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<component name="DebugServers">
|
||||
<generic-debug-target name="esp32s2" uniqueID="254eff00-2acf-48fe-b255-1d0c0c9c4a7a">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB">$USER_HOME$/.espressif/tools/xtensa-esp-elf-gdb/14.2_20240403/xtensa-esp-elf-gdb/bin/xtensa-esp32s2-elf-gdb</debugger>
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="$USER_HOME$/.espressif/tools/openocd-esp32/v0.12.0-esp32-20241016/openocd-esp32/bin/openocd" args="-f board/esp32s2-kaluga-1.cfg">
|
||||
<env />
|
||||
</gdbserver>
|
||||
<console port="4444" />
|
||||
<target download-type="NONE" reset-command="monitor reset halt" reset-before="false" />
|
||||
<connection extended-remote="false" remote-string="tcp::3333" warmup-ms="500" />
|
||||
</generic-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/max32690.xml
generated
Normal file
13
.idea/debugServers/max32690.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="max32690" uniqueID="cb5e7c25-cbda-4c6d-94e9-28a85a81ba66">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="MAX32690" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
14
.idea/debugServers/rp2040.xml
generated
Normal file
14
.idea/debugServers/rp2040.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<component name="DebugServers">
|
||||
<generic-debug-target name="rp2040" uniqueID="006ce655-8571-401e-a94b-6a4f6d519724">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/local/bin/openocd" args="-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"">
|
||||
<env />
|
||||
</gdbserver>
|
||||
<console enabled="true" port="4444" />
|
||||
<target download-type="UPDATED_ONLY" reset-before="false" />
|
||||
<connection extended-remote="false" remote-string="tcp::3333" warmup-ms="500" />
|
||||
</generic-debug-target>
|
||||
</component>
|
||||
14
.idea/debugServers/rp2350.xml
generated
Normal file
14
.idea/debugServers/rp2350.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<component name="DebugServers">
|
||||
<generic-debug-target name="rp2350" uniqueID="939fdf16-9c30-4261-8435-3e8df7fd5800">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/local/bin/openocd" args="-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"">
|
||||
<env />
|
||||
</gdbserver>
|
||||
<console enabled="true" port="4444" />
|
||||
<target download-type="UPDATED_ONLY" reset-before="false" />
|
||||
<connection extended-remote="false" remote-string="tcp::3333" warmup-ms="500" />
|
||||
</generic-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/rt1060.xml
generated
Normal file
13
.idea/debugServers/rt1060.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="rt1060" uniqueID="851396c6-2030-4694-b86d-21ba9547ddcb">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="MIMXRT1062xxx6A" reset-before="false" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/rt1064.xml
generated
Normal file
13
.idea/debugServers/rt1064.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="rt1064" uniqueID="9602472b-6ce8-4a2d-9636-1c03b5fcd6da">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="MIMXRT1064xxx6A" reset-before="false" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
6
.idea/debugServers/s3.xml
generated
Normal file
6
.idea/debugServers/s3.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="DebugServers">
|
||||
<esp-idf-debug-target name="s3" uniqueID="e096f0d4-5923-482d-bcc3-169a2cfd7cdc">
|
||||
<console enabled="false" />
|
||||
<target />
|
||||
</esp-idf-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/sam21.xml
generated
Normal file
13
.idea/debugServers/sam21.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="sam21" uniqueID="b22537b6-0924-4a2b-8721-48a8952239de">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="ATSAMD21G18A" reset-before="false" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/sam51.xml
generated
Normal file
13
.idea/debugServers/sam51.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="sam51" uniqueID="481ff0d4-6501-4394-8b6b-f7a2ca4c2675">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="ATSAMD51J19A" reset-before="false" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/stm32f769.xml
generated
Normal file
13
.idea/debugServers/stm32f769.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="stm32f769" uniqueID="7a47302f-f7e5-434b-b20b-78e95318dd0c">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="STM32F769NI" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/stm32h563.xml
generated
Normal file
13
.idea/debugServers/stm32h563.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="stm32h563" uniqueID="a3e9293d-113b-48b3-b83d-dd4249984abe">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="STM32H562ZI" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
13
.idea/debugServers/stm32h743.xml
generated
Normal file
13
.idea/debugServers/stm32h743.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="DebugServers">
|
||||
<jlink-debug-target name="stm32h743" uniqueID="6d6a3ed6-f66d-4f6a-9e70-6aafe5c971d0">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver exe="/usr/bin/JLinkGDBServerCLExe" />
|
||||
<console port="19021" />
|
||||
<target device="STM32H743XI" reset-before="false" frequency="16000" />
|
||||
<connection extended-remote="false" port="4444" warmup-ms="500" />
|
||||
<swo />
|
||||
</jlink-debug-target>
|
||||
</component>
|
||||
14
.idea/debugServers/wch_riscv.xml
generated
Normal file
14
.idea/debugServers/wch_riscv.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<component name="DebugServers">
|
||||
<generic-debug-target name="wch-riscv" uniqueID="c471e2d0-3cb4-4e7e-aeb7-a33d0c8fdc08">
|
||||
<debugger version="1">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<env />
|
||||
</debugger>
|
||||
<gdbserver dir="$CMakeProjectDir$" exe="$USER_HOME$/app/riscv-openocd-wch/src/openocd" args="-f hw/bsp/ch32v20x/wch-riscv.cfg">
|
||||
<env />
|
||||
</gdbserver>
|
||||
<console enabled="true" port="4444" />
|
||||
<target download-type="UPDATED_ONLY" reset-command="monitor reset init; resume 0x08000000" reset-before="false" />
|
||||
<connection extended-remote="false" remote-string="tcp::3333" warmup-ms="500" />
|
||||
</generic-debug-target>
|
||||
</component>
|
||||
@ -31,6 +31,13 @@ Notable contributors
|
||||
- Most features development
|
||||
|
||||
|
||||
`Heiko Kuester <https://github.com/IngHK>`__
|
||||
--------------------------------------------
|
||||
|
||||
- Add CH34x and PL2303 support (CDC host)
|
||||
- Improve FTDI and CP210x support (CDC host)
|
||||
|
||||
|
||||
`Hristo Gochkov <https://github.com/me-no-dev>`__
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
47
README.rst
47
README.rst
@ -15,10 +15,7 @@ TinyUSB Project
|
||||
.. figure:: docs/assets/logo.svg
|
||||
:alt: TinyUSB
|
||||
|
||||
TinyUSB is an open-source cross-platform USB Host/Device stack for
|
||||
embedded system, designed to be memory-safe with no dynamic allocation
|
||||
and thread-safe with all interrupt events are deferred then handled in
|
||||
the non-ISR task function. Check out the online `documentation <https://docs.tinyusb.org/>`__ for more details.
|
||||
TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function. Check out the online `documentation <https://docs.tinyusb.org/>`__ for more details.
|
||||
|
||||
.. figure:: docs/assets/stack.svg
|
||||
:width: 500px
|
||||
@ -32,7 +29,7 @@ the non-ISR task function. Check out the online `documentation <https://docs.tin
|
||||
├── hw
|
||||
│ ├── bsp # Supported boards source files
|
||||
│ └── mcu # Low level mcu core & peripheral drivers
|
||||
├── lib # Sources from 3rd party such as freeRTOS, fatfs ...
|
||||
├── lib # Sources from 3rd party such as FreeRTOS, FatFs ...
|
||||
├── src # All sources files for TinyUSB stack itself.
|
||||
├── test # Tests: unit test, fuzzing, hardware test
|
||||
└── tools # Files used internally
|
||||
@ -43,7 +40,7 @@ Getting started
|
||||
|
||||
See the `online documentation <https://docs.tinyusb.org>`_ for information about using TinyUSB and how it is implemented.
|
||||
|
||||
Check out `Getting Started`_ guide for adding TinyUSB to your project or building the examples. If you are new to TinyUSB, we recommend starting with the `cdc_msc` example. There is a handful of `Supported Boards`_ that should work out of the box.
|
||||
Check out `Getting Started`_ guide for adding TinyUSB to your project or building the examples. If you are new to TinyUSB, we recommend starting with the ``cdc_msc`` example. There is a handful of `Supported Boards`_ that should work out of the box.
|
||||
|
||||
We use `GitHub Discussions <https://github.com/hathach/tinyusb/discussions>`_ as our forum. It is a great place to ask questions and advice from the community or to discuss your TinyUSB-based projects.
|
||||
|
||||
@ -69,7 +66,7 @@ Supports multiple device configurations by dynamically changing USB descriptors,
|
||||
- Vendor-specific class support with generic In & Out endpoints. Can be used with MS OS 2.0 compatible descriptor to load winUSB driver without INF file.
|
||||
- `WebUSB <https://github.com/WICG/webusb>`__ with vendor-specific class
|
||||
|
||||
If you have a special requirement, `usbd_app_driver_get_cb()` can be used to write your own class driver without modifying the stack. Here is how the RPi team added their reset interface `raspberrypi/pico-sdk#197 <https://github.com/raspberrypi/pico-sdk/pull/197>`_
|
||||
If you have a special requirement, ``usbd_app_driver_get_cb()`` can be used to write your own class driver without modifying the stack. Here is how the RPi team added their reset interface `raspberrypi/pico-sdk#197 <https://github.com/raspberrypi/pico-sdk/pull/197>`_
|
||||
|
||||
Host Stack
|
||||
==========
|
||||
@ -77,10 +74,10 @@ Host Stack
|
||||
- Human Interface Device (HID): Keyboard, Mouse, Generic
|
||||
- Mass Storage Class (MSC)
|
||||
- Communication Device Class: CDC-ACM
|
||||
- Vendor serial over USB: FTDI, CP210x, CH34x
|
||||
- Vendor serial over USB: FTDI, CP210x, CH34x, PL2303
|
||||
- Hub with multiple-level support
|
||||
|
||||
Similar to the Device Stack, if you have a special requirement, `usbh_app_driver_get_cb()` can be used to write your own class driver without modifying the stack.
|
||||
Similar to the Device Stack, if you have a special requirement, ``usbh_app_driver_get_cb()`` can be used to write your own class driver without modifying the stack.
|
||||
|
||||
Power Delivery Stack
|
||||
====================
|
||||
@ -112,6 +109,12 @@ Supported CPUs
|
||||
| | MAX32 650, 666, 690, | ✔ | | ✔ | musb | 1-dir ep |
|
||||
| | MAX78002 | | | | | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| Artery AT32 | F403a_407, F413 | ✔ | | | fsdev | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | F415, F435_437, F423, F425 | ✔ | ✔ | | dwc2 | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | F402_F405 | ✔ | ✔ | ✔ | dwc2 | F405 is HS |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| Brigetek | FT90x | ✔ | | ✔ | ft9xx | 1-dir ep |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| Broadcom | BCM2711, BCM2837 | ✔ | | ✔ | dwc2 | |
|
||||
@ -170,7 +173,9 @@ Supported CPUs
|
||||
| | +-------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | 54, 55 | ✔ | | ✔ | lpc_ip3511 | |
|
||||
| +---------+-------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | MCX | N9, A15 | ✔ | | ✔ | ci_fs, ci_hs | |
|
||||
| | MCX | N9 | ✔ | | ✔ | ci_fs, ci_hs | |
|
||||
| | +-------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | A15 | ✔ | | | ci_fs | |
|
||||
+--------------+---------+-------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| Raspberry Pi | RP2040, RP2350 | ✔ | ✔ | ✖ | rp2040, pio_usb | |
|
||||
+--------------+-----+-----------------------+--------+------+-----------+------------------------+-------------------+
|
||||
@ -184,15 +189,13 @@ Supported CPUs
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| Sony | CXD56 | ✔ | ✖ | ✔ | cxd56 | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| ST STM32 | F0 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| ST STM32 | F0, F3, L0, L1, L5, WBx5 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | F1 | 102, 103 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| | +------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | 105, 107 | ✔ | ✔ | ✖ | dwc2 | |
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | F2, F4, F7, H7 | ✔ | ✔ | ✔ | dwc2 | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | F3 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| | F2, F4, F7, H7, H7RS | ✔ | ✔ | ✔ | dwc2 | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | C0, G0, H5 | ✔ | | ✖ | stm32_fsdev | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
@ -202,25 +205,19 @@ Supported CPUs
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | L4 | 4x2, 4x3 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| | +------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | 4x5, 4x6 | ✔ | ✔ | ✖ | dwc2 | |
|
||||
| | | 4x5, 4x6, 4+ | ✔ | ✔ | ✖ | dwc2 | |
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | L4+ | ✔ | ✔ | ✖ | dwc2 | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | L5 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
| | N6 | ✔ | ✔ | ✔ | dwc2 | |
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | U5 | 535, 545 | ✔ | | ✖ | stm32_fsdev | |
|
||||
| | +------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | 575, 585 | ✔ | ✔ | ✖ | dwc2 | |
|
||||
| | +------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | | 59x,5Ax,5Fx,5Gx | ✔ | ✔ | ✔ | dwc2 | |
|
||||
| +----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | WBx5 | ✔ | ✖ | ✖ | stm32_fsdev | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
+--------------+----+------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| TI | MSP430 | ✔ | ✖ | ✖ | msp430x5xx | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | MSP432E4 | ✔ | | ✖ | musb | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | TM4C123 | ✔ | | ✖ | musb | |
|
||||
| | MSP432E4, TM4C123 | ✔ | | ✖ | musb | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| ValentyUSB | eptri | ✔ | ✖ | ✖ | eptri | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
@ -228,7 +225,7 @@ Supported CPUs
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | CH32V20x | ✔ | | ✖ | stm32_fsdev/ch32_usbfs | |
|
||||
| +-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
| | CH32V307 | ✔ | | ✔ | ch32_usbfs/hs | |
|
||||
| | CH32V305, CH32V307 | ✔ | | ✔ | ch32_usbfs/hs | |
|
||||
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
|
||||
|
||||
Table Legend
|
||||
|
||||
@ -1 +0,0 @@
|
||||
.. include:: ../../CODE_OF_CONDUCT.rst
|
||||
1
docs/contributing/code_of_conduct.rst
Normal file
1
docs/contributing/code_of_conduct.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../CODE_OF_CONDUCT.rst
|
||||
@ -9,7 +9,7 @@ data transactions on different endpoints. Porting is the process of adding low-l
|
||||
the rest of the common stack. Once the low-level is implemented, it is very easy to add USB support
|
||||
for the microcontroller to other projects, especially those already using TinyUSB such as CircuitPython.
|
||||
|
||||
Below are instructions on how to get the cdc_msc device example running on a new microcontroller. Doing so includes adding the common code necessary for other uses while minimizing other extra code. Whenever you see a phrase or word in <> it should be replaced.
|
||||
Below are instructions on how to get the cdc_msc device example running on a new microcontroller. Doing so includes adding the common code necessary for other uses while minimizing other extra code. Whenever you see a phrase or word in ``<>`` it should be replaced.
|
||||
|
||||
Register defs
|
||||
-------------
|
||||
@ -18,25 +18,27 @@ The first step to adding support is including the register definitions and start
|
||||
microcontroller in TinyUSB. We write the TinyUSB implementation against these structs instead of higher level functions to keep the code small and to prevent function name collisions in linking of larger projects. For ARM microcontrollers this is the CMSIS definitions. They should be
|
||||
placed in the ``hw/mcu/<vendor>/<chip_family>`` directory.
|
||||
|
||||
Once this is done, create a directory in ``hw/bsp/<your board name>`` for the specific board you are using to test the code. (Duplicating an existing board's directory is the best way to get started.) The board should be a readily available development board so that others can also test.
|
||||
Once this is done, create a directory in ``hw/bsp/<your board name>`` for the specific board you are using to test the code (duplicating an existing board's directory is the best way to get started). The board should be a readily available development board so that others can also test.
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
Now that those directories are in place, we can start our iteration process to get the example building successfully. To build, run from the root of TinyUSB:
|
||||
|
||||
``make -C examples/device/cdc_msc BOARD=<board>``
|
||||
.. code-block:: bash
|
||||
|
||||
Unless, you've read ahead, this will fail miserably. Now, lets get it to fail less by updating the files in the board directory. The code in the board's directory is responsible for setting up the microcontroller's clocks and pins so that USB works. TinyUSB itself only operates on the USB peripheral. The board directory also includes information what files are needed to build the example.
|
||||
make -C examples/device/cdc_msc BOARD=<board>
|
||||
|
||||
One of the first things to change is the ``-DCFG_TUSB_MCU`` cflag in the ``board.mk`` file. This is used to tell TinyUSB what platform is being built. So, add an entry to ``src/tusb_option.h`` and update the CFLAG to match.
|
||||
Unless you've read ahead, this will fail miserably. Now, lets get it to fail less by updating the files in the board directory. The code in the board's directory is responsible for setting up the microcontroller's clocks and pins so that USB works. TinyUSB itself only operates on the USB peripheral. The board directory also includes information what files are needed to build the example.
|
||||
|
||||
Update ``board.mk``\ 's VENDOR and CHIP_FAMILY values when creating the directory for the struct files. Duplicate one of the other sources from ``src/portable`` into ``src/portable/<vendor>/<chip_family>`` and delete all of the implementation internals. We'll cover what everything there does later. For now, get it compiling.
|
||||
One of the first things to change is the ``-DCFG_TUSB_MCU`` C flag in the ``board.mk`` file. This is used to tell TinyUSB what platform is being built. So, add an entry to ``src/tusb_option.h`` and update the ``CFLAGS`` to match.
|
||||
|
||||
Update ``board.mk``'s VENDOR and CHIP_FAMILY values when creating the directory for the struct files. Duplicate one of the other sources from ``src/portable`` into ``src/portable/<vendor>/<chip_family>`` and delete all of the implementation internals. We'll cover what everything there does later. For now, get it compiling.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
At this point you should get an error due to an implementation issue and hopefully the build is setup for the new MCU. You will still need to modify the ``board.mk`` to include specific CFLAGS, the linker script, linker flags, source files, include directories. All file paths are relative to the top of the TinyUSB repo.
|
||||
At this point you should get an error due to an implementation issue and hopefully the build is setup for the new MCU. You will still need to modify the ``board.mk`` to include specific ``CFLAGS``, the linker script, linker flags, source files, include directories. All file paths are relative to the top of the TinyUSB repo.
|
||||
|
||||
Board Support (BSP)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
@ -45,17 +47,17 @@ The board support code is only used for self-contained examples and testing. It
|
||||
|
||||
It is located in ``hw/bsp/<board name>/board_<board name>.c``.
|
||||
|
||||
board_init
|
||||
~~~~~~~~~~
|
||||
``board_init()``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
``board_init`` is responsible for starting the MCU, setting up the USB clock and USB pins. It is also responsible for initializing LED pins.
|
||||
``board_init()`` is responsible for starting the MCU, setting up the USB clock and USB pins. It is also responsible for initializing LED pins.
|
||||
|
||||
One useful clock debugging technique is to set up a PWM output at a known value such as 500hz based on the USB clock so that you can verify it is correct with a logic probe or oscilloscope.
|
||||
|
||||
Setup your USB in a crystal-less mode when available. That makes the code easier to port across boards.
|
||||
|
||||
board_led_write
|
||||
~~~~~~~~~~~~~~~
|
||||
``board_led_write()``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Feel free to skip this until you want to verify your demo code is running. To implement, set the pin corresponding to the led to output a value that lights the LED when ``state`` is true.
|
||||
|
||||
@ -64,48 +66,48 @@ OS Abstraction Layer (OSAL)
|
||||
|
||||
The OS Abstraction Layer is responsible for providing basic data structures for TinyUSB that may allow for concurrency when used with an RTOS. Without an RTOS it simply handles concurrency issues between the main code and interrupts. The code is almost entirely agnostic of MCU and lives in ``src/osal``.
|
||||
|
||||
In RTOS configurations, tud_task()/tuh_task() blocks behind a synchronization structure when the event queue is empty, so that the scheduler may give the CPU to a different task. To take advantage of the library's capability to yield the CPU when there are no actionable USB device events, ensure that the `CFG_TUSB_OS` symbol is defined, e.g `OPT_OS_FREERTOS` enables the FreeRTOS scheduler to schedule other threads than that which calls `tud_task()/tuh_task()`.
|
||||
In RTOS configurations, ``tud_task()``/``tuh_task()`` blocks behind a synchronization structure when the event queue is empty, so that the scheduler may give the CPU to a different task. To take advantage of the library's capability to yield the CPU when there are no actionable USB device events, ensure that the ``CFG_TUSB_OS`` symbol is defined, e.g ``OPT_OS_FREERTOS`` enables the FreeRTOS scheduler to schedule other threads than that which calls ``tud_task()``/``tuh_task()``.
|
||||
|
||||
Device API
|
||||
^^^^^^^^^^
|
||||
|
||||
After the USB device is setup, the USB device code works by processing events on the main thread (by calling ``tud_task``\ ). These events are queued by the USB interrupt handler. So, there are three parts to the device low-level API: device setup, endpoint setup and interrupt processing.
|
||||
After the USB device is setup, the USB device code works by processing events on the main thread (by calling ``tud_task()``). These events are queued by the USB interrupt handler. So, there are three parts to the device low-level API: device setup, endpoint setup and interrupt processing.
|
||||
|
||||
All of the code for the low-level device API is in ``src/portable/<vendor>/<chip family>/dcd_<chip family>.c``.
|
||||
|
||||
Device Setup
|
||||
~~~~~~~~~~~~
|
||||
|
||||
dcd_init
|
||||
""""""""
|
||||
``dcd_init()``
|
||||
""""""""""""""
|
||||
|
||||
Initializes the USB peripheral for device mode and enables it.
|
||||
This function should enable internal D+/D- pull-up for enumeration.
|
||||
|
||||
dcd_int_enable / dcd_int_disable
|
||||
""""""""""""""""""""""""""""""""
|
||||
``dcd_int_enable()`` / ``dcd_int_disable()``
|
||||
""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Enables or disables the USB device interrupt(s). May be used to prevent concurrency issues when mutating data structures shared between main code and the interrupt handler.
|
||||
|
||||
dcd_int_handler
|
||||
"""""""""""""""
|
||||
``dcd_int_handler()``
|
||||
"""""""""""""""""""""
|
||||
|
||||
Processes all the hardware generated events e.g Bus reset, new data packet from host etc ... It will be called by application in the MCU USB interrupt handler.
|
||||
|
||||
dcd_set_address
|
||||
"""""""""""""""
|
||||
``dcd_set_address()``
|
||||
"""""""""""""""""""""
|
||||
|
||||
Called when the device is given a new bus address.
|
||||
|
||||
If your peripheral automatically changes address during enumeration (like the nrf52) you may leave this empty and also no queue an event for the corresponding SETUP packet.
|
||||
|
||||
dcd_remote_wakeup
|
||||
"""""""""""""""""
|
||||
``dcd_remote_wakeup()``
|
||||
"""""""""""""""""""""""
|
||||
|
||||
Called to remote wake up host when suspended (e.g hid keyboard)
|
||||
|
||||
dcd_connect / dcd_disconnect
|
||||
""""""""""""""""""""""""""""
|
||||
``dcd_connect()`` / ``dcd_disconnect()``
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Connect or disconnect the data-line pull-up resistor. Define only if MCU has an internal pull-up. (BSP may define for MCU without internal pull-up.)
|
||||
|
||||
@ -114,8 +116,8 @@ Special events
|
||||
|
||||
You must let TinyUSB know when certain events occur so that it can continue its work. There are a few methods you can call to queue events for TinyUSB to process.
|
||||
|
||||
dcd_event_bus_signal
|
||||
""""""""""""""""""""
|
||||
``dcd_event_bus_signal()``
|
||||
""""""""""""""""""""""""""
|
||||
|
||||
There are a number of events that your peripheral may communicate about the state of the bus. Here is an overview of what they are. Events in **BOLD** must be provided for TinyUSB to work.
|
||||
|
||||
@ -125,51 +127,51 @@ There are a number of events that your peripheral may communicate about the stat
|
||||
|
||||
Calls to this look like:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: c
|
||||
|
||||
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
|
||||
|
||||
|
||||
The first ``0`` is the USB peripheral number. Statically saying 0 is common for single USB device MCUs.
|
||||
The first ``0`` is the USB peripheral number. Statically saying ``0`` is common for single USB device MCUs.
|
||||
|
||||
The ``true`` indicates the call is from an interrupt handler and will always be the case when porting in this way.
|
||||
|
||||
dcd_setup_received
|
||||
""""""""""""""""""
|
||||
``dcd_setup_received()``
|
||||
""""""""""""""""""""""""
|
||||
|
||||
SETUP packets are a special type of transaction that can occur at any time on the control endpoint, numbered ``0``. Since they are unique, most peripherals have special handling for them. Their data is always 8 bytes in length as well.
|
||||
|
||||
Calls to this look like:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: c
|
||||
|
||||
dcd_event_setup_received(0, setup, true);
|
||||
|
||||
|
||||
As before with ``dcd_event_bus_signal`` the first argument is the USB peripheral number and the third is true to signal its being called from an interrupt handler. The middle argument is byte array of length 8 with the contents of the SETUP packet. It can be stack allocated because it is copied into the queue.
|
||||
As before with ``dcd_event_bus_signal()`` the first argument is the USB peripheral number and the third is true to signal its being called from an interrupt handler. The middle argument is byte array of length 8 with the contents of the SETUP packet. It can be stack allocated because it is copied into the queue.
|
||||
|
||||
Endpoints
|
||||
~~~~~~~~~
|
||||
|
||||
Endpoints are the core of the USB data transfer process. They come in a few forms such as control, isochronous, bulk, and interrupt. We won't cover the details here except with some caveats in open below. In general, data is transferred by setting up a buffer of a given length to be transferred on a given endpoint address and then waiting for an interrupt to signal that the transfer is finished. Further details below.
|
||||
|
||||
Endpoints within USB have an address which encodes both the number and direction of an endpoint. TinyUSB provides ``tu_edpt_number`` and ``tu_edpt_dir`` to unpack this data from the address. Here is a snippet that does it.
|
||||
Endpoints within USB have an address which encodes both the number and direction of an endpoint. TinyUSB provides ``tu_edpt_number()`` and ``tu_edpt_dir()`` to unpack this data from the address. Here is a snippet that does it.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: c
|
||||
|
||||
uint8_t epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
|
||||
dcd_edpt_open
|
||||
"""""""""""""
|
||||
``dcd_edpt_open()``
|
||||
"""""""""""""""""""
|
||||
|
||||
Opening an endpoint is done for all non-control endpoints once the host picks a configuration that the device should use. At this point, the endpoint should be enabled in the peripheral and configured to match the endpoint descriptor. Pay special attention to the direction of the endpoint you can get from the helper methods above. It will likely change what registers you are setting.
|
||||
|
||||
Also make sure to enable endpoint specific interrupts.
|
||||
|
||||
dcd_edpt_close
|
||||
""""""""""""""
|
||||
``dcd_edpt_close()``
|
||||
""""""""""""""""""""
|
||||
|
||||
Close an endpoint. his function is used for implementing alternate settings.
|
||||
|
||||
@ -177,10 +179,10 @@ After calling this, the device should not respond to any packets directed toward
|
||||
|
||||
Implementation is optional. Must be called from the USB task. Interrupts could be disabled or enabled during the call.
|
||||
|
||||
dcd_edpt_xfer
|
||||
"""""""""""""
|
||||
``dcd_edpt_xfer()``
|
||||
"""""""""""""""""""
|
||||
|
||||
``dcd_edpt_xfer`` is responsible for configuring the peripheral to send or receive data from the host. "xfer" is short for "transfer". **This is one of the core methods you must implement for TinyUSB to work (one other is the interrupt handler).** Data from the host is the OUT direction and data to the host is IN. It is used for all endpoints including the control endpoint 0. Make sure to handle the zero-length packet STATUS packet on endpoint 0 correctly. It may be a special transaction to the peripheral.
|
||||
``dcd_edpt_xfer()`` is responsible for configuring the peripheral to send or receive data from the host. "xfer" is short for "transfer". **This is one of the core methods you must implement for TinyUSB to work (one other is the interrupt handler).** Data from the host is the OUT direction and data to the host is IN. It is used for all endpoints including the control endpoint 0. Make sure to handle the zero-length packet STATUS packet on endpoint 0 correctly. It may be a special transaction to the peripheral.
|
||||
|
||||
Besides that, all other transactions are relatively straight-forward. The endpoint address provides the endpoint
|
||||
number and direction which usually determines where to write the buffer info. The buffer and its length are usually
|
||||
@ -195,21 +197,21 @@ Others (like the nRF52) may need each USB packet queued individually. To make th
|
||||
some state for yourself and queue up an intermediate USB packet from the interrupt handler.
|
||||
|
||||
Once the transaction is going, the interrupt handler will notify TinyUSB of transfer completion.
|
||||
During transmission, the IN data buffer is guaranteed to remain unchanged in memory until the ``dcd_xfer_complete`` function is called.
|
||||
During transmission, the IN data buffer is guaranteed to remain unchanged in memory until the ``dcd_xfer_complete()`` function is called.
|
||||
|
||||
The dcd_edpt_xfer function must never add zero-length-packets (ZLP) on its own to a transfer. If a ZLP is required,
|
||||
then it must be explicitly sent by the stack calling dcd_edpt_xfer(), by calling dcd_edpt_xfer() a second time with len=0.
|
||||
The ``dcd_edpt_xfer()`` function must never add zero-length-packets (ZLP) on its own to a transfer. If a ZLP is required,
|
||||
then it must be explicitly sent by the stack calling ``dcd_edpt_xfer()``, by calling ``dcd_edpt_xfer()`` a second time with len=0.
|
||||
For control transfers, this is automatically done in ``usbd_control.c``.
|
||||
|
||||
At the moment, only a single buffer can be transmitted at once. There is no provision for double-buffering. new dcd_edpt_xfer() will not
|
||||
be called again on the same endpoint address until the driver calls dcd_xfer_complete() (except in cases of USB resets).
|
||||
At the moment, only a single buffer can be transmitted at once. There is no provision for double-buffering. new ``dcd_edpt_xfer()`` will not
|
||||
be called again on the same endpoint address until the driver calls ``dcd_xfer_complete()`` (except in cases of USB resets).
|
||||
|
||||
dcd_xfer_complete
|
||||
"""""""""""""""""
|
||||
``dcd_xfer_complete()``
|
||||
"""""""""""""""""""""""
|
||||
|
||||
Once a transfer completes you must call dcd_xfer_complete from the USB interrupt handler to let TinyUSB know that a transaction has completed. Here is a sample call:
|
||||
Once a transfer completes you must call ``dcd_xfer_complete()`` from the USB interrupt handler to let TinyUSB know that a transaction has completed. Here is a sample call:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: c
|
||||
|
||||
dcd_event_xfer_complete(0, ep_addr, xfer->actual_len, XFER_RESULT_SUCCESS, true);
|
||||
|
||||
@ -219,23 +221,23 @@ The arguments are:
|
||||
|
||||
* the USB peripheral number
|
||||
* the endpoint address
|
||||
* the actual length of the transfer. (OUT transfers may be smaller than the buffer given in ``dcd_edpt_xfer``\ )
|
||||
* the actual length of the transfer. (OUT transfers may be smaller than the buffer given in ``dcd_edpt_xfer()``)
|
||||
* the result of the transfer. Failure isn't handled yet.
|
||||
* ``true`` to note the call is from an interrupt handler.
|
||||
|
||||
dcd_edpt_stall / dcd_edpt_clear_stall
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
``dcd_edpt_stall()`` / ``dcd_edpt_clear_stall()``
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Stalling is one way an endpoint can indicate failure such as when an unsupported command is transmitted. The pair of ``dcd_edpt_stall``\ , ``dcd_edpt_clear_stall`` help manage the stall state of all endpoints.
|
||||
Stalling is one way an endpoint can indicate failure such as when an unsupported command is transmitted. The pair of ``dcd_edpt_stall()``, ``dcd_edpt_clear_stall()`` help manage the stall state of all endpoints.
|
||||
|
||||
Woohoo!
|
||||
-------
|
||||
|
||||
At this point you should have everything working! ;-) Of course, you may not write perfect code. Here are some tips and tricks for debugging.
|
||||
At this point you should have everything working! 🙂 Of course, you may not write perfect code. Here are some tips and tricks for debugging.
|
||||
|
||||
Use `WireShark <https://www.wireshark.org/>`_ or `a Beagle <https://www.totalphase.com/protocols/usb/>`_ to sniff the USB traffic. When things aren't working its likely very early in the USB enumeration process. Figuring out where can help clue in where the issue is. For example:
|
||||
|
||||
|
||||
* If the host sends a SETUP packet and its not ACKed then your USB peripheral probably isn't started correctly.
|
||||
* If the peripheral is started correctly but it still didn't work, then verify your usb clock is correct. (You did output a PWM based on it right? ;-) )
|
||||
* If the SETUP packet is ACKed but nothing is sent back then you interrupt handler isn't queueing the setup packet correctly. (Also, if you are using your own code instead of an example ``tud_task`` may not be called.) If that's OK, the ``dcd_xfer_complete`` may not be setting up the next transaction correctly.
|
||||
* If the peripheral is started correctly but it still didn't work, then verify your usb clock is correct. (You did output a PWM based on it right? 🙂)
|
||||
* If the SETUP packet is ACKed but nothing is sent back then you interrupt handler isn't queueing the setup packet correctly. (Also, if you are using your own code instead of an example ``tud_task()`` may not be called.) If that's OK, the ``dcd_xfer_complete()`` may not be setting up the next transaction correctly.
|
||||
|
||||
@ -15,12 +15,12 @@ General
|
||||
|
||||
- Better support dcache, make sure all usb-transferred buffer are cache line aligned and occupy full cache line
|
||||
- Build ARM IAR with CircleCI
|
||||
- Improve HIL with dual/host_info_to_device_cdc optional for pico/pico2, enable dwc2 dma test
|
||||
- Improve HIL with `dual/host_info_to_device_cdc`` optional for pico/pico2, enable dwc2 dma test
|
||||
|
||||
API Changes
|
||||
-----------
|
||||
|
||||
- Change signature of ``tusb_init(rhport, tusb_rhport_init_t*)``, tusb_init(void) is now deprecated but still available for backward compatibility
|
||||
- Change signature of ``tusb_init(rhport, tusb_rhport_init_t*)``, ``tusb_init(void)`` is now deprecated but still available for backward compatibility
|
||||
- Add new ``tusb_int_handler(rhport, in_isr)``
|
||||
- Add time-related APIs: ``tusb_time_millis_api()`` and ``tusb_time_delay_ms_api()`` for non-RTOS, required for some ports/configuration
|
||||
- New configuration macros:
|
||||
@ -37,17 +37,17 @@ Controller Driver (DCD & HCD)
|
||||
- Add DMA support for both device and host controller
|
||||
- Add host driver support including: full/high speed, control/bulk/interrupt (CBI) transfer, split CBI i.e FS/LS attached via highspeed hub, hub support
|
||||
|
||||
- RP2: implement dcd_edpt_iso_alloc() and dcd_edpt_iso_activate() for isochronous endpoint
|
||||
- RP2: implement ``dcd_edpt_iso_alloc()`` and ``dcd_edpt_iso_activate()`` for isochronous endpoint
|
||||
- iMXRT1170 support M4 core
|
||||
|
||||
Device Stack
|
||||
------------
|
||||
|
||||
- Vendor Fix class reset
|
||||
- NCM fix recursions in tud_network_recv_renew()
|
||||
- Audio fix align issue of _audiod_fct.alt_setting
|
||||
- NCM fix recursions in ``tud_network_recv_renew()``
|
||||
- Audio fix align issue of ``_audiod_fct.alt_setting``
|
||||
- UVC support format frame based
|
||||
- Change dcd_dcache_() return type from void to bool
|
||||
- Change ``dcd_dcache_()`` return type from void to bool
|
||||
- HID add Usage Table for Physical Input Device Page (0x0F)
|
||||
|
||||
Host Stack
|
||||
@ -89,20 +89,20 @@ Controller Driver (DCD & HCD)
|
||||
- Add support for ch32 usbd e.g ch32v203
|
||||
- Add support for STM32G4 and STM32U5 microcontrollers.
|
||||
- Fix h5 (32-bit) errata 2.15.1: Buffer description table update completes after CTR interrupt triggers
|
||||
- ISO EP buffer allocation improvements, implement dcd_edpt_close_all()
|
||||
- ISO EP buffer allocation improvements, implement ``dcd_edpt_close_all()``
|
||||
|
||||
- Fix ch32v203 race condition and stability issue with
|
||||
|
||||
- fix ch32v203 seems to unconditionally accept ZLP on EP0 OUT.
|
||||
- fix v203 race condition between rx bufsize and RX_STAT which cause PMAOVR, occurs with WRITE10
|
||||
- correctly handle setup prepare at dcd_edpt0_status_complete(), which fixes the race condition with windows where we could miss setup packet (setup bit set, but count = 0)
|
||||
- correctly handle setup prepare at ``dcd_edpt0_status_complete()``, which fixes the race condition with windows where we could miss setup packet (setup bit set, but count = 0)
|
||||
|
||||
- MAX3421E
|
||||
|
||||
- Add support for rp2040, esp32 (c3, c6, h2, etc..)
|
||||
- Add hcd_deinit() for max3421
|
||||
- Add ``hcd_deinit()`` for max3421
|
||||
- Retry NAK handling next frame to reduce CPU and SPI bus usage
|
||||
- add cpuctl and pinctl to tuh_configure() option for max3421
|
||||
- add ``cpuctl`` and ``pinctl`` to ``tuh_configure()`` option for max3421
|
||||
- Implement hcd abort transfer for Max3421
|
||||
- Properly Handle NAK Response in MAX3421E driver: correctly switch and skip writing to 2 FIFOs when NAK received. Otherwise, the driver may hang in certain conditions.
|
||||
|
||||
@ -114,7 +114,7 @@ Controller Driver (DCD & HCD)
|
||||
|
||||
- nRF
|
||||
|
||||
- Fix dcd_edpt_open for iso endpoint
|
||||
- Fix ``dcd_edpt_open()`` for iso endpoint
|
||||
- Handle ISOOUT CRC errors
|
||||
- Add compile support with old nordic sdk
|
||||
- Fix a few race conditions
|
||||
@ -141,7 +141,7 @@ Controller Driver (DCD & HCD)
|
||||
Device Stack
|
||||
------------
|
||||
|
||||
- Add tud_deinit() and class driver deinit() to deinitialize TinyUSB device stack.
|
||||
- Add ``tud_deinit()`` and ``class driver deinit()`` to deinitialize TinyUSB device stack.
|
||||
- Add support for generic SOF callback.
|
||||
- Add set address recovery time 2ms per USB spec.
|
||||
|
||||
@ -157,7 +157,7 @@ Device Stack
|
||||
|
||||
- CDC
|
||||
|
||||
- Add tud_cdc_configure_fifo() to make RX/TX buffer persistent (not clear when disconnected)
|
||||
- Add ``tud_cdc_configure_fifo()`` to make RX/TX buffer persistent (not clear when disconnected)
|
||||
- Add missing capability bit for CDC ACM serial break support
|
||||
- Enhanced CDC class with better handling of large data transmissions.
|
||||
- Add missing capability bit for CDC ACM serial break support
|
||||
@ -175,39 +175,39 @@ Device Stack
|
||||
- Net
|
||||
|
||||
- Rewrite of NCM device driver to improve throughput
|
||||
- removed obsolete tud_network_link_state_cb()
|
||||
- removed obsolete ``tud_network_link_state_cb()``
|
||||
|
||||
- USBTMC Added notification support
|
||||
|
||||
- Vendor
|
||||
|
||||
- Migrate to new endpoint stream API, support non-buffered TX/RX
|
||||
- Add ZLP for write() when needed
|
||||
- Add ZLP for ``write()`` when needed
|
||||
|
||||
- Video
|
||||
|
||||
- Enhance UVC descriptors and example
|
||||
- Video Added support for USB Video Class (UVC) with MJPEG.
|
||||
- Fix multiple interfaces, add an example of 2ch video capture.
|
||||
- Fix race for tud_video_n_streaming check
|
||||
- Fix race for ``tud_video_n_streaming()`` check
|
||||
|
||||
Host Stack
|
||||
----------
|
||||
|
||||
- Added tuh_deinit() to de-initialize TinyUSB host stack.
|
||||
- Added ``tuh_deinit()`` to de-initialize TinyUSB host stack.
|
||||
- Added support for new USB mass storage class APIs.
|
||||
- Improved error handling and retry mechanisms for unstable devices.
|
||||
|
||||
- CDC Serial
|
||||
|
||||
- Add support for ch34x
|
||||
- Allow to overwrite CFG_TUH_CDC_FTDI/CP210X/CH32X_VID_PID_LIST
|
||||
- Allow to overwrite ``CFG_TUH_CDC_FTDI/CP210X/CH32X_VID_PID_LIST``
|
||||
- Enhanced stability of CDC-ACM devices during enumeration.
|
||||
|
||||
- HID
|
||||
|
||||
- Add tuh_hid_receive_abort()
|
||||
- Add tuh_hid_get_report()
|
||||
- Add ``tuh_hid_receive_abort()``
|
||||
- Add ``tuh_hid_get_report()``
|
||||
|
||||
- Hub
|
||||
|
||||
@ -224,14 +224,14 @@ Host Stack
|
||||
- Remove submodules and use python script to manage repo dependencies #1947
|
||||
- Add CMake support for most families and boards, move build file from tools/ to examples/build_system
|
||||
- Add ETM trace support with JTrace for nrf52840, nrf5340, mcb1857, stm32h743eval, ra6m5
|
||||
- [osal] Make it possible to override the osal_task_delay() in osal_none
|
||||
- [osal] Make it possible to override the ``osal_task_delay()`` in osal_none
|
||||
- Add CDC+UAC2 composite device example
|
||||
- Enhance Hardware-in-the-loop (HIL) testing with more boards: rp2040, stm32l412nucleo, stm32f746disco, lpcxpresso43s67
|
||||
|
||||
Controller Driver (DCD & HCD)
|
||||
-----------------------------
|
||||
|
||||
- Add new ISO endpoint API: dcd_edpt_iso_alloc() and dcd_edpt_iso_activate()
|
||||
- Add new ISO endpoint API: ``dcd_edpt_iso_alloc()`` and ``dcd_edpt_iso_activate()``
|
||||
- Remove legacy driver st/synopsys
|
||||
|
||||
- EHCI
|
||||
@ -244,10 +244,10 @@ Controller Driver (DCD & HCD)
|
||||
- Fix error on EHCI causes xfer error in non-queued qhd which cause memory fault
|
||||
- Un-roll recursive hub removal with usbh queue
|
||||
- Fix issue when removing queue head
|
||||
- Implement hcd_edpt_abort_xfer()
|
||||
- Implement ``hcd_edpt_abort_xfer()``
|
||||
- use standard USB complete interrupt instead of custom chipidea async/period interrupt to be more compatible with other ehci implementation
|
||||
- refactor usb complete & error isr processing, merge, update. Fix EHCI QHD reuses QTD on wrong endpoint
|
||||
- Improve bus reset, fix send_setup() not carried out if halted previously
|
||||
- Improve bus reset, fix ``send_setup()`` not carried out if halted previously
|
||||
- Fix clear qhd halted bit if not caused by STALL protocol to allow for next transfer
|
||||
|
||||
- ChipIdea Highspeed
|
||||
@ -273,12 +273,12 @@ Controller Driver (DCD & HCD)
|
||||
- rp2040
|
||||
|
||||
- [dcd] Make writes to SIE_CTRL aware of concurrent access
|
||||
- [hcd] add hcd_frame_number(), hcd_edpt_abort_xfer() for pio-usb host
|
||||
- [hcd] add ``hcd_frame_number()``, ``hcd_edpt_abort_xfer()`` for pio-usb host
|
||||
|
||||
- stm32 fsdev:
|
||||
|
||||
- Add STM32L5 support
|
||||
- Implement dcd_edpt_iso_alloc() and dcd_edpt_iso_activate()
|
||||
- Implement ``dcd_edpt_iso_alloc()`` and ``dcd_edpt_iso_activate()``
|
||||
|
||||
- OHCI
|
||||
|
||||
@ -292,7 +292,7 @@ Controller Driver (DCD & HCD)
|
||||
Device Stack
|
||||
------------
|
||||
|
||||
- Add optional hooks tud_event_hook_cb()
|
||||
- Add optional hooks ``tud_event_hook_cb()``
|
||||
- Audio (UAC2)
|
||||
|
||||
- Fix feedback EP buffer alignment.
|
||||
@ -310,12 +310,12 @@ Device Stack
|
||||
|
||||
- MIDI
|
||||
|
||||
- Fix stream_write() always writes system messages to cable 0
|
||||
- Fix ``stream_write()`` always writes system messages to cable 0
|
||||
- Fix incorrect NOTE_ON, NOTE_OFF definitions
|
||||
|
||||
- USBTMC: Fix tmc488 bit order
|
||||
|
||||
- Vendor: fix read()/write() race condition
|
||||
- Vendor: fix ``read()``/``write()`` race condition
|
||||
|
||||
- Video (UVC)
|
||||
|
||||
@ -326,26 +326,26 @@ Host Stack
|
||||
|
||||
- USBH
|
||||
|
||||
- Add new APIs: tuh_interface_set(), tuh_task_event_ready(), tuh_edpt_abort_xfer(), tuh_rhport_reset_bus(), tuh_rhport_is_active()
|
||||
- Add new APIs: ``tuh_interface_set()``, ``tuh_task_event_ready()``, ``tuh_edpt_abort_xfer()``, ``tuh_rhport_reset_bus()``, ``tuh_rhport_is_active()``
|
||||
- Fix issue when device generate multiple attach/detach/attach when plugging in
|
||||
- Prefer application callback over built-in driver on transfer complete event
|
||||
- Correct hcd_edpt_clear_stall() API signature
|
||||
- Correct ``hcd_edpt_clear_stall()`` API signature
|
||||
- Separate bus reset delay and contact debouncing delay in enumeration
|
||||
- Support usbh_app_driver_get_cb() for application drivers
|
||||
- Support ``usbh_app_driver_get_cb()`` for application drivers
|
||||
- Fix usbh enumeration removal race condition
|
||||
- Add optional hooks tuh_event_hook_cb()
|
||||
- Add optional hooks ``tuh_event_hook_cb()``
|
||||
|
||||
- CDC
|
||||
|
||||
- Breaking: change tuh_cdc_itf_get_info() to use tuh_itf_info_t instead of tuh_cdc_info_t
|
||||
- Breaking: change ``tuh_cdc_itf_get_info()`` to use tuh_itf_info_t instead of tuh_cdc_info_t
|
||||
- Fix cdc host enumeration issue when device does not support line request
|
||||
- Add support for vendor usb2uart serial: ftdi, cp210x, ch9102f
|
||||
- Improve sync control API e.g tuh_cdc_set_control_line_state(), tuh_cdc_set_line_coding()
|
||||
- Improve sync control API e.g ``tuh_cdc_set_control_line_state()``, ``tuh_cdc_set_line_coding()``
|
||||
|
||||
- HID
|
||||
|
||||
- Add new APIs tuh_hid_send_report(), tuh_hid_itf_get_info(), tuh_hid_receive_ready(), tuh_hid_send_ready(), tuh_hid_set_default_protocol()
|
||||
- Change meaning of CFG_TUH_HID to total number of HID interfaces supported. Previously CFG_TUH_HID is max number of interfaces per device which is rather limited and consume more resources than needed.
|
||||
- Add new APIs ``tuh_hid_send_report()``, ``tuh_hid_itf_get_info()``, ``tuh_hid_receive_ready()``, ``tuh_hid_send_ready()``, ``tuh_hid_set_default_protocol()``
|
||||
- Change meaning of CFG_TUH_HID to total number of HID interfaces supported. Previously ``CFG_TUH_HID`` is max number of interfaces per device which is rather limited and consume more resources than needed.
|
||||
|
||||
- HUB
|
||||
|
||||
@ -354,7 +354,7 @@ Host Stack
|
||||
|
||||
- MSC
|
||||
|
||||
- Fix bug in tuh_msc_ready()
|
||||
- Fix bug in ``tuh_msc_ready()``
|
||||
- Fix host msc get maxlun not using aligned section memory
|
||||
|
||||
0.15.0
|
||||
@ -385,7 +385,7 @@ Controller Driver (DCD & HCD)
|
||||
- [rp2040]
|
||||
|
||||
- [dcd] Implement workaround for Errata 15. This enable SOF when bulk-in endpoint is in use and reduce its bandwidth to only 80%
|
||||
- [hcd] Fix shared irq slots filling up when hcd_init() is called multiple times
|
||||
- [hcd] Fix shared irq slots filling up when ``hcd_init()`` is called multiple times
|
||||
- [hcd] Support host bulk endpoint using hw "interrupt" endpoint. Note speed limit is 64KB/s
|
||||
|
||||
- [samd][dcd] Add support for ISO endpoint
|
||||
@ -410,12 +410,12 @@ Device Stack
|
||||
- [HID]
|
||||
|
||||
- Add FIDO descriptor template
|
||||
- change length in tud_hid_report_complete_cb() from uint8 to uint16
|
||||
- change length in ``tud_hid_report_complete_cb()`` from ``uint8_t`` to ``uint16_t``
|
||||
|
||||
- [CDC]
|
||||
|
||||
- Fix autoflush for FIFO < MPS
|
||||
- Fix tx fifo memory overflown when DTR is not set and tud_cdc_write() is called repeatedly with large enough data
|
||||
- Fix tx fifo memory overflown when DTR is not set and ``tud_cdc_write()`` is called repeatedly with large enough data
|
||||
|
||||
- [USBTMC] Fix packet size with highspeed
|
||||
|
||||
@ -423,7 +423,7 @@ Host Stack
|
||||
----------
|
||||
|
||||
- Retry a few times with transfers in enumeration since device can be unstable when starting up
|
||||
- [MSC] Rework host masstorage API. Add new **host/msc_file_explorer** example
|
||||
- [MSC] Rework host masstorage API. Add new ``host/msc_file_explorer`` example
|
||||
- [CDC]
|
||||
|
||||
- Add support for host cdc
|
||||
@ -433,22 +433,22 @@ Host Stack
|
||||
======
|
||||
|
||||
- Improve compiler support for CCRX and IAR
|
||||
- Add timeout to osal_queue_receive()
|
||||
- Add tud_task_ext(timeout, in_isr) as generic version of tud_task(). Same as tuh_task_ext(), tuh_task()
|
||||
- Enable more warnings -Wnull-dereference -Wuninitialized -Wunused -Wredundant-decls -Wconversion
|
||||
- Add timeout to ``osal_queue_receive()``
|
||||
- Add ``tud_task_ext(timeout, in_isr)`` as generic version of ``tud_task()``. Same as ``tuh_task_ext()``, ``tuh_task()``
|
||||
- Enable more warnings ``-Wnull-dereference -Wuninitialized -Wunused -Wredundant-decls -Wconversion``
|
||||
- Add new examples
|
||||
|
||||
- host/bare_api to demonstrate generic (app-level) enumeration and endpoint transfer
|
||||
- dual/host_hid_to_device_cdc to run both device and host stack concurrently, get HID report from host and print out to device CDC. This example only work with multiple-controller MCUs and rp2040 with the help of pio-usb as added controller.
|
||||
- ``host/bare_api`` to demonstrate generic (app-level) enumeration and endpoint transfer
|
||||
- ``dual/host_hid_to_device_cdc`` to run both device and host stack concurrently, get HID report from host and print out to device CDC. This example only work with multiple-controller MCUs and rp2040 with the help of pio-usb as added controller.
|
||||
|
||||
Controller Driver (DCD & HCD)
|
||||
-----------------------------
|
||||
|
||||
- Enhance rhports management to better support dual roles
|
||||
|
||||
- CFG_TUD_ENABLED/CFG_TUH_ENABLED, CFG_TUD_MAX_SPEED/CFG_TUH_MAX_SPEED can be used to replace CFG_TUSB_RHPORT0_MODE/CFG_TUSB_RHPORT1_MODE
|
||||
- tud_init(rphort), tuh_init(rhport) can be used to init stack on specified roothub port (controller) instead of tusb_init(void)
|
||||
- Add dcd/hcd port specific defines `TUP_` (stand for tinyusb port-specific)
|
||||
- ``CFG_TUD_ENABLED``/``CFG_TUH_ENABLED``, ``CFG_TUD_MAX_SPEED``/``CFG_TUH_MAX_SPEED`` can be used to replace ``CFG_TUSB_RHPORT0_MODE``/``CFG_TUSB_RHPORT1_MODE``
|
||||
- ``tud_init(rphort)``, ``tuh_init(rhport)`` can be used to init stack on specified roothub port (controller) instead of ``tusb_init(void)``
|
||||
- Add dcd/hcd port specific defines ``TUP_`` (stand for tinyusb port-specific)
|
||||
- [dwc2]
|
||||
|
||||
- Update to support stm32 h72x, h73x with only 1 otg controller
|
||||
@ -469,10 +469,10 @@ Device Stack
|
||||
|
||||
- [Audio] Add support for feedback endpoint computation
|
||||
|
||||
- New API tud_audio_feedback_params_cb(), tud_audio_feedback_interval_isr().
|
||||
- New API ``tud_audio_feedback_params_cb()``, ``tud_audio_feedback_interval_isr()``.
|
||||
- Supported computation method are: frequency with fixed/float or power of 2. Feedback with fifo count is not yet supported.
|
||||
- Fix nitfs (should be 3) in TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR
|
||||
- Fix typo in audiod_rx_done_cb()
|
||||
- Fix nitfs (should be 3) in ``TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR``
|
||||
- Fix typo in ``audiod_rx_done_cb()``
|
||||
|
||||
- [DFU] Fix coexistence with other interfaces BTH, RNDIS
|
||||
- [MSC] Fix inquiry response additional length field
|
||||
@ -481,23 +481,23 @@ Device Stack
|
||||
Host Stack
|
||||
----------
|
||||
|
||||
- Add new API tuh_configure(rhport, cfg_id, cfg_param) for dynamnic port specific behavior configuration
|
||||
- Add new API ``tuh_configure(rhport, cfg_id, cfg_param)`` for dynamnic port specific behavior configuration
|
||||
- [HID] Open OUT endpoint if available
|
||||
- [Hub] hub clear port and device interrupts
|
||||
- [USBH] Major improvement
|
||||
|
||||
- Rework usbh control transfer with complete callback. New API tuh_control_xfer() though still only carry 1 usbh (no queueing) at a time.
|
||||
- Add generic endpoint transfer with tuh_edpt_open(), tuh_edpt_xfer(). Require `CFG_TUH_API_EDPT_XFER=1`
|
||||
- Rework usbh control transfer with complete callback. New API ``tuh_control_xfer()`` though still only carry 1 usbh (no queueing) at a time.
|
||||
- Add generic endpoint transfer with ``tuh_edpt_open()``, ``tuh_edpt_xfer()``. Require ``CFG_TUH_API_EDPT_XFER=1``
|
||||
- Support app-level enumeration with new APIs
|
||||
|
||||
- tuh_descriptor_get(), tuh_descriptor_get_device(), tuh_descriptor_get_configuration(), tuh_descriptor_get_hid_report()
|
||||
- tuh_descriptor_get_string(), tuh_descriptor_get_manufacturer_string(), tuh_descriptor_get_product_string(), tuh_descriptor_get_serial_string()
|
||||
- Also add _sync() as sync/blocking version for above APIs
|
||||
- ``tuh_descriptor_get()``, ``tuh_descriptor_get_device()``, ``tuh_descriptor_get_configuration()``, ``tuh_descriptor_get_hid_report()``
|
||||
- ``tuh_descriptor_get_string()``, ``tuh_descriptor_get_manufacturer_string()``, ``tuh_descriptor_get_product_string()``, ``tuh_descriptor_get_serial_string()``
|
||||
- Also add ``_sync()`` as sync/blocking version for above APIs
|
||||
|
||||
0.13.0
|
||||
======
|
||||
|
||||
- [tu_fifo] Fix locked mutex when full, and return type in peek_n()
|
||||
- [tu_fifo] Fix locked mutex when full, and return type in ``peek_n()``
|
||||
|
||||
Controller Driver (DCD & HCD)
|
||||
-----------------------------
|
||||
@ -526,7 +526,7 @@ Device Stack
|
||||
------------
|
||||
|
||||
- [Audio] Support disabling feedback format correction (16.16 <-> 10.14 format)
|
||||
- [MSC] Add tud_msc_request_sense_cb() callback, change most default sense error to medium not present (0x02, 0x3A, 0x00)
|
||||
- [MSC] Add ``tud_msc_request_sense_cb()`` callback, change most default sense error to medium not present (0x02, 0x3A, 0x00)
|
||||
- [Video] Fix video_capture example fails enumeration when 8FPS
|
||||
|
||||
Host Stack
|
||||
@ -537,22 +537,22 @@ No notable changes
|
||||
0.12.0
|
||||
======
|
||||
|
||||
- add CFG_TUSB_OS_INC_PATH for os include path
|
||||
- add ``CFG_TUSB_OS_INC_PATH`` for os include path
|
||||
|
||||
Device Controller Driver (DCD)
|
||||
------------------------------
|
||||
|
||||
- Getting device stack to pass USB Compliance Verification test (chapter9, HID, MSC). Ports are tested:
|
||||
nRF, SAMD 21/51, rp2040, stm32f4, Renesas RX, iMXRT, ESP32-S2/3, Kinetic KL25/32, DA146xx
|
||||
- Added dcd_edpt_close_all() for switching configuration
|
||||
- [Transdimension] Support dcd_edpt_xfer_fifo() with auto wrap over if fifo buffer is 4K aligned and size is multiple of 4K.
|
||||
- Added ``dcd_edpt_close_all()`` for switching configuration
|
||||
- [Transdimension] Support ``dcd_edpt_xfer_fifo()`` with auto wrap over if fifo buffer is 4K aligned and size is multiple of 4K.
|
||||
- [DA146xx] Improve vbus, reset, suspend, resume detection, and remote wakeup.
|
||||
|
||||
Device Stack
|
||||
------------
|
||||
|
||||
- Add new network driver Network Control Model (CDC-NCM), update net_lwip_webserver to work with NCM (need re-configure example)
|
||||
- Add new USB Video Class UVC 1.5 driver and video_capture example ((work in progress)
|
||||
- Add new network driver Network Control Model (CDC-NCM), update ``net_lwip_webserver`` to work with NCM (need re-configure example)
|
||||
- Add new USB Video Class UVC 1.5 driver and video_capture example (work in progress)
|
||||
- Fix potential buffer overflow for HID, bluetooth drivers
|
||||
|
||||
Host Controller Driver (HCD)
|
||||
@ -589,13 +589,13 @@ Synopsys
|
||||
^^^^^^^^
|
||||
|
||||
- Fix Synopsys set address bug which could cause re-enumeration failed
|
||||
- Fix dcd_synopsys driver integer overflow in HS mode (issue #968)
|
||||
- Fix ``dcd_synopsys`` driver integer overflow in HS mode (issue #968)
|
||||
|
||||
nRF5x
|
||||
^^^^^
|
||||
|
||||
- Add nRF5x suspend, resume and remote wakeup
|
||||
- Fix nRF5x race condition with TASKS_EP0RCVOUT
|
||||
- Fix nRF5x race condition with ``TASKS_EP0RCVOUT``
|
||||
|
||||
RP2040
|
||||
^^^^^^
|
||||
@ -610,8 +610,8 @@ USBD
|
||||
^^^^
|
||||
|
||||
- Better support big endian mcu
|
||||
- Add tuh_inited() and tud_inited(), will separate tusb_init/inited() to tud/tuh init/inited
|
||||
- Add dcd_attr.h for defining common controller attribute such as max endpoints
|
||||
- Add ``tuh_inited()`` and ``tud_inited()``, will separate ``tusb_init/inited()`` to ``tud/tuh_init/inited()``
|
||||
- Add ``dcd_attr.h`` for defining common controller attribute such as max endpoints
|
||||
|
||||
Bluetooth
|
||||
^^^^^^^^^
|
||||
@ -621,8 +621,8 @@ Bluetooth
|
||||
DFU
|
||||
^^^
|
||||
|
||||
- Enhance DFU implementation to support multiple alternate interface and better support bwPollTimeout
|
||||
- Rename CFG_TUD_DFU_MODE to simply CFG_TUD_DFU
|
||||
- Enhance DFU implementation to support multiple alternate interface and better support ``bwPollTimeout``
|
||||
- Rename ``CFG_TUD_DFU_MODE`` to simply ``CFG_TUD_DFU``
|
||||
|
||||
HID
|
||||
^^^
|
||||
@ -647,7 +647,7 @@ Vendor
|
||||
^^^^^^
|
||||
|
||||
- Fix vendor fifo deadlock in certain case
|
||||
- Add tud_vendor_n_read_flush
|
||||
- Add ``tud_vendor_n_read_flush()``
|
||||
|
||||
Host Controller Driver (HCD)
|
||||
----------------------------
|
||||
@ -664,7 +664,7 @@ Host Stack
|
||||
- Major update and rework most of host stack, still needs more improvement
|
||||
- Lots of improvement and update in parsing configuration and control
|
||||
- Rework and major update to HID driver. Will default to enable boot interface if available
|
||||
- Separate CFG_TUH_DEVICE_MAX and CFG_TUH_HUB for better management and reduce SRAM usage
|
||||
- Separate ``CFG_TUH_DEVICE_MAX`` and ``CFG_TUH_HUB`` for better management and reduce SRAM usage
|
||||
|
||||
0.10.1 (2021-06-03)
|
||||
===================
|
||||
@ -676,9 +676,9 @@ Host Controller Driver (HCD)
|
||||
|
||||
- Fix rp2040 host driver: incorrect PID with low speed device with max packet size of 8 bytes
|
||||
- Improve hub driver
|
||||
- Remove obsolete hcd_pipe_queue_xfer()/hcd_pipe_xfer()
|
||||
- Use hcd_frame_number() instead of micro frame
|
||||
- Fix OHCI endpoint address and xferred_bytes in xfer complete event
|
||||
- Remove obsolete ``hcd_pipe_queue_xfer()``/``hcd_pipe_xfer()``
|
||||
- Use ``hcd_frame_number()`` instead of micro frame
|
||||
- Fix OHCI endpoint address and ``xferred_bytes`` in xfer complete event
|
||||
|
||||
0.10.0 (2021-05-28)
|
||||
===================
|
||||
@ -697,7 +697,7 @@ Device Controller Driver (DCD)
|
||||
- Fix build with nRF5340
|
||||
- Fix build with lpc15 and lpc54
|
||||
- Fix build with lpc177x_8x
|
||||
- STM32 Synopsys: greatly improve Isochronous transfer with edpt_xfer_fifo API
|
||||
- STM32 Synopsys: greatly improve Isochronous transfer with ``edpt_xfer_fifo()`` API
|
||||
- Support LPC55 port1 highspeed
|
||||
- Add support for Espressif esp32s3
|
||||
- nRF: fix race condition that could cause drop packet of Bulk OUT transfer
|
||||
@ -705,14 +705,14 @@ Device Controller Driver (DCD)
|
||||
USB Device Driver (USBD)
|
||||
------------------------
|
||||
|
||||
- Add new (optional) endpoint ADPI usbd_edpt_xfer_fifo
|
||||
- Add new (optional) endpoint ADPI ``usbd_edpt_xfer_fifo()``
|
||||
|
||||
Device Class Driver
|
||||
-------------------
|
||||
|
||||
CDC
|
||||
|
||||
- [Breaking] tud_cdc_peek(), tud_vendor_peek() no longer support random offset and dropped position parameter.
|
||||
- [Breaking] ``tud_cdc_peek()``, ``tud_vendor_peek()`` no longer support random offset and dropped position parameter.
|
||||
|
||||
DFU
|
||||
|
||||
@ -724,19 +724,19 @@ HID
|
||||
- Add more hid keys constant from 0x6B to 0xA4
|
||||
|
||||
- [Breaking] rename API
|
||||
- HID_PROTOCOL_NONE/KEYBOARD/MOUST to HID_ITF_PROTOCOL_NONE/KEYBOARD/MOUSE
|
||||
- tud_hid_boot_mode() to tud_hid_get_protocol()
|
||||
- tud_hid_boot_mode_cb() to tud_hid_set_protocol_cb()
|
||||
- ``HID_PROTOCOL_NONE/KEYBOARD/MOUSE`` to ``HID_ITF_PROTOCOL_NONE/KEYBOARD/MOUSE``
|
||||
- ``tud_hid_boot_mode()`` to ``tud_hid_get_protocol()``
|
||||
- ``tud_hid_boot_mode_cb()`` to ``tud_hid_set_protocol_cb()``
|
||||
|
||||
MIDI
|
||||
|
||||
- Fix MIDI buffer overflow issue
|
||||
|
||||
- [Breaking] rename API
|
||||
- Rename tud_midi_read() to tud_midi_stream_read()
|
||||
- Rename tud_midi_write() to tud_midi_stream_write()
|
||||
- Rename tud_midi_receive() to tud_midi_packet_read()
|
||||
- Rename tud_midi_send() to tud_midi_packet_write()
|
||||
- Rename ``tud_midi_read()`` to ``tud_midi_stream_read()``
|
||||
- Rename ``tud_midi_write()`` to ``tud_midi_stream_write()``
|
||||
- Rename ``tud_midi_receive()`` to ``tud_midi_packet_read()``
|
||||
- Rename ``tud_midi_send()`` to ``tud_midi_packet_write()``
|
||||
|
||||
Host Controller Driver (HCD)
|
||||
----------------------------
|
||||
@ -783,7 +783,7 @@ NXP Transdimention
|
||||
USB Device Driver (USBD)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Fix issue with status zlp (tud_control_status) is returned by class driver with SET/CLEAR_FEATURE for endpoint.
|
||||
- Fix issue with status zlp (``tud_control_status()``) is returned by class driver with SET/CLEAR_FEATURE for endpoint.
|
||||
- Correct endpoint size check for fullspeed bulk, can be 8, 16, 32, 64
|
||||
- Ack SET_INTERFACE even if it is not implemented by class driver.
|
||||
|
||||
@ -792,34 +792,34 @@ Device Class Driver
|
||||
|
||||
DFU Runtime
|
||||
|
||||
- rename dfu_rt to dfu_runtime for easy reading
|
||||
- rename ``dfu_rt()`` to ``dfu_runtime()`` for easy reading
|
||||
|
||||
CDC
|
||||
|
||||
- Add tud_cdc_send_break_cb() to support break request
|
||||
- Improve CDC receive, minor behavior changes: when tud_cdc_rx_wanted_cb() is invoked wanted_char may not be the last byte in the fifo
|
||||
- Add ``tud_cdc_send_break_cb()`` to support break request
|
||||
- Improve CDC receive, minor behavior changes: when ``tud_cdc_rx_wanted_cb()`` is invoked wanted_char may not be the last byte in the fifo
|
||||
|
||||
HID
|
||||
|
||||
- [Breaking] Add itf argument to hid API to support multiple instances, follow API has signature changes
|
||||
|
||||
- tud_hid_descriptor_report_cb()
|
||||
- tud_hid_get_report_cb()
|
||||
- tud_hid_set_report_cb()
|
||||
- tud_hid_boot_mode_cb()
|
||||
- tud_hid_set_idle_cb()
|
||||
- ``tud_hid_descriptor_report_cb()``
|
||||
- ``tud_hid_get_report_cb()``
|
||||
- ``tud_hid_set_report_cb()``
|
||||
- ``tud_hid_boot_mode_cb()``
|
||||
- ``tud_hid_set_idle_cb()``
|
||||
|
||||
- Add report complete callback tud_hid_report_complete_cb() API
|
||||
- Add report complete callback ``tud_hid_report_complete_cb()`` API
|
||||
- Add DPad/Hat support for HID Gamepad
|
||||
|
||||
- `TUD_HID_REPORT_DESC_GAMEPAD()` now support 16 buttons, 2 joysticks, 1 hat/dpad
|
||||
- Add hid_gamepad_report_t along with `GAMEPAD_BUTTON_` and `GAMEPAD_HAT_` enum
|
||||
- Add Gamepad to hid_composite / hid_composite_freertos example
|
||||
- ``TUD_HID_REPORT_DESC_GAMEPAD()`` now support 16 buttons, 2 joysticks, 1 hat/dpad
|
||||
- Add ``hid_gamepad_report_t`` along with ``GAMEPAD_BUTTON_`` and ``GAMEPAD_HAT_`` enum
|
||||
- Add Gamepad to ``hid_composite`` / ``hid_composite_freertos`` example
|
||||
|
||||
MIDI
|
||||
|
||||
- Fix dropping MIDI sysex message when fifo is full
|
||||
- Fix typo in tud_midi_write24(), make example less ambiguous for cable and channel
|
||||
- Fix typo in ``tud_midi_write24()``, make example less ambiguous for cable and channel
|
||||
- Fix incorrect endpoint descriptor length, MIDI v1 use Audio v1 which has 9-byte endpoint descriptor (instead of 7)
|
||||
|
||||
Host Stack
|
||||
@ -828,14 +828,14 @@ Host Stack
|
||||
Host Controller Driver (HCD)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Add rhport to hcd_init()
|
||||
- Add rhport to ``hcd_init()``
|
||||
- Improve EHCI/OHCI driver abstraction
|
||||
|
||||
- Move echi/ohci files to portable/
|
||||
- Rename hcd_lpc18_43 to hcd_transdimension
|
||||
- Sub hcd API with hcd_ehci_init(), hcd_ehci_register_addr()
|
||||
- Rename ``hcd_lpc18_43`` to ``hcd_transdimension``
|
||||
- Sub hcd API with ``hcd_ehci_init()``, ``hcd_ehci_register_addr()``
|
||||
|
||||
- Update NXP transdimention hcd_init() to reset controller to host mode
|
||||
- Update NXP transdimension ``hcd_init()`` to reset controller to host mode
|
||||
|
||||
- Ported hcd to rt10xx
|
||||
|
||||
@ -849,20 +849,20 @@ Host Class Driver
|
||||
|
||||
MSC
|
||||
|
||||
- Rename tuh_msc_scsi_inquiry() to tuh_msc_inquiry()
|
||||
- Rename tuh_msc_mounted_cb/tuh_msc_unmounted_cb to tuh_msc_mount_cb/tuh_msc_unmount_cb to match device stack naming
|
||||
- Change tuh_msc_is_busy() to tuh_msc_ready()
|
||||
- Add read10 and write10 function: tuh_msc_read10(), tuh_msc_write10()
|
||||
- Rename ``tuh_msc_scsi_inquiry()`` to ``tuh_msc_inquiry()``
|
||||
- Rename ``tuh_msc_mounted_cb()``/``tuh_msc_unmounted_cb()`` to ``tuh_msc_mount_cb()``/``tuh_msc_unmount_cb()`` to match device stack naming
|
||||
- Change ``tuh_msc_is_busy()`` to ``tuh_msc_ready()``
|
||||
- Add read10 and write10 function: ``tuh_msc_read10()``, ``tuh_msc_write10()``
|
||||
- Read_Capacity is invoked as part of enumeration process
|
||||
- Add tuh_msc_get_block_count(), tuh_msc_get_block_size()
|
||||
- Add CFG_TUH_MSC_MAXLUN (default to 4) to hold lun capacities
|
||||
- Add ``tuh_msc_get_block_count()``, ``tuh_msc_get_block_size()``
|
||||
- Add ``CFG_TUH_MSC_MAXLUN`` (default to 4) to hold lun capacities
|
||||
|
||||
Others
|
||||
------
|
||||
|
||||
- Add basic support for rt-thread OS
|
||||
- Change zero bitfield length to more explicit padding
|
||||
- Build example now fetch required submodules on the fly while running `make` without prio submodule init for mcu drivers
|
||||
- Build example now fetch required submodules on the fly while running ``make`` without prior submodule init for mcu drivers
|
||||
- Update pico-sdk to v1.1.0
|
||||
|
||||
**New Boards**
|
||||
@ -880,7 +880,7 @@ Device Controller Driver
|
||||
|
||||
- Added new device support for Raspberry Pi RP2040
|
||||
- Added new device support for NXP Kinetis KL25ZXX
|
||||
- Use dcd_event_bus_reset() with link speed to replace bus_signal
|
||||
- Use ``dcd_event_bus_reset()`` with link speed to replace bus_signal
|
||||
|
||||
- ESP32-S2:
|
||||
- Add bus suspend and wakeup support
|
||||
@ -902,8 +902,8 @@ USB Device
|
||||
**USBD**
|
||||
|
||||
- Rework usbd control transfer to have additional stage parameter for setup, data, status
|
||||
- Fix tusb_init() return true instead of TUSB_ERROR_NONE
|
||||
- Added new API tud_connected() that return true after device got out of bus reset and received the very first setup packet
|
||||
- Fix ``tusb_init()`` return true instead of ``TUSB_ERROR_NONE``
|
||||
- Added new API ``tud_connected()`` that return true after device got out of bus reset and received the very first setup packet
|
||||
|
||||
**Class Driver**
|
||||
|
||||
@ -911,22 +911,22 @@ USB Device
|
||||
- Allow to transmit data, even if the host does not support control line states i.e set DTR
|
||||
|
||||
- HID
|
||||
- change default CFG_TUD_HID_EP_BUFSIZE from 16 to 64
|
||||
- change default ``CFG_TUD_HID_EP_BUFSIZE`` from 16 to 64
|
||||
|
||||
- MIDI
|
||||
- Fix midi sysex sending bug
|
||||
|
||||
- MSC
|
||||
- Invoke only scsi complete callback after status transaction is complete.
|
||||
- Fix scsi_mode_sense6_t padding, which cause IAR compiler internal error.
|
||||
- Fix ``scsi_mode_sense6_t`` padding, which cause IAR compiler internal error.
|
||||
|
||||
- USBTMC
|
||||
- Change interrupt endpoint example size to 8 instead of 2 for better compatibility with mcu
|
||||
|
||||
**Example**
|
||||
|
||||
- Support make from windows cmd.exe
|
||||
- Add HID Consumer Control (media keys) to hid_composite & hid_composite_freertos examples
|
||||
- Support make from windows ``cmd.exe``
|
||||
- Add HID Consumer Control (media keys) to ``hid_composite`` & ``hid_composite_freertos`` examples
|
||||
|
||||
|
||||
USB Host
|
||||
@ -967,28 +967,28 @@ Device Controller Driver
|
||||
- Support multiple usb ports with rhport=1 is highspeed on selected MCUs e.g H743, F23. It is possible to have OTG_HS to run on Fullspeed PHY (e.g lacking external PHY)
|
||||
- Add ISO transfer, fix odd/even frame
|
||||
- Fix FIFO flush during stall
|
||||
- Implement dcd_edpt_close() API
|
||||
- Implement ``dcd_edpt_close()`` API
|
||||
- Support F105, F107
|
||||
|
||||
- Enhance STM32 fsdev
|
||||
- Improve dcd fifo allocation
|
||||
- Fix ISTR race condition
|
||||
- Support remap USB IRQ on supported MCUs
|
||||
- Implement dcd_edpt_close() API
|
||||
- Implement ``dcd_edpt_close()`` API
|
||||
|
||||
- Enhance NUC 505: enhance set configure behavior
|
||||
|
||||
- Enhance SAMD
|
||||
- Fix race condition with setup packet
|
||||
- Add SAMD11 option `OPT_MCU_SAMD11`
|
||||
- Add SAME5x option `OPT_MCU_SAME5X`
|
||||
- Add SAMD11 option ``OPT_MCU_SAMD11``
|
||||
- Add SAME5x option ``OPT_MCU_SAME5X``
|
||||
|
||||
- Fix SAMG control data toggle and stall race condition
|
||||
|
||||
- Enhance nRF
|
||||
- Fix hanged when tud_task() is called within critical section (disabled interrupt)
|
||||
- Fix hanged when ``tud_task()`` is called within critical section (disabled interrupt)
|
||||
- Fix disconnect bus event not submitted
|
||||
- Implement ISO transfer and dcd_edpt_close()
|
||||
- Implement ISO transfer and ``dcd_edpt_close()``
|
||||
|
||||
|
||||
USB Device
|
||||
@ -997,26 +997,26 @@ USB Device
|
||||
**USBD**
|
||||
|
||||
- Add new class driver for **Bluetooth HCI** class driver with example can be found in [mynewt-tinyusb-example](https://github.com/hathach/mynewt-tinyusb-example) since it needs mynewt OS to run with.
|
||||
- Fix USBD endpoint usage racing condition with `usbd_edpt_claim()/usbd_edpt_release()`
|
||||
- Added `tud_task_event_ready()` and `osal_queue_empty()`. This API is needed to check before enter low power mode with WFI/WFE
|
||||
- Rename USB IRQ Handler to `dcd_int_handler()`. Application must define IRQ handler in which it calls this API.
|
||||
- Add `dcd_connect()` and `dcd_disconnect()` to enable/disable internal pullup on D+/D- on supported MCUs.
|
||||
- Add `usbd_edpt_open()`
|
||||
- Remove `dcd_set_config()`
|
||||
- Add *OPT_OS_CUMSTOM* as hook for application to overwrite and/or add their own OS implementation
|
||||
- Fix USBD endpoint usage racing condition with ``usbd_edpt_claim()``/``usbd_edpt_release()``
|
||||
- Added ``tud_task_event_ready()`` and ``osal_queue_empty()``. This API is needed to check before enter low power mode with WFI/WFE
|
||||
- Rename USB IRQ Handler to ``dcd_int_handler()``. Application must define IRQ handler in which it calls this API.
|
||||
- Add ``dcd_connect()`` and ``dcd_disconnect()`` to enable/disable internal pullup on D+/D- on supported MCUs.
|
||||
- Add ``usbd_edpt_open()``
|
||||
- Remove ``dcd_set_config()``
|
||||
- Add ``OPT_OS_CUMSTOM`` as hook for application to overwrite and/or add their own OS implementation
|
||||
- Support SET_INTERFACE, GET_INTERFACE request
|
||||
- Add Logging for debug with optional uart/rtt/swo printf retarget or `CFG_TUSB_DEBUG_PRINTF` hook
|
||||
- Add Logging for debug with optional uart/rtt/swo printf retarget or ``CFG_TUSB_DEBUG_PRINTF`` hook
|
||||
- Add IAR compiler support
|
||||
- Support multiple configuration descriptors. `TUD_CONFIG_DESCRIPTOR()` template has extra config_num as 1st argument
|
||||
- Improve USB Highspeed support with actual link speed detection with `dcd_event_bus_reset()`
|
||||
- Support multiple configuration descriptors. ``TUD_CONFIG_DESCRIPTOR()`` template has extra config_num as 1st argument
|
||||
- Improve USB Highspeed support with actual link speed detection with ``dcd_event_bus_reset()``
|
||||
|
||||
- Enhance class driver management
|
||||
- `usbd_driver_open()` add max length argument, and return length of interface (0 for not supported). Return value is used for finding appropriate driver
|
||||
- Add application implemented class driver via `usbd_app_driver_get_cb()`
|
||||
- ``usbd_driver_open()`` add max length argument, and return length of interface (0 for not supported). Return value is used for finding appropriate driver
|
||||
- Add application implemented class driver via ``usbd_app_driver_get_cb()``
|
||||
- IAD is handled to assign driver id
|
||||
|
||||
- Added `tud_descriptor_device_qualifier_cb()` callback
|
||||
- Optimize `tu_fifo` bulk write/read transfer
|
||||
- Added ``tud_descriptor_device_qualifier_cb()`` callback
|
||||
- Optimize ``tu_fifo`` bulk write/read transfer
|
||||
- Forward non-std control request to class driver
|
||||
- Let application handle Microsoft OS 1.0 Descriptors (the 0xEE index string)
|
||||
- Fix OSAL FreeRTOS yield from ISR
|
||||
@ -1028,8 +1028,8 @@ USB Device
|
||||
|
||||
- CDC:
|
||||
- Send zero length packet for end of data when needed
|
||||
- Add `tud_cdc_tx_complete_cb()` callback
|
||||
- Change tud_cdc_n_write_flush() return number of bytes forced to transfer, and flush when writing enough data to fifo
|
||||
- Add ``tud_cdc_tx_complete_cb()`` callback
|
||||
- Change ``tud_cdc_n_write_flush()`` return number of bytes forced to transfer, and flush when writing enough data to fifo
|
||||
|
||||
- MIDI:
|
||||
- Add packet interface
|
||||
@ -1039,10 +1039,10 @@ USB Device
|
||||
- DFU Runtime: fix response to SET_INTERFACE and DFU_GETSTATUS request
|
||||
|
||||
- Rename some configure macro to make it clear that those are used directly for endpoint transfer
|
||||
- CFG_TUD_HID_BUFSIZE to CFG_TUD_HID_EP_BUFSIZE
|
||||
- CFG_TUD_CDC_EPSIZE to CFG_TUD_CDC_EP_BUFSIZE
|
||||
- CFG_TUD_MSC_BUFSIZE to CFG_TUD_MSC_EP_BUFSIZE
|
||||
- CFG_TUD_MIDI_EPSIZE to CFG_TUD_MIDI_EP_BUFSIZE
|
||||
- ``CFG_TUD_HID_BUFSIZE`` to ``CFG_TUD_HID_EP_BUFSIZE``
|
||||
- ``CFG_TUD_CDC_EPSIZE`` to ``CFG_TUD_CDC_EP_BUFSIZE``
|
||||
- ``CFG_TUD_MSC_BUFSIZE`` to ``CFG_TUD_MSC_EP_BUFSIZE``
|
||||
- ``CFG_TUD_MIDI_EPSIZE`` to ``CFG_TUD_MIDI_EP_BUFSIZE``
|
||||
|
||||
- HID:
|
||||
- Fix gamepad template descriptor
|
||||
@ -1061,15 +1061,15 @@ USB Host
|
||||
Examples
|
||||
--------
|
||||
|
||||
- Add new hid_composite_freertos
|
||||
- Add new dynamic_configuration to demonstrate how to switch configuration descriptors
|
||||
- Add new hid_multiple_interface
|
||||
- Add new ``hid_composite_freertos``
|
||||
- Add new ``dynamic_configuration`` to demonstrate how to switch configuration descriptors
|
||||
- Add new ``hid_multiple_interface``
|
||||
|
||||
- Enhance `net_lwip_webserver` example
|
||||
- Enhance ``net_lwip_webserver`` example
|
||||
- Add multiple configuration: RNDIS for Windows, CDC-ECM for macOS (Linux will work with both)
|
||||
- Update lwip to STABLE-2_1_2_RELEASE for net_lwip_webserver
|
||||
- Update lwip to STABLE-2_1_2_RELEASE for ``net_lwip_webserver``
|
||||
|
||||
- Added new Audio example: audio_test uac2_headsest
|
||||
- Added new Audio example: ``audio_test`` ``uac2_headsest``
|
||||
|
||||
New Boards
|
||||
----------
|
||||
@ -1110,37 +1110,37 @@ Added
|
||||
- Added multiple instances support for CDC and MIDI
|
||||
- Added a handful of unit test with Ceedling.
|
||||
- Added LOG support for debugging with CFG_TUSB_DEBUG
|
||||
- Added `tud_descriptor_bos_cb()` for BOS descriptor (required for USB 2.1)
|
||||
- Added `dcd_edpt0_status_complete()` as optional API for DCD
|
||||
- Added ``tud_descriptor_bos_cb()`` for BOS descriptor (required for USB 2.1)
|
||||
- Added ``dcd_edpt0_status_complete()`` as optional API for DCD
|
||||
|
||||
**Examples**
|
||||
|
||||
Following examples are added:
|
||||
|
||||
- board_test
|
||||
- cdc_dual_ports
|
||||
- dfu_rt
|
||||
- hid_composite
|
||||
- net_lwip_webserver
|
||||
- usbtmc
|
||||
- webusb_serial
|
||||
- ``board_test``
|
||||
- ``cdc_dual_ports``
|
||||
- ``dfu_rt``
|
||||
- ``hid_composite``
|
||||
- ``net_lwip_webserver``
|
||||
- ``usbtmc``
|
||||
- ``webusb_serial``
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
- Changed `tud_descriptor_string_cb()` to have additional Language ID argument
|
||||
- Merged hal_nrf5x.c into dcd_nrf5x.c
|
||||
- Merged dcd_samd21.c and dcd_samd51.c into dcd_samd.c
|
||||
- Generalized dcd_stm32f4.c to dcd_synopsys.c
|
||||
- Changed cdc_msc_hid to cdc_msc (drop hid) due to limited endpoints number of some MCUs
|
||||
- Changed ``tud_descriptor_string_cb()`` to have additional Language ID argument
|
||||
- Merged ``hal_nrf5x.c`` into ``dcd_nrf5x.c``
|
||||
- Merged ``dcd_samd21.c`` and ``dcd_samd51.c`` into ``dcd_samd.c``
|
||||
- Generalized ``dcd_stm32f4.c`` to ``dcd_synopsys.c``
|
||||
- Changed ``cdc_msc_hid`` to ``cdc_msc`` (drop hid) due to limited endpoints number of some MCUs
|
||||
- Improved DCD SAMD stability, fix missing setup packet occasionally
|
||||
- Improved usbd/usbd_control with proper handling of zero-length packet (ZLP)
|
||||
- Improved ``usbd/usbd_control`` with proper handling of zero-length packet (ZLP)
|
||||
- Improved STM32 DCD FSDev
|
||||
- Improved STM32 DCD Synopsys
|
||||
- Migrated CI from Travis to Github Action
|
||||
- Updated nrfx submodule to 2.1.0
|
||||
- Fixed mynewt osal queue definition
|
||||
- Fixed cdc_msc_freertos example build for all MCUs
|
||||
- Fixed ``cdc_msc_freertos`` example build for all MCUs
|
||||
|
||||
|
||||
0.5.0 (2019-06)
|
||||
|
||||
@ -1 +0,0 @@
|
||||
.. include:: ../../CONTRIBUTORS.rst
|
||||
1
docs/info/contributors.rst
Normal file
1
docs/info/contributors.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../CONTRIBUTORS.rst
|
||||
@ -7,25 +7,44 @@ It is responsible for getting the MCU started and the USB peripheral clocked wit
|
||||
|
||||
- One LED : for status
|
||||
- One Button : to get input from user
|
||||
- One UART : optional for device, but required for host examples
|
||||
- One UART : needed for logging with LOGGER=uart, maybe required for host/dual examples
|
||||
|
||||
Following boards are supported
|
||||
|
||||
Analog Devices
|
||||
--------------
|
||||
|
||||
============= ================ ======== =========================================================================================================================== ======
|
||||
Board Name Family URL Note
|
||||
============= ================ ======== =========================================================================================================================== ======
|
||||
max32650evkit MAX32650 EVKIT max32650 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32650-evkit.html#eb-overview
|
||||
max32650fthr MAX32650 Feather max32650 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32650fthr.html
|
||||
max32651evkit MAX32651 EVKIT max32650 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32651-evkit.html
|
||||
max32666evkit MAX32666 EVKIT max32666 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32666evkit.html
|
||||
max32666fthr MAX32666 Feather max32666 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32666fthr.html
|
||||
apard32690 APARD32690-SL max32690 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/ad-apard32690-sl.html
|
||||
max32690evkit MAX32690 EVKIT max32690 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32690evkit.html
|
||||
max78002evkit MAX78002 EVKIT max78002 https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max78002evkit.html
|
||||
============= ================ ======== =========================================================================================================================== ======
|
||||
============= ================ ======== ================================================================================================================= ======
|
||||
Board Name Family URL Note
|
||||
============= ================ ======== ================================================================================================================= ======
|
||||
apard32690 APARD32690-SL maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/ad-apard32690-sl.html
|
||||
max32650evkit MAX32650 EVKIT maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32650-evkit.html
|
||||
max32650fthr MAX32650 Feather maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32650fthr.html
|
||||
max32651evkit MAX32651 EVKIT maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32651-evkit.html
|
||||
max32666evkit MAX32666 EVKIT maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32666evkit.html
|
||||
max32666fthr MAX32666 Feather maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32666fthr.html
|
||||
max32690evkit MAX32690 EVKIT maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max32690evkit.html
|
||||
max78002evkit MAX78002 EVKIT maxim https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/max78002evkit.html
|
||||
============= ================ ======== ================================================================================================================= ======
|
||||
|
||||
Artery
|
||||
------
|
||||
|
||||
========================= ============================= ============= ==================================================== ======
|
||||
Board Name Family URL Note
|
||||
========================= ============================= ============= ==================================================== ======
|
||||
at_start_f402 AT-START-F402 at32f402_405 https://www.arterychip.com/en/product/AT32F402.jsp
|
||||
at_start_f405 AT-START-F405 at32f402_405 https://www.arterychip.com/en/product/AT32F405.jsp
|
||||
at32f403a_weact_blackpill WeAct BlackPill AT32F403ACGU7 at32f403a_407 https://github.com/WeActStudio/WeActStudio.BlackPill
|
||||
at_start_f403a AT-START-F403a at32f403a_407 https://www.arterychip.com/en/product/AT32F403.jsp
|
||||
at_start_f407 AT-START-F407 at32f403a_407 https://www.arterychip.com/en/product/AT32F407.jsp
|
||||
at_start_f413 AT-START-F413 at32f413 https://www.arterychip.com/en/product/AT32F413.jsp
|
||||
at_start_f415 AT-START-F415 at32f415 https://www.arterychip.com/en/product/AT32F415.jsp
|
||||
at_start_f423 AT-START-F423 at32f423 https://www.arterychip.com/en/product/AT32F423.jsp
|
||||
at_start_f425 AT-START-F425 at32f425 https://www.arterychip.com/en/product/AT32F425.jsp
|
||||
at_start_f435 AT-START-F435 at32f435_437 https://www.arterychip.com/en/product/AT32F435.jsp
|
||||
at_start_f437 AT-START-F437 at32f435_437 https://www.arterychip.com/en/product/AT32F437.jsp
|
||||
========================= ============================= ============= ==================================================== ======
|
||||
|
||||
Bridgetek
|
||||
---------
|
||||
@ -43,6 +62,7 @@ Espressif
|
||||
Board Name Family URL Note
|
||||
========================= ============================== ========= ======================================================================================================== ======
|
||||
adafruit_feather_esp32_v2 Adafruit Feather ESP32 v2 espressif https://www.adafruit.com/product/5400
|
||||
adafruit_feather_esp32c6 Adafruit Feather EPS32-C6 espressif https://www.adafruit.com/product/5933
|
||||
adafruit_feather_esp32s2 Adafruit Feather ESP32S2 espressif https://www.adafruit.com/product/5000
|
||||
adafruit_feather_esp32s3 Adafruit Feather ESP32S3 espressif https://www.adafruit.com/product/5323
|
||||
adafruit_magtag_29gray Adafruit MagTag 2.9" Grayscale espressif https://www.adafruit.com/product/4800
|
||||
@ -165,6 +185,7 @@ lpcxpresso55s28 LPCXpresso55s28 lpc55 ht
|
||||
lpcxpresso55s69 LPCXpresso55s69 lpc55 https://www.nxp.com/design/design-center/software/development-software/mcuxpresso-software-and-tools-/lpcxpresso-boards/lpcxpresso55s69-development-board:LPC55S69-EVK
|
||||
mcu_link MCU Link lpc55 https://www.nxp.com/design/design-center/software/development-software/mcuxpresso-software-and-tools-/mcu-link-debug-probe:MCU-LINK
|
||||
frdm_mcxa153 Freedom MCXA153 mcx https://www.nxp.com/design/design-center/development-boards-and-designs/FRDM-MCXA153
|
||||
frdm_mcxa156 Freedom MCXA156 mcx https://www.nxp.com/design/design-center/development-boards-and-designs/FRDM-MCXA156
|
||||
frdm_mcxn947 Freedom MCXN947 mcx https://www.nxp.com/design/design-center/development-boards-and-designs/FRDM-MCXN947
|
||||
mcxn947brk MCXN947 Breakout mcx n/a
|
||||
================== ========================================= ============= ========================================================================================================================================================================= ======
|
||||
@ -190,13 +211,19 @@ pca10100 Nordic nRF52833 DK nrf ht
|
||||
Raspberry Pi
|
||||
------------
|
||||
|
||||
================= ================= ============== ========================================================== ======
|
||||
Board Name Family URL Note
|
||||
================= ================= ============== ========================================================== ======
|
||||
raspberrypi_zero Raspberry Pi Zero broadcom_32bit https://www.raspberrypi.org/products/raspberry-pi-zero/
|
||||
raspberrypi_cm4 Raspberry CM4 broadcom_64bit https://www.raspberrypi.org/products/compute-module-4
|
||||
raspberrypi_zero2 Raspberry Zero2 broadcom_64bit https://www.raspberrypi.org/products/raspberry-pi-zero-2-w
|
||||
================= ================= ============== ========================================================== ======
|
||||
================================ ============================================ ============== ========================================================== ======
|
||||
Board Name Family URL Note
|
||||
================================ ============================================ ============== ========================================================== ======
|
||||
raspberrypi_zero Raspberry Pi Zero broadcom_32bit https://www.raspberrypi.org/products/raspberry-pi-zero/
|
||||
raspberrypi_cm4 Raspberry CM4 broadcom_64bit https://www.raspberrypi.org/products/compute-module-4
|
||||
raspberrypi_zero2 Raspberry Zero2 broadcom_64bit https://www.raspberrypi.org/products/raspberry-pi-zero-2-w
|
||||
adafruit_feather_rp2040_usb_host Adafruit Feather RP2040 with USB Type A Host rp2040 https://www.adafruit.com/product/5723
|
||||
adafruit_fruit_jam Adafruit Fruit Jam - Mini RP2350 rp2040 https://www.adafruit.com/product/6200
|
||||
adafruit_metro_rp2350 Adafruit Metro RP2350 rp2040 https://www.adafruit.com/product/6003
|
||||
raspberry_pi_pico Pico rp2040 https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
raspberry_pi_pico2 Pico2 rp2040 https://www.raspberrypi.com/products/raspberry-pi-pico-2/
|
||||
raspberry_pi_pico_w Pico rp2040 https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
================================ ============================================ ============== ========================================================== ======
|
||||
|
||||
Renesas
|
||||
-------
|
||||
@ -219,62 +246,66 @@ uno_r4 Arduino UNO R4 ra https://store-usa.arduino
|
||||
STMicroelectronics
|
||||
------------------
|
||||
|
||||
=================== ================================= ======== ================================================================= ======
|
||||
Board Name Family URL Note
|
||||
=================== ================================= ======== ================================================================= ======
|
||||
stm32c071nucleo STM32C071 Nucleo stm32c0 https://www.st.com/en/evaluation-tools/nucleo-g071rb.html
|
||||
stm32f070rbnucleo STM32 F070 Nucleo stm32f0 https://www.st.com/en/evaluation-tools/nucleo-f070rb.html
|
||||
stm32f072disco STM32 F072 Discovery stm32f0 https://www.st.com/en/evaluation-tools/32f072bdiscovery.html
|
||||
stm32f072eval STM32 F072 Eval stm32f0 https://www.st.com/en/evaluation-tools/stm32072b-eval.html
|
||||
stm32f103_bluepill STM32 F103 Bluepill stm32f1 https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill
|
||||
stm32f103_mini_2 STM32 F103 Mini v2 stm32f1 https://stm32-base.org/boards/STM32F103RCT6-STM32-Mini-V2.0
|
||||
stm32f103ze_iar IAR STM32 F103ze starter kit stm32f1 n/a
|
||||
stm32f207nucleo STM32 F207 Nucleo stm32f2 https://www.st.com/en/evaluation-tools/nucleo-f207zg.html
|
||||
stm32f303disco STM32 F303 Discovery stm32f3 https://www.st.com/en/evaluation-tools/stm32f3discovery.html
|
||||
feather_stm32f405 Adafruit Feather STM32F405 stm32f4 https://www.adafruit.com/product/4382
|
||||
pyboardv11 Pyboard v1.1 stm32f4 https://www.adafruit.com/product/2390
|
||||
stm32f401blackpill STM32 F401 Blackpill stm32f4 https://stm32-base.org/boards/STM32F401CCU6-WeAct-Black-Pill-V1.2
|
||||
stm32f407blackvet STM32 F407 Blackvet stm32f4 https://stm32-base.org/boards/STM32F407VET6-STM32-F4VE-V2.0
|
||||
stm32f407disco STM32 F407 Discovery stm32f4 https://www.st.com/en/evaluation-tools/stm32f4discovery.html
|
||||
stm32f411blackpill STM32 F411 Blackpill stm32f4 https://stm32-base.org/boards/STM32F411CEU6-WeAct-Black-Pill-V2.0
|
||||
stm32f411disco STM32 F411 Discovery stm32f4 https://www.st.com/en/evaluation-tools/32f411ediscovery.html
|
||||
stm32f412disco STM32 F412 Discovery stm32f4 https://www.st.com/en/evaluation-tools/32f412gdiscovery.html
|
||||
stm32f412nucleo STM32 F412 Nucleo stm32f4 https://www.st.com/en/evaluation-tools/nucleo-f412zg.html
|
||||
stm32f439nucleo STM32 F439 Nucleo stm32f4 https://www.st.com/en/evaluation-tools/nucleo-f439zi.html
|
||||
stlinkv3mini Stlink-v3 mini stm32f7 https://www.st.com/en/development-tools/stlink-v3mini.html
|
||||
stm32f723disco STM32 F723 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f723ediscovery.html
|
||||
stm32f746disco STM32 F746 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f746gdiscovery.html
|
||||
stm32f746nucleo STM32 F746 Nucleo stm32f7 https://www.st.com/en/evaluation-tools/nucleo-f746zg.html
|
||||
stm32f767nucleo STM32 F767 Nucleo stm32f7 https://www.st.com/en/evaluation-tools/nucleo-f767zi.html
|
||||
stm32f769disco STM32 F769 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f769idiscovery.html
|
||||
stm32g0b1nucleo STM32 G0B1 Nucleo stm32g0 https://www.st.com/en/evaluation-tools/nucleo-g0b1re.html
|
||||
b_g474e_dpow1 STM32 B-G474E-DPOW1 Discovery kit stm32g4 https://www.st.com/en/evaluation-tools/b-g474e-dpow1.html
|
||||
stm32g474nucleo STM32 G474 Nucleo stm32g4 https://www.st.com/en/evaluation-tools/nucleo-g474re.html
|
||||
stm32g491nucleo STM32 G491 Nucleo stm32g4 https://www.st.com/en/evaluation-tools/nucleo-g491re.html
|
||||
stm32h503nucleo STM32 H503 Nucleo stm32h5 https://www.st.com/en/evaluation-tools/nucleo-h503rb.html
|
||||
stm32h563nucleo STM32 H563 Nucleo stm32h5 https://www.st.com/en/evaluation-tools/nucleo-h563zi.html
|
||||
stm32h573i_dk STM32 H573i Discovery stm32h5 https://www.st.com/en/evaluation-tools/stm32h573i-dk.html
|
||||
daisyseed Daisy Seed stm32h7 https://electro-smith.com/products/daisy-seed
|
||||
stm32h723nucleo STM32 H723 Nucleo stm32h7 https://www.st.com/en/evaluation-tools/nucleo-h723zg.html
|
||||
stm32h743eval STM32 H743 Eval stm32h7 https://www.st.com/en/evaluation-tools/stm32h743i-eval.html
|
||||
stm32h743nucleo STM32 H743 Nucleo stm32h7 https://www.st.com/en/evaluation-tools/nucleo-h743zi.html
|
||||
stm32h745disco STM32 H745 Discovery stm32h7 https://www.st.com/en/evaluation-tools/stm32h745i-disco.html
|
||||
stm32h750_weact STM32 H750 WeAct stm32h7 https://www.adafruit.com/product/5032
|
||||
stm32h750bdk STM32 H750b Discovery Kit stm32h7 https://www.st.com/en/evaluation-tools/stm32h750b-dk.html
|
||||
waveshare_openh743i Waveshare Open H743i stm32h7 https://www.waveshare.com/openh743i-c-standard.htm
|
||||
stm32l052dap52 STM32 L052 DAP stm32l0 n/a
|
||||
stm32l0538disco STM32 L0538 Discovery stm32l0 https://www.st.com/en/evaluation-tools/32l0538discovery.html
|
||||
stm32l412nucleo STM32 L412 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l412kb.html
|
||||
stm32l476disco STM32 L476 Disco stm32l4 https://www.st.com/en/evaluation-tools/32l476gdiscovery.html
|
||||
stm32l4p5nucleo STM32 L4P5 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l4p5zg.html
|
||||
stm32l4r5nucleo STM32 L4R5 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l4r5zi.html
|
||||
b_u585i_iot2a STM32 B-U585i IOT2A Discovery kit stm32u5 https://www.st.com/en/evaluation-tools/b-u585i-iot02a.html
|
||||
stm32u545nucleo STM32 U545 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u545re-q.html
|
||||
stm32u575eval STM32 U575 Eval stm32u5 https://www.st.com/en/evaluation-tools/stm32u575i-ev.html
|
||||
stm32u575nucleo STM32 U575 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u575zi-q.html
|
||||
stm32u5a5nucleo STM32 U5a5 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u5a5zj-q.html
|
||||
stm32wb55nucleo STM32 P-NUCLEO-WB55 stm32wb https://www.st.com/en/evaluation-tools/p-nucleo-wb55.html
|
||||
=================== ================================= ======== ================================================================= ======
|
||||
=================== ================================= ========= ================================================================= ======
|
||||
Board Name Family URL Note
|
||||
=================== ================================= ========= ================================================================= ======
|
||||
stm32c071nucleo STM32C071 Nucleo stm32c0 https://www.st.com/en/evaluation-tools/nucleo-g071rb.html
|
||||
stm32f070rbnucleo STM32 F070 Nucleo stm32f0 https://www.st.com/en/evaluation-tools/nucleo-f070rb.html
|
||||
stm32f072disco STM32 F072 Discovery stm32f0 https://www.st.com/en/evaluation-tools/32f072bdiscovery.html
|
||||
stm32f072eval STM32 F072 Eval stm32f0 https://www.st.com/en/evaluation-tools/stm32072b-eval.html
|
||||
stm32f103_bluepill STM32 F103 Bluepill stm32f1 https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill
|
||||
stm32f103_mini_2 STM32 F103 Mini v2 stm32f1 https://stm32-base.org/boards/STM32F103RCT6-STM32-Mini-V2.0
|
||||
stm32f103ze_iar IAR STM32 F103ze starter kit stm32f1 n/a
|
||||
stm32f207nucleo STM32 F207 Nucleo stm32f2 https://www.st.com/en/evaluation-tools/nucleo-f207zg.html
|
||||
stm32f303disco STM32 F303 Discovery stm32f3 https://www.st.com/en/evaluation-tools/stm32f3discovery.html
|
||||
feather_stm32f405 Adafruit Feather STM32F405 stm32f4 https://www.adafruit.com/product/4382
|
||||
pyboardv11 Pyboard v1.1 stm32f4 https://www.adafruit.com/product/2390
|
||||
stm32f401blackpill STM32 F401 Blackpill stm32f4 https://stm32-base.org/boards/STM32F401CCU6-WeAct-Black-Pill-V1.2
|
||||
stm32f407blackvet STM32 F407 Blackvet stm32f4 https://stm32-base.org/boards/STM32F407VET6-STM32-F4VE-V2.0
|
||||
stm32f407disco STM32 F407 Discovery stm32f4 https://www.st.com/en/evaluation-tools/stm32f4discovery.html
|
||||
stm32f411blackpill STM32 F411 Blackpill stm32f4 https://stm32-base.org/boards/STM32F411CEU6-WeAct-Black-Pill-V2.0
|
||||
stm32f411disco STM32 F411 Discovery stm32f4 https://www.st.com/en/evaluation-tools/32f411ediscovery.html
|
||||
stm32f412disco STM32 F412 Discovery stm32f4 https://www.st.com/en/evaluation-tools/32f412gdiscovery.html
|
||||
stm32f412nucleo STM32 F412 Nucleo stm32f4 https://www.st.com/en/evaluation-tools/nucleo-f412zg.html
|
||||
stm32f439nucleo STM32 F439 Nucleo stm32f4 https://www.st.com/en/evaluation-tools/nucleo-f439zi.html
|
||||
stlinkv3mini Stlink-v3 mini stm32f7 https://www.st.com/en/development-tools/stlink-v3mini.html
|
||||
stm32f723disco STM32 F723 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f723ediscovery.html
|
||||
stm32f746disco STM32 F746 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f746gdiscovery.html
|
||||
stm32f746nucleo STM32 F746 Nucleo stm32f7 https://www.st.com/en/evaluation-tools/nucleo-f746zg.html
|
||||
stm32f767nucleo STM32 F767 Nucleo stm32f7 https://www.st.com/en/evaluation-tools/nucleo-f767zi.html
|
||||
stm32f769disco STM32 F769 Discovery stm32f7 https://www.st.com/en/evaluation-tools/32f769idiscovery.html
|
||||
stm32g0b1nucleo STM32 G0B1 Nucleo stm32g0 https://www.st.com/en/evaluation-tools/nucleo-g0b1re.html
|
||||
b_g474e_dpow1 STM32 B-G474E-DPOW1 Discovery kit stm32g4 https://www.st.com/en/evaluation-tools/b-g474e-dpow1.html
|
||||
stm32g474nucleo STM32 G474 Nucleo stm32g4 https://www.st.com/en/evaluation-tools/nucleo-g474re.html
|
||||
stm32g491nucleo STM32 G491 Nucleo stm32g4 https://www.st.com/en/evaluation-tools/nucleo-g491re.html
|
||||
stm32h503nucleo STM32 H503 Nucleo stm32h5 https://www.st.com/en/evaluation-tools/nucleo-h503rb.html
|
||||
stm32h563nucleo STM32 H563 Nucleo stm32h5 https://www.st.com/en/evaluation-tools/nucleo-h563zi.html
|
||||
stm32h573i_dk STM32 H573i Discovery stm32h5 https://www.st.com/en/evaluation-tools/stm32h573i-dk.html
|
||||
daisyseed Daisy Seed stm32h7 https://electro-smith.com/products/daisy-seed
|
||||
stm32h723nucleo STM32 H723 Nucleo stm32h7 https://www.st.com/en/evaluation-tools/nucleo-h723zg.html
|
||||
stm32h743eval STM32 H743 Eval stm32h7 https://www.st.com/en/evaluation-tools/stm32h743i-eval.html
|
||||
stm32h743nucleo STM32 H743 Nucleo stm32h7 https://www.st.com/en/evaluation-tools/nucleo-h743zi.html
|
||||
stm32h745disco STM32 H745 Discovery stm32h7 https://www.st.com/en/evaluation-tools/stm32h745i-disco.html
|
||||
stm32h750_weact STM32 H750 WeAct stm32h7 https://www.adafruit.com/product/5032
|
||||
stm32h750bdk STM32 H750b Discovery Kit stm32h7 https://www.st.com/en/evaluation-tools/stm32h750b-dk.html
|
||||
waveshare_openh743i Waveshare Open H743i stm32h7 https://www.waveshare.com/openh743i-c-standard.htm
|
||||
stm32h7s3nucleo STM32 H7S3L8 Nucleo stm32h7rs https://www.st.com/en/evaluation-tools/nucleo-h7s3l8.html
|
||||
stm32l052dap52 STM32 L052 DAP stm32l0 n/a
|
||||
stm32l0538disco STM32 L0538 Discovery stm32l0 https://www.st.com/en/evaluation-tools/32l0538discovery.html
|
||||
stm32l412nucleo STM32 L412 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l412kb.html
|
||||
stm32l476disco STM32 L476 Disco stm32l4 https://www.st.com/en/evaluation-tools/32l476gdiscovery.html
|
||||
stm32l4p5nucleo STM32 L4P5 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l4p5zg.html
|
||||
stm32l4r5nucleo STM32 L4R5 Nucleo stm32l4 https://www.st.com/en/evaluation-tools/nucleo-l4r5zi.html
|
||||
stm32n6570dk STM32 N6570-DK stm32n6 https://www.st.com/en/evaluation-tools/stm32n6570-dk.html
|
||||
stm32n657nucleo STM32 N657X0-Q Nucleo stm32n6 https://www.st.com/en/evaluation-tools/nucleo-n657x0-q.html
|
||||
b_u585i_iot2a STM32 B-U585i IOT2A Discovery kit stm32u5 https://www.st.com/en/evaluation-tools/b-u585i-iot02a.html
|
||||
stm32u545nucleo STM32 U545 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u545re-q.html
|
||||
stm32u575eval STM32 U575 Eval stm32u5 https://www.st.com/en/evaluation-tools/stm32u575i-ev.html
|
||||
stm32u575nucleo STM32 U575 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u575zi-q.html
|
||||
stm32u5a5nucleo STM32 U5a5 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u5a5zj-q.html
|
||||
stm32wb55nucleo STM32 P-NUCLEO-WB55 stm32wb https://www.st.com/en/evaluation-tools/p-nucleo-wb55.html
|
||||
stm32wba_nucleo STM32 NUCLEO-WBA65RI stm32wba https://www.st.com/en/evaluation-tools/nucleo-wba65ri.html
|
||||
=================== ================================= ========= ================================================================= ======
|
||||
|
||||
Sunxi
|
||||
-----
|
||||
@ -316,5 +347,6 @@ ch32v103r_r1_1v0 CH32V103R-R1-1v1 ch32v10x https://github.com/openwch/ch32v10
|
||||
ch32v203c_r0_1v0 CH32V203C-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0
|
||||
ch32v203g_r0_1v0 CH32V203G-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0
|
||||
nanoch32v203 nanoCH32V203 ch32v20x https://github.com/wuxx/nanoCH32V203
|
||||
ch32v307v_r1_1v0 CH32V307V-R1-1v0 ch32v307 https://github.com/openwch/ch32v307/tree/main/SCHPCB/CH32V307V-R1-1v0
|
||||
ch32v307v_r1_1v0 CH32V307V-R1-1v0 ch32v30x https://github.com/openwch/ch32v307/tree/main/SCHPCB/CH32V307V-R1-1v0
|
||||
nanoch32v305 nanoCH32V305 ch32v30x https://github.com/wuxx/nanoCH32V305
|
||||
================ ================ ======== ===================================================================== ======
|
||||
|
||||
@ -4,70 +4,84 @@ Dependencies
|
||||
|
||||
MCU low-level peripheral driver and external libraries for building TinyUSB examples
|
||||
|
||||
======================================== ============================================================== ======================================== ====================================================================================================================================================================================================================================================================================================================================
|
||||
Local Path Repo Commit Required by
|
||||
======================================== ============================================================== ======================================== ====================================================================================================================================================================================================================================================================================================================================
|
||||
hw/mcu/allwinner https://github.com/hathach/allwinner_driver.git 8e5e89e8e132c0fd90e72d5422e5d3d68232b756 fc100s
|
||||
hw/mcu/analog/max32 https://github.com/analogdevicesinc/msdk.git b20b398d3e5e2007594e54a74ba3d2a2e50ddd75 max32650 max32666 max32690 max78002
|
||||
hw/mcu/bridgetek/ft9xx/ft90x-sdk https://github.com/BRTSG-FOSS/ft90x-sdk.git 91060164afe239fcb394122e8bf9eb24d3194eb1 brtmm90x
|
||||
hw/mcu/broadcom https://github.com/adafruit/broadcom-peripherals.git 08370086080759ed54ac1136d62d2ad24c6fa267 broadcom_32bit broadcom_64bit
|
||||
hw/mcu/gd/nuclei-sdk https://github.com/Nuclei-Software/nuclei-sdk.git 7eb7bfa9ea4fbeacfafe1d5f77d5a0e6ed3922e7 gd32vf103
|
||||
hw/mcu/infineon/mtb-xmclib-cat3 https://github.com/Infineon/mtb-xmclib-cat3.git daf5500d03cba23e68c2f241c30af79cd9d63880 xmc4000
|
||||
hw/mcu/microchip https://github.com/hathach/microchip_driver.git 9e8b37e307d8404033bb881623a113931e1edf27 sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg
|
||||
hw/mcu/mindmotion/mm32sdk https://github.com/hathach/mm32sdk.git b93e856211060ae825216c6a1d6aa347ec758843 mm32
|
||||
hw/mcu/nordic/nrfx https://github.com/NordicSemiconductor/nrfx.git 7c47cc0a56ce44658e6da2458e86cd8783ccc4a2 nrf
|
||||
hw/mcu/nuvoton https://github.com/majbthrd/nuc_driver.git 2204191ec76283371419fbcec207da02e1bc22fa nuc
|
||||
hw/mcu/nxp/lpcopen https://github.com/hathach/nxp_lpcopen.git b41cf930e65c734d8ec6de04f1d57d46787c76ae lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43
|
||||
hw/mcu/nxp/mcux-sdk https://github.com/hathach/mcux-sdk.git 144f1eb7ea8c06512e12f12b27383601c0272410 kinetis_k kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt
|
||||
hw/mcu/raspberry_pi/Pico-PIO-USB https://github.com/sekigon-gonnoc/Pico-PIO-USB.git fe9133fc513b82cc3dc62c67cb51f2339cf29ef7 rp2040
|
||||
hw/mcu/renesas/fsp https://github.com/renesas/fsp.git edcc97d684b6f716728a60d7a6fea049d9870bd6 ra
|
||||
hw/mcu/renesas/rx https://github.com/kkitayam/rx_device.git 706b4e0cf485605c32351e2f90f5698267996023 rx
|
||||
hw/mcu/silabs/cmsis-dfp-efm32gg12b https://github.com/cmsis-packs/cmsis-dfp-efm32gg12b.git f1c31b7887669cb230b3ea63f9b56769078960bc efm32
|
||||
hw/mcu/sony/cxd56/spresense-exported-sdk https://github.com/sonydevworld/spresense-exported-sdk.git 2ec2a1538362696118dc3fdf56f33dacaf8f4067 spresense
|
||||
hw/mcu/st/cmsis_device_c0 https://github.com/STMicroelectronics/cmsis_device_c0.git fb56b1b70c73b74eacda2a4bcc36886444364ab3 stm32c0
|
||||
hw/mcu/st/cmsis_device_f0 https://github.com/STMicroelectronics/cmsis_device_f0.git 2fc25ee22264bc27034358be0bd400b893ef837e stm32f0
|
||||
hw/mcu/st/cmsis_device_f1 https://github.com/STMicroelectronics/cmsis_device_f1.git 6601104a6397299b7304fd5bcd9a491f56cb23a6 stm32f1
|
||||
hw/mcu/st/cmsis_device_f2 https://github.com/STMicroelectronics/cmsis_device_f2.git 182fcb3681ce116816feb41b7764f1b019ce796f stm32f2
|
||||
hw/mcu/st/cmsis_device_f3 https://github.com/STMicroelectronics/cmsis_device_f3.git 5e4ee5ed7a7b6c85176bb70a9fd3c72d6eb99f1b stm32f3
|
||||
hw/mcu/st/cmsis_device_f4 https://github.com/STMicroelectronics/cmsis_device_f4.git 2615e866fa48fe1ff1af9e31c348813f2b19e7ec stm32f4
|
||||
hw/mcu/st/cmsis_device_f7 https://github.com/STMicroelectronics/cmsis_device_f7.git 25b0463439303b7a38f0d27b161f7d2f3c096e79 stm32f7
|
||||
hw/mcu/st/cmsis_device_g0 https://github.com/STMicroelectronics/cmsis_device_g0.git 3a23e1224417f3f2d00300ecd620495e363f2094 stm32g0
|
||||
hw/mcu/st/cmsis_device_g4 https://github.com/STMicroelectronics/cmsis_device_g4.git ce822adb1dc552b3aedd13621edbc7fdae124878 stm32g4
|
||||
hw/mcu/st/cmsis_device_h5 https://github.com/STMicroelectronics/cmsis_device_h5.git cd2d1d579743de57b88ccaf61a968b9c05848ffc stm32h5
|
||||
hw/mcu/st/cmsis_device_h7 https://github.com/STMicroelectronics/cmsis_device_h7.git 60dc2c913203dc8629dc233d4384dcc41c91e77f stm32h7
|
||||
hw/mcu/st/cmsis_device_l0 https://github.com/STMicroelectronics/cmsis_device_l0.git 69cd5999fd40ae6e546d4905b21635c6ca1bcb92 stm32l0
|
||||
hw/mcu/st/cmsis_device_l1 https://github.com/STMicroelectronics/cmsis_device_l1.git 7f16ec0a1c4c063f84160b4cc6bf88ad554a823e stm32l1
|
||||
hw/mcu/st/cmsis_device_l4 https://github.com/STMicroelectronics/cmsis_device_l4.git 6ca7312fa6a5a460b5a5a63d66da527fdd8359a6 stm32l4
|
||||
hw/mcu/st/cmsis_device_l5 https://github.com/STMicroelectronics/cmsis_device_l5.git d922865fc0326a102c26211c44b8e42f52c1e53d stm32l5
|
||||
hw/mcu/st/cmsis_device_u5 https://github.com/STMicroelectronics/cmsis_device_u5.git 5ad9797c54ec3e55eff770fc9b3cd4a1aefc1309 stm32u5
|
||||
hw/mcu/st/cmsis_device_wb https://github.com/STMicroelectronics/cmsis_device_wb.git 9c5d1920dd9fabbe2548e10561d63db829bb744f stm32wb
|
||||
hw/mcu/st/stm32-mfxstm32l152 https://github.com/STMicroelectronics/stm32-mfxstm32l152.git 7f4389efee9c6a655b55e5df3fceef5586b35f9b stm32h7
|
||||
hw/mcu/st/stm32c0xx_hal_driver https://github.com/STMicroelectronics/stm32c0xx_hal_driver.git 41253e2f1d7ae4a4d0c379cf63f5bcf71fcf8eb3 stm32c0
|
||||
hw/mcu/st/stm32f0xx_hal_driver https://github.com/STMicroelectronics/stm32f0xx_hal_driver.git 0e95cd88657030f640a11e690a8a5186c7712ea5 stm32f0
|
||||
hw/mcu/st/stm32f1xx_hal_driver https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git 1dd9d3662fb7eb2a7f7d3bc0a4c1dc7537915a29 stm32f1
|
||||
hw/mcu/st/stm32f2xx_hal_driver https://github.com/STMicroelectronics/stm32f2xx_hal_driver.git c75ace9b908a9aca631193ebf2466963b8ea33d0 stm32f2
|
||||
hw/mcu/st/stm32f3xx_hal_driver https://github.com/STMicroelectronics/stm32f3xx_hal_driver.git 1761b6207318ede021706e75aae78f452d72b6fa stm32f3
|
||||
hw/mcu/st/stm32f4xx_hal_driver https://github.com/STMicroelectronics/stm32f4xx_hal_driver.git 04e99fbdabd00ab8f370f377c66b0a4570365b58 stm32f4
|
||||
hw/mcu/st/stm32f7xx_hal_driver https://github.com/STMicroelectronics/stm32f7xx_hal_driver.git f7ffdf6bf72110e58b42c632b0a051df5997e4ee stm32f7
|
||||
hw/mcu/st/stm32g0xx_hal_driver https://github.com/STMicroelectronics/stm32g0xx_hal_driver.git e911b12c7f67084d7f6b76157a4c0d4e2ec3779c stm32g0
|
||||
hw/mcu/st/stm32g4xx_hal_driver https://github.com/STMicroelectronics/stm32g4xx_hal_driver.git 8b4518417706d42eef5c14e56a650005abf478a8 stm32g4
|
||||
hw/mcu/st/stm32h5xx_hal_driver https://github.com/STMicroelectronics/stm32h5xx_hal_driver.git 2cf77de584196d619cec1b4586c3b9e2820a254e stm32h5
|
||||
hw/mcu/st/stm32h7xx_hal_driver https://github.com/STMicroelectronics/stm32h7xx_hal_driver.git d8461b980b59b1625207d8c4f2ce0a9c2a7a3b04 stm32h7
|
||||
hw/mcu/st/stm32l0xx_hal_driver https://github.com/STMicroelectronics/stm32l0xx_hal_driver.git fbdacaf6f8c82a4e1eb9bd74ba650b491e97e17b stm32l0
|
||||
hw/mcu/st/stm32l1xx_hal_driver https://github.com/STMicroelectronics/stm32l1xx_hal_driver.git 44efc446fa69ed8344e7fd966e68ed11043b35d9 stm32l1
|
||||
hw/mcu/st/stm32l4xx_hal_driver https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git aee3d5bf283ae5df87532b781bdd01b7caf256fc stm32l4
|
||||
hw/mcu/st/stm32l5xx_hal_driver https://github.com/STMicroelectronics/stm32l5xx_hal_driver.git 675c32a75df37f39d50d61f51cb0dcf53f07e1cb stm32l5
|
||||
hw/mcu/st/stm32u5xx_hal_driver https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git 4d93097a67928e9377e655ddd14622adc31b9770 stm32u5
|
||||
hw/mcu/st/stm32wbxx_hal_driver https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git 2c5f06638be516c1b772f768456ba637f077bac8 stm32wb
|
||||
hw/mcu/ti https://github.com/hathach/ti_driver.git 143ed6cc20a7615d042b03b21e070197d473e6e5 msp430 msp432e4 tm4c
|
||||
hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.git 77c4095087e5ed2c548ec9058e655d0b8757663b ch32f20x
|
||||
hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x
|
||||
hw/mcu/wch/ch32v20x https://github.com/openwch/ch32v20x.git c4c38f507e258a4e69b059ccc2dc27dde33cea1b ch32v20x
|
||||
hw/mcu/wch/ch32v307 https://github.com/openwch/ch32v307.git 184f21b852cb95eed58e86e901837bc9fff68775 ch32v307
|
||||
lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32u5 stm32wb sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c
|
||||
lib/CMSIS_6 https://github.com/ARM-software/CMSIS_6.git b0bbb0423b278ca632cfe1474eb227961d835fd2 ra
|
||||
lib/FreeRTOS-Kernel https://github.com/FreeRTOS/FreeRTOS-Kernel.git cc0e0707c0c748713485b870bb980852b210877f all
|
||||
lib/lwip https://github.com/lwip-tcpip/lwip.git 159e31b689577dbf69cf0683bbaffbd71fa5ee10 all
|
||||
lib/sct_neopixel https://github.com/gsteiert/sct_neopixel.git e73e04ca63495672d955f9268e003cffe168fcd8 lpc55
|
||||
tools/uf2 https://github.com/microsoft/uf2.git c594542b2faa01cc33a2b97c9fbebc38549df80a all
|
||||
======================================== ============================================================== ======================================== ====================================================================================================================================================================================================================================================================================================================================
|
||||
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
|
||||
Local Path Repo Commit Required by
|
||||
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
|
||||
hw/mcu/allwinner https://github.com/hathach/allwinner_driver.git 8e5e89e8e132c0fd90e72d5422e5d3d68232b756 fc100s
|
||||
hw/mcu/analog/msdk https://github.com/analogdevicesinc/msdk.git b20b398d3e5e2007594e54a74ba3d2a2e50ddd75 maxim
|
||||
hw/mcu/artery/at32f402_405 https://github.com/ArteryTek/AT32F402_405_Firmware_Library.git 4424515c2663e82438654e0947695295df2abdfe at32f402_405
|
||||
hw/mcu/artery/at32f403a_407 https://github.com/ArteryTek/AT32F403A_407_Firmware_Library.git f2cb360c3d28fada76b374308b8c4c61d37a090b at32f403a_407
|
||||
hw/mcu/artery/at32f413 https://github.com/ArteryTek/AT32F413_Firmware_Library.git f6fe62dfec9fd40c5b63d92fc5ef2c2b5e77a450 at32f413
|
||||
hw/mcu/artery/at32f415 https://github.com/ArteryTek/AT32F415_Firmware_Library.git 716f545aa1290ff144ccf023a8e797b951e1bc8e at32f415
|
||||
hw/mcu/artery/at32f423 https://github.com/ArteryTek/AT32F423_Firmware_Library.git 2afa7f12852e57a9e8aab3a892c641e1a8635a18 at32f423
|
||||
hw/mcu/artery/at32f425 https://github.com/ArteryTek/AT32F425_Firmware_Library.git 620233e1357d5c1b7e2bde6b9dd5196822b91817 at32f425
|
||||
hw/mcu/artery/at32f435_437 https://github.com/ArteryTek/AT32F435_437_Firmware_Library.git 25439cc6650a8ae0345934e8707a5f38c7ae41f8 at32f435_437
|
||||
hw/mcu/bridgetek/ft9xx/ft90x-sdk https://github.com/BRTSG-FOSS/ft90x-sdk.git 91060164afe239fcb394122e8bf9eb24d3194eb1 brtmm90x
|
||||
hw/mcu/broadcom https://github.com/adafruit/broadcom-peripherals.git 08370086080759ed54ac1136d62d2ad24c6fa267 broadcom_32bit broadcom_64bit
|
||||
hw/mcu/gd/nuclei-sdk https://github.com/Nuclei-Software/nuclei-sdk.git 7eb7bfa9ea4fbeacfafe1d5f77d5a0e6ed3922e7 gd32vf103
|
||||
hw/mcu/infineon/mtb-xmclib-cat3 https://github.com/Infineon/mtb-xmclib-cat3.git daf5500d03cba23e68c2f241c30af79cd9d63880 xmc4000
|
||||
hw/mcu/microchip https://github.com/hathach/microchip_driver.git 9e8b37e307d8404033bb881623a113931e1edf27 sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg
|
||||
hw/mcu/mindmotion/mm32sdk https://github.com/hathach/mm32sdk.git b93e856211060ae825216c6a1d6aa347ec758843 mm32
|
||||
hw/mcu/nordic/nrfx https://github.com/NordicSemiconductor/nrfx.git 7c47cc0a56ce44658e6da2458e86cd8783ccc4a2 nrf
|
||||
hw/mcu/nuvoton https://github.com/majbthrd/nuc_driver.git 2204191ec76283371419fbcec207da02e1bc22fa nuc
|
||||
hw/mcu/nxp/lpcopen https://github.com/hathach/nxp_lpcopen.git b41cf930e65c734d8ec6de04f1d57d46787c76ae lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43
|
||||
hw/mcu/nxp/mcux-sdk https://github.com/nxp-mcuxpresso/mcux-sdk a1bdae309a14ec95a4f64a96d3315a4f89c397c6 kinetis_k kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt
|
||||
hw/mcu/raspberry_pi/Pico-PIO-USB https://github.com/sekigon-gonnoc/Pico-PIO-USB.git 675543bcc9baa8170f868ab7ba316d418dbcf41f rp2040
|
||||
hw/mcu/renesas/fsp https://github.com/renesas/fsp.git edcc97d684b6f716728a60d7a6fea049d9870bd6 ra
|
||||
hw/mcu/renesas/rx https://github.com/kkitayam/rx_device.git 706b4e0cf485605c32351e2f90f5698267996023 rx
|
||||
hw/mcu/silabs/cmsis-dfp-efm32gg12b https://github.com/cmsis-packs/cmsis-dfp-efm32gg12b.git f1c31b7887669cb230b3ea63f9b56769078960bc efm32
|
||||
hw/mcu/sony/cxd56/spresense-exported-sdk https://github.com/sonydevworld/spresense-exported-sdk.git 2ec2a1538362696118dc3fdf56f33dacaf8f4067 spresense
|
||||
hw/mcu/st/cmsis-device-wba https://github.com/STMicroelectronics/cmsis-device-wba.git 647d8522e5fd15049e9a1cc30ed19d85e5911eaf stm32wba
|
||||
hw/mcu/st/cmsis_device_c0 https://github.com/STMicroelectronics/cmsis_device_c0.git 517611273f835ffe95318947647bc1408f69120d stm32c0
|
||||
hw/mcu/st/cmsis_device_f0 https://github.com/STMicroelectronics/cmsis_device_f0.git cbb5da5d48b4b5f2efacdc2f033be30f9d29889f stm32f0
|
||||
hw/mcu/st/cmsis_device_f1 https://github.com/STMicroelectronics/cmsis_device_f1.git c8e9a4a4f16b6d2cb2a2083cbe5161025280fb22 stm32f1
|
||||
hw/mcu/st/cmsis_device_f2 https://github.com/STMicroelectronics/cmsis_device_f2.git 49321f1e4d2bd3e65687b37f2652a28ea7983674 stm32f2
|
||||
hw/mcu/st/cmsis_device_f3 https://github.com/STMicroelectronics/cmsis_device_f3.git 5558e64e3675a1e1fcb1c71f468c7c407c1b1134 stm32f3
|
||||
hw/mcu/st/cmsis_device_f4 https://github.com/STMicroelectronics/cmsis_device_f4.git 3c77349ce04c8af401454cc51f85ea9a50e34fc1 stm32f4
|
||||
hw/mcu/st/cmsis_device_f7 https://github.com/STMicroelectronics/cmsis_device_f7.git 2352e888e821aa0f4fe549bd5ea81d29c67a3222 stm32f7
|
||||
hw/mcu/st/cmsis_device_g0 https://github.com/STMicroelectronics/cmsis_device_g0.git f484fe852535f913a02ee79787eafa74dd7f9488 stm32g0
|
||||
hw/mcu/st/cmsis_device_g4 https://github.com/STMicroelectronics/cmsis_device_g4.git 7c39c32593b03764aaa57531588b8bf7cdd443a5 stm32g4
|
||||
hw/mcu/st/cmsis_device_h5 https://github.com/STMicroelectronics/cmsis_device_h5.git 5273b8f134ba65f5b8174c4141b711b5c0d295b2 stm32h5
|
||||
hw/mcu/st/cmsis_device_h7 https://github.com/STMicroelectronics/cmsis_device_h7.git 45b818cab6ee2806e3a27c80e330957223424392 stm32h7
|
||||
hw/mcu/st/cmsis_device_h7rs https://github.com/STMicroelectronics/cmsis_device_h7rs.git 57ea11f70ebf1850e1048989d665c9070f0bb863 stm32h7rs
|
||||
hw/mcu/st/cmsis_device_l0 https://github.com/STMicroelectronics/cmsis_device_l0.git 7b7ae8cd71437331e1d7824f157d00c7bb4a5044 stm32l0
|
||||
hw/mcu/st/cmsis_device_l1 https://github.com/STMicroelectronics/cmsis_device_l1.git a23ade4ccf14012085fedf862e33a536ab7ed8be stm32l1
|
||||
hw/mcu/st/cmsis_device_l4 https://github.com/STMicroelectronics/cmsis_device_l4.git a2530753e86dd326a75467d28feb92e2ba7d0df2 stm32l4
|
||||
hw/mcu/st/cmsis_device_l5 https://github.com/STMicroelectronics/cmsis_device_l5.git 7d9a51481f0e6c376e62c3c849e6caf652c66482 stm32l5
|
||||
hw/mcu/st/cmsis_device_n6 https://github.com/STMicroelectronics/cmsis-device-n6.git 7bcdc944fbf7cf5928d3c1d14054ca13261d33ec stm32n6
|
||||
hw/mcu/st/cmsis_device_u5 https://github.com/STMicroelectronics/cmsis_device_u5.git 6e67187dec98035893692ab2923914cb5f4e0117 stm32u5
|
||||
hw/mcu/st/cmsis_device_wb https://github.com/STMicroelectronics/cmsis_device_wb.git cda2cb9fc4a5232ab18efece0bb06b0b60910083 stm32wb
|
||||
hw/mcu/st/stm32-mfxstm32l152 https://github.com/STMicroelectronics/stm32-mfxstm32l152.git 7f4389efee9c6a655b55e5df3fceef5586b35f9b stm32h7
|
||||
hw/mcu/st/stm32-tcpp0203 https://github.com/STMicroelectronics/stm32-tcpp0203.git 9918655bff176ac3046ccf378b5c7bbbc6a38d15 stm32h7rs stm32n6
|
||||
hw/mcu/st/stm32c0xx_hal_driver https://github.com/STMicroelectronics/stm32c0xx_hal_driver.git c283b143bef6bdaacf64240ee6f15eb61dad6125 stm32c0
|
||||
hw/mcu/st/stm32f0xx_hal_driver https://github.com/STMicroelectronics/stm32f0xx_hal_driver.git 94399697cb5eeaf8511b81b7f50dc62f0a5a3f6c stm32f0
|
||||
hw/mcu/st/stm32f1xx_hal_driver https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git 18074e3e5ecad0b380a5cf5a9131fe4b5ed1b2b7 stm32f1
|
||||
hw/mcu/st/stm32f2xx_hal_driver https://github.com/STMicroelectronics/stm32f2xx_hal_driver.git ae7b47fe41cf75ccaf65cbf8ee8749b18ba0e0f3 stm32f2
|
||||
hw/mcu/st/stm32f3xx_hal_driver https://github.com/STMicroelectronics/stm32f3xx_hal_driver.git e098c8c8ce6f426bcee7db3a37c0932ea881eb0b stm32f3
|
||||
hw/mcu/st/stm32f4xx_hal_driver https://github.com/STMicroelectronics/stm32f4xx_hal_driver.git b6f0ed3829f3829eb358a2e7417d80bba1a42db7 stm32f4
|
||||
hw/mcu/st/stm32f7xx_hal_driver https://github.com/STMicroelectronics/stm32f7xx_hal_driver.git e1446fa12ffda80ea1016faf349e45b2047fff12 stm32f7
|
||||
hw/mcu/st/stm32g0xx_hal_driver https://github.com/STMicroelectronics/stm32g0xx_hal_driver.git a248a9e484d58943b46c68f6c49b4b276778bd59 stm32g0
|
||||
hw/mcu/st/stm32g4xx_hal_driver https://github.com/STMicroelectronics/stm32g4xx_hal_driver.git 10138a41749ea62d53ecab65b2bc2a950acc04d2 stm32g4
|
||||
hw/mcu/st/stm32h5xx_hal_driver https://github.com/STMicroelectronics/stm32h5xx_hal_driver.git 3c84eaa6000ab620be01afbcfba2735389afe09b stm32h5
|
||||
hw/mcu/st/stm32h7rsxx_hal_driver https://github.com/STMicroelectronics/stm32h7rsxx-hal-driver.git 9e83b95ae0f70faa067eddce2da617d180937f9b stm32h7rs
|
||||
hw/mcu/st/stm32h7xx_hal_driver https://github.com/STMicroelectronics/stm32h7xx_hal_driver.git dbfb749f229e1aa89e50b54229ca87766e180d2d stm32h7
|
||||
hw/mcu/st/stm32l0xx_hal_driver https://github.com/STMicroelectronics/stm32l0xx_hal_driver.git 65da4cd8a10ad859ec8d9cd71f3f6c50735bd473 stm32l0
|
||||
hw/mcu/st/stm32l1xx_hal_driver https://github.com/STMicroelectronics/stm32l1xx_hal_driver.git 54f0b7568ce2acb33d090c70c897ee32229c1d32 stm32l1
|
||||
hw/mcu/st/stm32l4xx_hal_driver https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git 3e039bbf62f54bbd834d578185521cff80596efe stm32l4
|
||||
hw/mcu/st/stm32l5xx_hal_driver https://github.com/STMicroelectronics/stm32l5xx_hal_driver.git 3340b9a597bcf75cc173345a90a74aa2a4a37510 stm32l5
|
||||
hw/mcu/st/stm32n6xx_hal_driver https://github.com/STMicroelectronics/stm32n6xx-hal-driver.git bc6c41f8f67d61b47af26695d0bf67762a000666 stm32n6
|
||||
hw/mcu/st/stm32u5xx_hal_driver https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git 2c5e2568fbdb1900a13ca3b2901fdd302cac3444 stm32u5
|
||||
hw/mcu/st/stm32wbaxx_hal_driver https://github.com/STMicroelectronics/stm32wbaxx_hal_driver.git 9442fbb71f855ff2e64fbf662b7726beba511a24 stm32wba
|
||||
hw/mcu/st/stm32wbxx_hal_driver https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git d60dd46996876506f1d2e9abd6b1cc110c8004cd stm32wb
|
||||
hw/mcu/ti https://github.com/hathach/ti_driver.git 143ed6cc20a7615d042b03b21e070197d473e6e5 msp430 msp432e4 tm4c
|
||||
hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.git 77c4095087e5ed2c548ec9058e655d0b8757663b ch32f20x
|
||||
hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x
|
||||
hw/mcu/wch/ch32v20x https://github.com/openwch/ch32v20x.git c4c38f507e258a4e69b059ccc2dc27dde33cea1b ch32v20x
|
||||
hw/mcu/wch/ch32v307 https://github.com/openwch/ch32v307.git 184f21b852cb95eed58e86e901837bc9fff68775 ch32v30x
|
||||
lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32n6 stm32u5 stm32wb sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c
|
||||
lib/CMSIS_6 https://github.com/ARM-software/CMSIS_6.git b0bbb0423b278ca632cfe1474eb227961d835fd2 ra
|
||||
lib/FreeRTOS-Kernel https://github.com/FreeRTOS/FreeRTOS-Kernel.git cc0e0707c0c748713485b870bb980852b210877f all
|
||||
lib/lwip https://github.com/lwip-tcpip/lwip.git 159e31b689577dbf69cf0683bbaffbd71fa5ee10 all
|
||||
lib/sct_neopixel https://github.com/gsteiert/sct_neopixel.git e73e04ca63495672d955f9268e003cffe168fcd8 lpc55
|
||||
tools/uf2 https://github.com/microsoft/uf2.git c594542b2faa01cc33a2b97c9fbebc38549df80a all
|
||||
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
|
||||
|
||||
@ -5,19 +5,19 @@ Getting Started
|
||||
Add TinyUSB to your project
|
||||
---------------------------
|
||||
|
||||
It is relatively simple to incorporate tinyusb to your project
|
||||
To incorporate tinyusb to your project
|
||||
|
||||
* Copy or ``git submodule`` this repo into your project in a subfolder. Let's say it is *your_project/tinyusb*
|
||||
* Add all the .c in the ``tinyusb/src`` folder to your project
|
||||
* Add *your_project/tinyusb/src* to your include path. Also make sure your current include path also contains the configuration file tusb_config.h.
|
||||
* Make sure all required macros are all defined properly in tusb_config.h (configure file in demo application is sufficient, but you need to add a few more such as ``CFG_TUSB_MCU``, ``CFG_TUSB_OS`` since they are passed by IDE/compiler to maintain a unique configure for all boards).
|
||||
* Copy or ``git submodule`` this repo into your project in a subfolder. Let's say it is ``your_project/tinyusb``
|
||||
* Add all the ``.c`` in the ``tinyusb/src`` folder to your project
|
||||
* Add ``your_project/tinyusb/src`` to your include path. Also make sure your current include path also contains the configuration file ``tusb_config.h``.
|
||||
* Make sure all required macros are all defined properly in ``tusb_config.h`` (configure file in demo application is sufficient, but you need to add a few more such as ``CFG_TUSB_MCU``, ``CFG_TUSB_OS`` since they are passed by make/cmake to maintain a unique configure for all boards).
|
||||
* If you use the device stack, make sure you have created/modified usb descriptors for your own need. Ultimately you need to implement all **tud descriptor** callbacks for the stack to work.
|
||||
* Add tusb_init(rhport, role) call to your reset initialization code.
|
||||
* Add ``tusb_init(rhport, role)`` call to your reset initialization code.
|
||||
* Call ``tusb_int_handler(rhport, in_isr)`` in your USB IRQ Handler
|
||||
* Implement all enabled classes's callbacks.
|
||||
* If you don't use any RTOSes at all, you need to continuously and/or periodically call tud_task()/tuh_task() function. All of the callbacks and functionality are handled and invoked within the call of that task runner.
|
||||
* If you don't use any RTOSes at all, you need to continuously and/or periodically call ``tud_task()``/``tuh_task()`` function. All of the callbacks and functionality are handled and invoked within the call of that task runner.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: c
|
||||
|
||||
int main(void) {
|
||||
tusb_rhport_init_t dev_init = {
|
||||
@ -50,9 +50,9 @@ It is relatively simple to incorporate tinyusb to your project
|
||||
Examples
|
||||
--------
|
||||
|
||||
For your convenience, TinyUSB contains a handful of examples for both host and device with/without RTOS to quickly test the functionality as well as demonstrate how API() should be used. Most examples will work on most of `the supported boards <boards.rst>`_. Firstly we need to ``git clone`` if not already
|
||||
For your convenience, TinyUSB contains a handful of examples for both host and device with/without RTOS to quickly test the functionality as well as demonstrate how API should be used. Most examples will work on most of `the supported boards <boards.rst>`_. Firstly we need to ``git clone`` if not already
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ git clone https://github.com/hathach/tinyusb tinyusb
|
||||
$ cd tinyusb
|
||||
@ -62,41 +62,53 @@ Some ports will also require a port-specific SDK (e.g. RP2040) or binary (e.g. S
|
||||
Dependencies
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The hardware code is located in ``hw/bsp`` folder, and is organized by family/boards. e.g raspberry_pi_pico is located in ``hw/bsp/rp2040/boards/raspberry_pi_pico`` where FAMILY=rp2040 and BOARD=raspberry_pi_pico. Before building, we firstly need to download dependencies such as: MCU low-level peripheral driver and external libraries e.g FreeRTOS (required by some examples). We can do that by either ways:
|
||||
The hardware code is located in ``hw/bsp`` folder, and is organized by family/boards. e.g raspberry_pi_pico is located in ``hw/bsp/rp2040/boards/raspberry_pi_pico`` where ``FAMILY=rp2040`` and ``BOARD=raspberry_pi_pico``. Before building, we firstly need to download dependencies such as: MCU low-level peripheral driver and external libraries e.g FreeRTOS (required by some examples). We can do that by either ways:
|
||||
|
||||
1. Run ``tools/get_deps.py {FAMILY}`` script to download all dependencies for a family as follow. Note: For TinyUSB developer to download all dependencies, use FAMILY=all.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ python tools/get_deps.py rp2040
|
||||
|
||||
2. Or run the ``get-deps`` target in one of the example folder as follow.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd examples/device/cdc_msc
|
||||
$ make BOARD=raspberry_pi_pico get-deps
|
||||
$ make BOARD=feather_nrf52840_express get-deps
|
||||
|
||||
You only need to do this once per family. Check out `complete list of dependencies and their designated path here <dependencies.rst>`_
|
||||
|
||||
Build
|
||||
^^^^^
|
||||
Build Examples
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
To build example, first change directory to an example folder.
|
||||
Examples support make and cmake build system for most MCUs, however some MCU families such as espressif or rp2040 only support cmake. First change directory to an example folder.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd examples/device/cdc_msc
|
||||
|
||||
Then compile with ``make BOARD={board_name} all`` , for example
|
||||
Then compile with make or cmake
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=raspberry_pi_pico all
|
||||
$ # make
|
||||
$ make BOARD=feather_nrf52840_express all
|
||||
|
||||
$ # cmake
|
||||
$ mkdir build && cd build
|
||||
$ cmake -DBOARD=raspberry_pi_pico ..
|
||||
$ make
|
||||
|
||||
To list all available targets with cmake
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cmake --build . --target help
|
||||
|
||||
Note: some examples especially those that uses Vendor class (e.g webUSB) may requires udev permission on Linux (and/or macOS) to access usb device. It depends on your OS distro, typically copy ``99-tinyusb.rules`` and reload your udev is good to go
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ cp examples/device/99-tinyusb.rules /etc/udev/rules.d/
|
||||
$ sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
@ -104,27 +116,31 @@ Note: some examples especially those that uses Vendor class (e.g webUSB) may req
|
||||
RootHub Port Selection
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a board has several ports, one port is chosen by default in the individual board.mk file. Use option ``PORT=x`` To choose another port. For example to select the HS port of a STM32F746Disco board, use:
|
||||
If a board has several ports, one port is chosen by default in the individual board.mk file. Use option ``RHPORT_DEVICE=x`` or ``RHPORT_HOST=x`` To choose another port. For example to select the HS port of a STM32F746Disco board, use:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=stm32f746disco PORT=1 all
|
||||
$ make BOARD=stm32f746disco RHPORT_DEVICE=1 all
|
||||
|
||||
$ cmake -DBOARD=stm32f746disco -DRHPORT_DEVICE=1 ..
|
||||
|
||||
Port Speed
|
||||
~~~~~~~~~~
|
||||
|
||||
A MCU can support multiple operational speed. By default, the example build system will use the fastest supported on the board. Use option ``SPEED=full/high`` e.g To force F723 operate at full instead of default high speed
|
||||
A MCU can support multiple operational speed. By default, the example build system will use the fastest supported on the board. Use option ``RHPORT_DEVICE_SPEED=OPT_MODE_FULL/HIGH_SPEED/`` or ``RHPORT_HOST_SPEED=OPT_MODE_FULL/HIGH_SPEED/`` e.g To force F723 operate at full instead of default high speed
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=stm32f746disco SPEED=full all
|
||||
$ make BOARD=stm32f746disco RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED all
|
||||
|
||||
$ cmake -DBOARD=stm32f746disco -DRHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED ..
|
||||
|
||||
Size Analysis
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
First install `linkermap tool <https://github.com/hathach/linkermap>`_ then ``linkermap`` target can be used to analyze code size. You may want to compile with ``NO_LTO=1`` since -flto merges code across .o files and make it difficult to analyze.
|
||||
First install `linkermap tool <https://github.com/hathach/linkermap>`_ then ``linkermap`` target can be used to analyze code size. You may want to compile with ``NO_LTO=1`` since ``-flto`` merges code across ``.o`` files and make it difficult to analyze.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express NO_LTO=1 all linkermap
|
||||
|
||||
@ -133,19 +149,23 @@ Debug
|
||||
|
||||
To compile for debugging add ``DEBUG=1``\ , for example
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express DEBUG=1 all
|
||||
|
||||
$ cmake -DBOARD=feather_nrf52840_express -DCMAKE_BUILD_TYPE=Debug ..
|
||||
|
||||
Log
|
||||
~~~
|
||||
|
||||
Should you have an issue running example and/or submitting an bug report. You could enable TinyUSB built-in debug logging with optional ``LOG=``. LOG=1 will only print out error message, LOG=2 print more information with on-going events. LOG=3 or higher is not used yet.
|
||||
Should you have an issue running example and/or submitting an bug report. You could enable TinyUSB built-in debug logging with optional ``LOG=``. ``LOG=1`` will only print out error message, ``LOG=2`` print more information with on-going events. ``LOG=3`` or higher is not used yet.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express LOG=2 all
|
||||
|
||||
$ cmake -DBOARD=feather_nrf52840_express -DLOG=2 ..
|
||||
|
||||
Logger
|
||||
~~~~~~
|
||||
|
||||
@ -164,74 +184,80 @@ By default log message is printed via on-board UART which is slow and take lots
|
||||
* Pros: should be compatible with more debugger that support SWO.
|
||||
* Software viewer should be provided along with your debugger driver.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=rtt all
|
||||
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=swo all
|
||||
|
||||
$ cmake -DBOARD=feather_nrf52840_express -DLOG=2 -DLOGGER=rtt ..
|
||||
$ cmake -DBOARD=feather_nrf52840_express -DLOG=2 -DLOGGER=swo ..
|
||||
|
||||
Flash
|
||||
^^^^^
|
||||
|
||||
``flash`` target will use the default on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary, please install those support software in advance. Some board use bootloader/DFU via serial which is required to pass to make command
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express flash
|
||||
$ make SERIAL=/dev/ttyACM0 BOARD=feather_nrf52840_express flash
|
||||
|
||||
Since jlink can be used with most of the boards, there is also ``flash-jlink`` target for your convenience.
|
||||
Since jlink/openocd can be used with most of the boards, there is also ``flash-jlink/openocd`` (make) and ``EXAMPLE-jlink/openocd`` target for your convenience. Note for stm32 board with stlink, you can use ``flash-stlink`` target as well.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express flash-jlink
|
||||
$ make BOARD=feather_nrf52840_express flash-openocd
|
||||
|
||||
$ cmake --build . --target cdc_msc-jlink
|
||||
$ cmake --build . --target cdc_msc-openocd
|
||||
|
||||
Some board use uf2 bootloader for drag & drop in to mass storage device, uf2 can be generated with ``uf2`` target
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: bash
|
||||
|
||||
$ make BOARD=feather_nrf52840_express all uf2
|
||||
|
||||
$ cmake --build . --target cdc_msc-uf2
|
||||
|
||||
IAR Support
|
||||
-----------
|
||||
^^^^^^^^^^^
|
||||
|
||||
Use project connection
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
IAR Project Connection files are provided to import TinyUSB stack into your project.
|
||||
|
||||
* A buldable project of your MCU need to be created in advance.
|
||||
|
||||
* A buildable project of your MCU need to be created in advance.
|
||||
|
||||
* Take example of STM32F0:
|
||||
|
||||
- You need `stm32l0xx.h`, `startup_stm32f0xx.s`, `system_stm32f0xx.c`.
|
||||
- You need ``stm32l0xx.h``, ``startup_stm32f0xx.s``, ``system_stm32f0xx.c``.
|
||||
|
||||
- `STM32L0xx_HAL_Driver` is only needed to run examples, TinyUSB stack itself doesn't rely on MCU's SDKs.
|
||||
- ``STM32L0xx_HAL_Driver`` is only needed to run examples, TinyUSB stack itself doesn't rely on MCU's SDKs.
|
||||
|
||||
* Open ``Tools -> Configure Custom Argument Variables`` (Switch to `Global` tab if you want to do it for all your projects)
|
||||
Click `New Group ...`, name it to `TUSB`, Click `Add Variable ...`, name it to `TUSB_DIR`, change it's value to the path of your TinyUSB stack,
|
||||
for example `C:\\tinyusb`
|
||||
* Open ``Tools -> Configure Custom Argument Variables`` (Switch to ``Global`` tab if you want to do it for all your projects)
|
||||
Click ``New Group ...``, name it to ``TUSB``, Click ``Add Variable ...``, name it to ``TUSB_DIR``, change it's value to the path of your TinyUSB stack,
|
||||
for example ``C:\\tinyusb``
|
||||
|
||||
Import stack only
|
||||
~~~~~~~~~~~~~~~~~
|
||||
**Import stack only**
|
||||
|
||||
1. Open ``Project -> Add project Connection ...``, click `OK`, choose `tinyusb\\tools\\iar_template.ipcf`.
|
||||
Open ``Project -> Add project Connection ...``, click ``OK``, choose ``tinyusb\\tools\\iar_template.ipcf``.
|
||||
|
||||
Run examples
|
||||
~~~~~~~~~~~~
|
||||
**Run examples**
|
||||
|
||||
1. (Python3 is needed) Run ``iar_gen.py`` to generate .ipcf files of examples:
|
||||
1. Run ``iar_gen.py`` to generate .ipcf files of examples:
|
||||
|
||||
.. code-block::
|
||||
|
||||
cd C:\tinyusb\tools
|
||||
python iar_gen.py
|
||||
> cd C:\tinyusb\tools
|
||||
> python iar_gen.py
|
||||
|
||||
2. Open `Project -> Add project Connection ...`, click `OK`, choose `tinyusb\\examples\\(.ipcf of example)`.
|
||||
For example `C:\\tinyusb\\examples\\device\\cdc_msc\\iar_cdc_msc.ipcf`
|
||||
2. Open ``Project -> Add project Connection ...``, click ``OK``, choose ``tinyusb\\examples\\(.ipcf of example)``.
|
||||
For example ``C:\\tinyusb\\examples\\device\\cdc_msc\\iar_cdc_msc.ipcf``
|
||||
|
||||
Native CMake support (9.50.1+)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Native CMake support
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With 9.50.1 release, IAR added experimental native CMake support (strangely not mentioned in public release note). Now it's possible to import CMakeLists.txt then build and debug as a normal project.
|
||||
|
||||
@ -239,5 +265,5 @@ Following these steps:
|
||||
|
||||
1. Add IAR compiler binary path to system ``PATH`` environment variable, such as ``C:\Program Files\IAR Systems\Embedded Workbench 9.2\arm\bin``.
|
||||
2. Create new project in IAR, in Tool chain dropdown menu, choose CMake for Arm then Import ``CMakeLists.txt`` from chosen example directory.
|
||||
3. Set up board option in ``Option - CMake/CMSIS-TOOLBOX - CMake``, for example :code:`-DBOARD=stm32f439nucleo -DTOOLCHAIN=iar`, **Uncheck 'Override tools in env'**.
|
||||
3. Set up board option in ``Option - CMake/CMSIS-TOOLBOX - CMake``, for example ``-DBOARD=stm32f439nucleo -DTOOLCHAIN=iar``, **Uncheck 'Override tools in env'**.
|
||||
4. (For debug only) Choose correct CPU model in ``Option - General Options - Target``, to profit register and memory view.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
if (TOOLCHAIN STREQUAL "gcc")
|
||||
set(TOOLCHAIN_COMMON_FLAGS
|
||||
-mthumb
|
||||
-mcpu=cortex-m0plus
|
||||
-mcpu=cortex-m0
|
||||
-mfloat-abi=soft
|
||||
)
|
||||
set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
|
||||
|
||||
30
examples/build_system/cmake/cpu/cortex-m4-nofpu.cmake
Normal file
30
examples/build_system/cmake/cpu/cortex-m4-nofpu.cmake
Normal file
@ -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 ()
|
||||
26
examples/build_system/cmake/cpu/cortex-m55.cmake
Normal file
26
examples/build_system/cmake/cpu/cortex-m55.cmake
Normal file
@ -0,0 +1,26 @@
|
||||
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
|
||||
)
|
||||
set(FREERTOS_PORT GCC_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
|
||||
|
||||
elseif (TOOLCHAIN STREQUAL "iar")
|
||||
set(TOOLCHAIN_COMMON_FLAGS
|
||||
--cpu cortex-m55
|
||||
--fpu VFPv5_D16
|
||||
)
|
||||
set(FREERTOS_PORT IAR_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
|
||||
|
||||
endif ()
|
||||
20
examples/build_system/make/cpu/cortex-m4-nofpu.mk
Normal file
20
examples/build_system/make/cpu/cortex-m4-nofpu.mk
Normal file
@ -0,0 +1,20 @@
|
||||
ifeq ($(TOOLCHAIN),gcc)
|
||||
CFLAGS += \
|
||||
-mthumb \
|
||||
-mcpu=cortex-m4 \
|
||||
-mfloat-abi=soft
|
||||
|
||||
else ifeq ($(TOOLCHAIN),clang)
|
||||
CFLAGS += \
|
||||
--target=arm-none-eabi \
|
||||
-mcpu=cortex-m4
|
||||
|
||||
else ifeq ($(TOOLCHAIN),iar)
|
||||
CFLAGS += --cpu cortex-m4 --fpu none
|
||||
ASFLAGS += --cpu cortex-m4 --fpu none
|
||||
|
||||
else
|
||||
$(error "TOOLCHAIN is not supported")
|
||||
endif
|
||||
|
||||
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM3
|
||||
@ -12,8 +12,8 @@ else ifeq ($(TOOLCHAIN),clang)
|
||||
-mfpu=fpv4-sp-d16 \
|
||||
|
||||
else ifeq ($(TOOLCHAIN),iar)
|
||||
CFLAGS += --cpu cortex-m4 --fpu VFPv4
|
||||
ASFLAGS += --cpu cortex-m4 --fpu VFPv4
|
||||
CFLAGS += --cpu cortex-m4 --fpu VFPv4-SP
|
||||
ASFLAGS += --cpu cortex-m4 --fpu VFPv4-SP
|
||||
|
||||
else
|
||||
$(error "TOOLCHAIN is not supported")
|
||||
|
||||
28
examples/build_system/make/cpu/cortex-m55.mk
Normal file
28
examples/build_system/make/cpu/cortex-m55.mk
Normal file
@ -0,0 +1,28 @@
|
||||
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 ifeq ($(TOOLCHAIN),iar)
|
||||
CFLAGS += \
|
||||
--cpu cortex-m55 \
|
||||
--fpu VFPv5_D16 \
|
||||
|
||||
ASFLAGS += \
|
||||
--cpu cortex-m55 \
|
||||
--fpu VFPv5_D16 \
|
||||
|
||||
else
|
||||
$(error "TOOLCHAIN is not supported")
|
||||
endif
|
||||
|
||||
FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM55_NTZ/non_secure
|
||||
@ -2,6 +2,9 @@
|
||||
# Common make definition for all examples
|
||||
# ---------------------------------------
|
||||
|
||||
# upper helper function
|
||||
to_upper = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$(subst -,_,$(1))))))))))))))))))))))))))))
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# Toolchain
|
||||
# Can be changed via TOOLCHAIN=gcc|iar or CC=arm-none-eabi-gcc|iccarm|clang
|
||||
@ -109,7 +112,7 @@ INC += \
|
||||
$(TOP)/$(FAMILY_PATH) \
|
||||
$(TOP)/src \
|
||||
|
||||
BOARD_UPPER = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$(subst -,_,$(BOARD))))))))))))))))))))))))))))
|
||||
BOARD_UPPER = $(call to_upper,$(BOARD))
|
||||
CFLAGS += -DBOARD_$(BOARD_UPPER)
|
||||
|
||||
ifdef CFLAGS_CLI
|
||||
@ -120,27 +123,24 @@ endif
|
||||
ifeq (${MAX3421_HOST},1)
|
||||
SRC_C += src/portable/analog/max3421/hcd_max3421.c
|
||||
CFLAGS += -DCFG_TUH_MAX3421=1
|
||||
CMAKE_DEFSYM += -DMAX3421_HOST=1
|
||||
endif
|
||||
|
||||
# Log level is mapped to TUSB DEBUG option
|
||||
ifneq ($(LOG),)
|
||||
CMAKE_DEFSYM += -DLOG=$(LOG)
|
||||
CFLAGS += -DCFG_TUSB_DEBUG=$(LOG)
|
||||
endif
|
||||
|
||||
# Logger: default is uart, can be set to rtt or swo
|
||||
ifneq ($(LOGGER),)
|
||||
CMAKE_DEFSYM += -DLOGGER=$(LOGGER)
|
||||
endif
|
||||
|
||||
ifeq ($(LOGGER),rtt)
|
||||
CFLAGS += -DLOGGER_RTT -DSEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
|
||||
RTT_SRC = lib/SEGGER_RTT
|
||||
INC += $(TOP)/$(RTT_SRC)/RTT
|
||||
SRC_C += $(RTT_SRC)/RTT/SEGGER_RTT.c
|
||||
else ifeq ($(LOGGER),swo)
|
||||
CFLAGS += -DLOGGER_RTT
|
||||
#CFLAGS += -DSEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
|
||||
INC += $(TOP)/$(lib/SEGGER_RTT)/RTT
|
||||
SRC_C += $(lib/SEGGER_RTT)/RTT/SEGGER_RTT.c
|
||||
endif
|
||||
ifeq ($(LOGGER),swo)
|
||||
CFLAGS += -DLOGGER_SWO
|
||||
else
|
||||
CFLAGS += -DLOGGER_UART
|
||||
endif
|
||||
|
||||
# CPU specific flags
|
||||
|
||||
@ -31,10 +31,10 @@
|
||||
* $ python3 plot_audio_samples.py
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
@ -43,14 +43,14 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
|
||||
#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 {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
@ -60,36 +60,29 @@ 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
|
||||
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
|
||||
uint32_t sampFreq;
|
||||
uint8_t clkValid;
|
||||
|
||||
// Range states
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
|
||||
#else
|
||||
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000];
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -106,47 +99,23 @@ int main(void)
|
||||
sampleFreqRng.subrange[0].bRes = 0;
|
||||
|
||||
// Generate dummy data
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
uint16_t * p_buff = i2s_dummy_buffer[0];
|
||||
uint16_t *p_buff = i2s_dummy_buffer;
|
||||
uint16_t dataVal = 0;
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) {
|
||||
// CH0 saw wave
|
||||
*p_buff++ = dataVal;
|
||||
// CH1 inverted saw wave
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
|
||||
dataVal+= 32;
|
||||
}
|
||||
p_buff = i2s_dummy_buffer[1];
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal;
|
||||
dataVal += 32;
|
||||
// CH3 square wave
|
||||
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
|
||||
*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);
|
||||
float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000);
|
||||
*p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000);
|
||||
}
|
||||
#else
|
||||
uint16_t * p_buff = i2s_dummy_buffer;
|
||||
uint16_t dataVal = 0;
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
// CH0 saw wave
|
||||
*p_buff++ = dataVal;
|
||||
// CH1 inverted saw wave
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
|
||||
dataVal+= 32;
|
||||
// CH3 square wave
|
||||
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
|
||||
// CH4 sinus wave
|
||||
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
|
||||
*p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // tinyusb device task
|
||||
while (1) {
|
||||
tud_task();// tinyusb device task
|
||||
led_blinking_task();
|
||||
audio_task();
|
||||
}
|
||||
@ -157,29 +126,25 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
@ -187,23 +152,15 @@ void tud_resume_cb(void)
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// Yet to be filled - e.g. read audio from I2S buffer.
|
||||
// Here we simulate a I2S receive callback every 1ms.
|
||||
// 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 = board_millis();
|
||||
if ( start_ms == curr_ms ) return; // not enough time
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
// Write I2S buffer into FIFO
|
||||
for (uint8_t cnt=0; cnt < 2; cnt++)
|
||||
{
|
||||
tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
|
||||
}
|
||||
#else
|
||||
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -211,8 +168,7 @@ void audio_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -224,14 +180,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -243,14 +200,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -265,40 +223,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
// If request is for our feature unit
|
||||
if ( entityID == 2 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Request uses format layout 1
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
|
||||
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
// Request uses format layout 2
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -306,16 +261,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
// return tud_control_xfer(rhport, p_request, &tmp, 1);
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -323,14 +279,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -340,12 +297,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// Input terminal (Microphone input)
|
||||
if (entityID == 1)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR:
|
||||
{
|
||||
if (entityID == 1) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_TE_CTRL_CONNECTOR: {
|
||||
// The terminal connector control only has a get request with only the CUR attribute.
|
||||
audio_desc_channel_cluster_t ret;
|
||||
|
||||
@ -356,9 +310,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
TU_LOG2(" Get terminal connector\r\n");
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
break;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
} break;
|
||||
|
||||
// Unknown/Unsupported control selector
|
||||
default:
|
||||
@ -368,10 +321,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Feature unit
|
||||
if (entityID == 2)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
@ -379,8 +330,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
@ -390,21 +340,21 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
// Copy values - only for testing - better is version below
|
||||
audio_control_range_2_n_t(1)
|
||||
ret;
|
||||
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
|
||||
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));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -414,14 +364,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == 4 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 4) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
// Buffered control transfer is needed for IN flow control to work
|
||||
@ -431,12 +378,12 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case AUDIO_CS_CTRL_CLK_VALID:
|
||||
// Only cur attribute exists for this request
|
||||
@ -451,59 +398,20 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
|
||||
return false; // Yet not implemented
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
|
||||
// In read world application data flow is driven by I2S clock,
|
||||
// both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used.
|
||||
// For example in your I2S receive callback:
|
||||
// void I2S_Rx_Callback(int channel, const void* data, uint16_t samples)
|
||||
// {
|
||||
// tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) n_bytes_copied;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) p_request;
|
||||
|
||||
return true;
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
|
||||
if (board_millis() - 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
|
||||
led_state = 1 - led_state;// toggle
|
||||
}
|
||||
|
||||
@ -115,26 +115,11 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
|
||||
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
#define CFG_TUD_AUDIO_ENABLE_ENCODING 1
|
||||
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
|
||||
|
||||
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1
|
||||
#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
#else
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -31,50 +31,50 @@
|
||||
* $ python3 plot_audio_samples.py
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#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/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#else
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.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)
|
||||
#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
|
||||
#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
|
||||
#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 {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
@ -82,13 +82,13 @@ enum {
|
||||
|
||||
// static task
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
StackType_t blinky_stack[BLINKY_STACK_SIZE];
|
||||
StackType_t blinky_stack[BLINKY_STACK_SIZE];
|
||||
StaticTask_t blinky_taskdef;
|
||||
|
||||
StackType_t usb_device_stack[USBD_STACK_SIZE];
|
||||
StackType_t usb_device_stack[USBD_STACK_SIZE];
|
||||
StaticTask_t usb_device_taskdef;
|
||||
|
||||
StackType_t audio_stack[AUDIO_STACK_SIZE];
|
||||
StackType_t audio_stack[AUDIO_STACK_SIZE];
|
||||
StaticTask_t audio_taskdef;
|
||||
#endif
|
||||
|
||||
@ -96,30 +96,24 @@ 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
|
||||
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
|
||||
uint32_t sampFreq;
|
||||
uint8_t clkValid;
|
||||
|
||||
// Range states
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
|
||||
#else
|
||||
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000];
|
||||
#endif
|
||||
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_task(void* param);
|
||||
void led_blinking_task(void *param);
|
||||
void usb_device_task(void *param);
|
||||
void audio_isr_task(void *param);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
// Init values
|
||||
@ -132,87 +126,62 @@ int main(void)
|
||||
sampleFreqRng.subrange[0].bRes = 0;
|
||||
|
||||
// Generate dummy data
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
uint16_t * p_buff = i2s_dummy_buffer[0];
|
||||
uint16_t *p_buff = i2s_dummy_buffer;
|
||||
uint16_t dataVal = 0;
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) {
|
||||
// CH0 saw wave
|
||||
*p_buff++ = dataVal;
|
||||
// CH1 inverted saw wave
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
|
||||
dataVal+= 32;
|
||||
}
|
||||
p_buff = i2s_dummy_buffer[1];
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal;
|
||||
dataVal += 32;
|
||||
// CH3 square wave
|
||||
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
|
||||
*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);
|
||||
float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000);
|
||||
*p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000);
|
||||
}
|
||||
#else
|
||||
uint16_t * p_buff = i2s_dummy_buffer;
|
||||
uint16_t dataVal = 0;
|
||||
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
|
||||
{
|
||||
// CH0 saw wave
|
||||
*p_buff++ = dataVal;
|
||||
// CH1 inverted saw wave
|
||||
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
|
||||
dataVal+= 32;
|
||||
// CH3 square wave
|
||||
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
|
||||
// CH4 sinus wave
|
||||
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
|
||||
*p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
// blinky task
|
||||
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
|
||||
|
||||
// Create a task for tinyusb device stack
|
||||
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
|
||||
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, usb_device_stack, &usb_device_taskdef);
|
||||
|
||||
// Create a task for audio
|
||||
xTaskCreateStatic(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES-1, audio_stack, &audio_taskdef);
|
||||
// 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_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 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
|
||||
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// only start scheduler for non-espressif mcu
|
||||
#ifndef ESP_PLATFORM
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
void app_main(void)
|
||||
{
|
||||
#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 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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -220,8 +189,7 @@ void usb_device_task(void* param)
|
||||
}
|
||||
|
||||
// RTOS forever loop
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
// tinyusb device task
|
||||
tud_task();
|
||||
}
|
||||
@ -232,29 +200,25 @@ void usb_device_task(void* param)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
@ -262,22 +226,14 @@ void tud_resume_cb(void)
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void* param)
|
||||
{
|
||||
// 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;
|
||||
// Yet to be filled - e.g. read audio from I2S buffer.
|
||||
// Here we simulate a I2S receive callback every 1ms.
|
||||
while (1) {
|
||||
vTaskDelay(1);
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
// Write I2S buffer into FIFO
|
||||
for (uint8_t cnt=0; cnt < 2; cnt++)
|
||||
{
|
||||
tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
|
||||
}
|
||||
#else
|
||||
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,8 +242,7 @@ void audio_task(void* param)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -299,14 +254,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -318,14 +274,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -340,40 +297,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
// If request is for our feature unit
|
||||
if ( entityID == 2 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Request uses format layout 1
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
|
||||
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
// Request uses format layout 2
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
volume[channelNum] = ((audio_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;
|
||||
}
|
||||
}
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -381,14 +334,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -396,14 +350,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -413,12 +368,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// Input terminal (Microphone input)
|
||||
if (entityID == 1)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR:
|
||||
{
|
||||
if (entityID == 1) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_TE_CTRL_CONNECTOR: {
|
||||
// The terminal connector control only has a get request with only the CUR attribute.
|
||||
audio_desc_channel_cluster_t ret;
|
||||
|
||||
@ -427,11 +379,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
ret.bmChannelConfig = 0;
|
||||
ret.iChannelNames = 0;
|
||||
|
||||
TU_LOG2(" Get terminal connector\r\n");
|
||||
TU_LOG1(" Get terminal connector\r\n");
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
break;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
} break;
|
||||
|
||||
// Unknown/Unsupported control selector
|
||||
default:
|
||||
@ -441,43 +392,39 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Feature unit
|
||||
if (entityID == 2)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
TU_LOG1(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
|
||||
// Copy values - only for testing - better is version below
|
||||
audio_control_range_2_n_t(1)
|
||||
ret;
|
||||
audio_control_range_2_n_t(1) ret;
|
||||
|
||||
ret.wNumSubRanges = 1;
|
||||
ret.subrange[0].bMin = -90; // -90 dB
|
||||
ret.subrange[0].bMax = 90; // +90 dB
|
||||
ret.subrange[0].bRes = 1; // 1 dB steps
|
||||
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));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -487,33 +434,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == 4 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 4) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
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 AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
TU_LOG1(" Get Sample Freq. range\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case AUDIO_CS_CTRL_CLK_VALID:
|
||||
// Only cur attribute exists for this request
|
||||
TU_LOG2(" Get Sample Freq. valid\r\n");
|
||||
TU_LOG1(" Get Sample Freq. valid\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
@ -523,62 +467,22 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
}
|
||||
|
||||
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
|
||||
return false; // Yet not implemented
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
|
||||
// In read world application data flow is driven by I2S clock,
|
||||
// both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used.
|
||||
// For example in your I2S receive callback:
|
||||
// void I2S_Rx_Callback(int channel, const void* data, uint16_t samples)
|
||||
// {
|
||||
// tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) n_bytes_copied;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) p_request;
|
||||
|
||||
return true;
|
||||
TU_LOG1(" Unsupported entity: %d\r\n", entityID);
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
///--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void* param) {
|
||||
void led_blinking_task(void *param) {
|
||||
(void) param;
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
while (1) {
|
||||
// Blink every interval ms
|
||||
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
led_state = 1 - led_state;// toggle
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
@ -121,26 +121,11 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
|
||||
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
#define CFG_TUD_AUDIO_ENABLE_ENCODING 1
|
||||
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
|
||||
|
||||
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1
|
||||
#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
#else
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -31,8 +31,8 @@
|
||||
* $ python3 plot_audio_samples.py
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
@ -47,7 +47,7 @@
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
@ -57,32 +57,30 @@ 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
|
||||
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
|
||||
uint32_t sampFreq;
|
||||
uint8_t clkValid;
|
||||
|
||||
// Range states
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
|
||||
// Audio test data
|
||||
uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2];
|
||||
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)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -98,9 +96,8 @@ int main(void)
|
||||
sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
|
||||
sampleFreqRng.subrange[0].bRes = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // tinyusb device task
|
||||
while (1) {
|
||||
tud_task();// tinyusb device task
|
||||
led_blinking_task();
|
||||
audio_task();
|
||||
}
|
||||
@ -111,40 +108,45 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
|
||||
// asm("nop");
|
||||
// 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 = board_millis();
|
||||
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));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -152,8 +154,7 @@ void audio_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -165,14 +166,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -184,14 +186,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -206,40 +209,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
// If request is for our feature unit
|
||||
if ( entityID == 2 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Request uses format layout 1
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
|
||||
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
// Request uses format layout 2
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -247,16 +247,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
// return tud_control_xfer(rhport, p_request, &tmp, 1);
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -264,14 +265,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -281,12 +283,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// Input terminal (Microphone input)
|
||||
if (entityID == 1)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR:
|
||||
{
|
||||
if (entityID == 1) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_TE_CTRL_CONNECTOR: {
|
||||
// The terminal connector control only has a get request with only the CUR attribute.
|
||||
audio_desc_channel_cluster_t ret;
|
||||
|
||||
@ -297,9 +296,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
TU_LOG2(" Get terminal connector\r\n");
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
break;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
} break;
|
||||
|
||||
// Unknown/Unsupported control selector
|
||||
default:
|
||||
@ -309,43 +307,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Feature unit
|
||||
if (entityID == 2)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
|
||||
// Copy values - only for testing - better is version below
|
||||
audio_control_range_2_n_t(1)
|
||||
ret;
|
||||
ret;
|
||||
|
||||
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
|
||||
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));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -355,33 +350,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == 4 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 4) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case AUDIO_CS_CTRL_CLK_VALID:
|
||||
// Only cur attribute exists for this request
|
||||
TU_LOG2(" Get Sample Freq. valid\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -391,39 +383,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
|
||||
return false; // Yet not implemented
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) n_bytes_copied;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++)
|
||||
{
|
||||
test_buffer_audio[cnt] = startVal++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
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;
|
||||
@ -434,15 +397,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
|
||||
if (board_millis() - 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
|
||||
led_state = 1 - led_state;// toggle
|
||||
}
|
||||
|
||||
@ -12,11 +12,11 @@ if __name__ == '__main__':
|
||||
# print(sd.query_devices())
|
||||
|
||||
fs = 48000 # Sample rate
|
||||
duration = 100e-3 # Duration of recording
|
||||
duration = 3 # Duration of recording
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
# MME is needed since there are more than one MicNode device APIs (at least in Windows)
|
||||
device = 'Microphone (MicNode) MME'
|
||||
device = 'Microphone (MicNode), Windows WASAPI'
|
||||
elif platform.system() == 'Darwin':
|
||||
device = 'MicNode'
|
||||
else:
|
||||
|
||||
@ -117,7 +117,7 @@ extern "C" {
|
||||
#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(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 ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
#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
|
||||
}
|
||||
|
||||
@ -14,3 +14,4 @@ mcu:VALENTYUSB_EPTRI
|
||||
mcu:RAXXX
|
||||
family:broadcom_32bit
|
||||
family:broadcom_64bit
|
||||
board:stm32l0538disco
|
||||
|
||||
@ -31,36 +31,37 @@
|
||||
* $ python3 plot_audio_samples.py
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#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/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#else
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.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)
|
||||
#define USBD_STACK_SIZE (4 * configMINIMAL_STACK_SIZE / 2) * (CFG_TUSB_DEBUG ? 2 : 1)
|
||||
#endif
|
||||
|
||||
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
@ -71,7 +72,7 @@
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
@ -82,34 +83,36 @@ enum {
|
||||
StackType_t blinky_stack[BLINKY_STACK_SIZE];
|
||||
StaticTask_t blinky_taskdef;
|
||||
|
||||
StackType_t usb_device_stack[USBD_STACK_SIZE];
|
||||
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
|
||||
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
|
||||
uint32_t sampFreq;
|
||||
uint8_t clkValid;
|
||||
|
||||
// Range states
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
|
||||
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
|
||||
|
||||
// Audio test data
|
||||
uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2];
|
||||
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_task(void);
|
||||
void led_blinking_task(void *param);
|
||||
void usb_device_task(void *param);
|
||||
void audio_isr_task(void *param);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
// Init values
|
||||
@ -126,21 +129,26 @@ int main(void)
|
||||
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);
|
||||
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
|
||||
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// only start scheduler for non-espressif mcu
|
||||
#ifndef ESP_PLATFORM
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void) {
|
||||
main();
|
||||
}
|
||||
@ -148,17 +156,15 @@ void app_main(void) {
|
||||
|
||||
// USB Device Driver task
|
||||
// This top level thread process all usb events and invoke callbacks
|
||||
void usb_device_task(void* param)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -166,8 +172,7 @@ void usb_device_task(void* param)
|
||||
}
|
||||
|
||||
// RTOS forever loop
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
// tinyusb device task
|
||||
tud_task();
|
||||
}
|
||||
@ -204,10 +209,18 @@ void tud_resume_cb(void) {
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
|
||||
// asm("nop");
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -215,8 +228,7 @@ void audio_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -228,14 +240,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -247,14 +260,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -269,40 +283,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
// If request is for our feature unit
|
||||
if ( entityID == 2 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Request uses format layout 1
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
|
||||
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
// Request uses format layout 2
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
volume[channelNum] = (uint16_t) ((audio_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;
|
||||
}
|
||||
}
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -310,14 +320,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -325,14 +336,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -342,12 +354,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// Input terminal (Microphone input)
|
||||
if (entityID == 1)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR:
|
||||
{
|
||||
if (entityID == 1) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_TE_CTRL_CONNECTOR: {
|
||||
// The terminal connector control only has a get request with only the CUR attribute.
|
||||
audio_desc_channel_cluster_t ret;
|
||||
|
||||
@ -356,11 +365,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
ret.bmChannelConfig = (audio_channel_config_t) 0;
|
||||
ret.iChannelNames = 0;
|
||||
|
||||
TU_LOG2(" Get terminal connector\r\n");
|
||||
TU_LOG1(" Get terminal connector\r\n");
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
break;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
} break;
|
||||
|
||||
// Unknown/Unsupported control selector
|
||||
default:
|
||||
@ -370,43 +378,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Feature unit
|
||||
if (entityID == 2)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 2) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
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 AUDIO_FU_CTRL_VOLUME:
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
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 AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
|
||||
// Copy values - only for testing - better is version below
|
||||
audio_control_range_2_n_t(1)
|
||||
ret;
|
||||
ret;
|
||||
|
||||
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
|
||||
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));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -416,33 +421,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == 4 )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == 4) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
TU_LOG1(" Get Sample Freq.\r\n");
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
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
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case AUDIO_CS_CTRL_CLK_VALID:
|
||||
// Only cur attribute exists for this request
|
||||
TU_LOG2(" Get Sample Freq. valid\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
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:
|
||||
@ -451,40 +453,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
}
|
||||
|
||||
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
|
||||
return false; // Yet not implemented
|
||||
TU_LOG1(" Unsupported entity: %d\r\n", entityID);
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) n_bytes_copied;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++)
|
||||
{
|
||||
test_buffer_audio[cnt] = startVal++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
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;
|
||||
@ -495,17 +468,15 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void* param) {
|
||||
void led_blinking_task(void *param) {
|
||||
(void) param;
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
while (1) {
|
||||
// Blink every interval ms
|
||||
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
led_state = 1 - led_state;// toggle
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,11 +11,11 @@ if __name__ == '__main__':
|
||||
# print(sd.query_devices())
|
||||
|
||||
fs = 48000 # Sample rate
|
||||
duration = 1000e-3 # Duration of recording
|
||||
duration = 3 # Duration of recording
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
# MME is needed since there are more than one MicNode device APIs (at least in Windows)
|
||||
device = 'Microphone (MicNode) MME'
|
||||
device = 'Microphone (MicNode), Windows WASAPI'
|
||||
elif platform.system() == 'Darwin':
|
||||
device = 'MicNode'
|
||||
else:
|
||||
|
||||
@ -59,7 +59,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
@ -123,7 +123,7 @@ extern "C" {
|
||||
#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(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 ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
#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
|
||||
}
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
* $ python3 plot_audio_samples.py
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
@ -49,7 +49,7 @@
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
@ -59,8 +59,8 @@ 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
|
||||
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 bytesPerSample;
|
||||
uint8_t clkValid;
|
||||
@ -68,39 +68,36 @@ uint8_t clkValid;
|
||||
// Range states
|
||||
// List of supported sample rates
|
||||
static const uint32_t sampleRatesList[] =
|
||||
{
|
||||
32000, 48000, 96000
|
||||
};
|
||||
{
|
||||
32000, 48000, 96000};
|
||||
|
||||
#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList)
|
||||
#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList)
|
||||
|
||||
// Bytes per format of every Alt settings
|
||||
static const uint8_t bytesPerSampleAltList[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] =
|
||||
{
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX,
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX,
|
||||
{
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX,
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX,
|
||||
};
|
||||
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
|
||||
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
|
||||
|
||||
|
||||
// Audio test data
|
||||
CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
|
||||
CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[(TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
|
||||
uint16_t startVal = 0;
|
||||
|
||||
void led_blinking_task(void);
|
||||
void audio_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -111,9 +108,8 @@ int main(void)
|
||||
sampFreq = sampleRatesList[0];
|
||||
clkValid = 1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // tinyusb device task
|
||||
while (1) {
|
||||
tud_task();// tinyusb device task
|
||||
led_blinking_task();
|
||||
audio_task();
|
||||
}
|
||||
@ -127,29 +123,25 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
@ -157,10 +149,29 @@ void tud_resume_cb(void)
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
|
||||
// asm("nop");
|
||||
// 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 = board_millis();
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
// 16bit
|
||||
if (bytesPerSample == 2) {
|
||||
uint16_t *pData_16 = (uint16_t *) ((void *) test_buffer_audio);
|
||||
for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) {
|
||||
pData_16[cnt] = startVal++;
|
||||
}
|
||||
}
|
||||
// 24bit in 32bit slot
|
||||
else if (bytesPerSample == 4) {
|
||||
uint32_t *pData_32 = (uint32_t *) ((void *) test_buffer_audio);
|
||||
for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) {
|
||||
pData_32[cnt] = (uint32_t) startVal++ << 16U;
|
||||
}
|
||||
}
|
||||
tud_audio_write((uint8_t *) test_buffer_audio, (uint16_t) (sampFreq / 1000 * bytesPerSample));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -168,23 +179,20 @@ void audio_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when set interface is called, typically on start/stop streaming or format change
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
//uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
// Clear buffer when streaming format is changed
|
||||
if(alt != 0)
|
||||
{
|
||||
bytesPerSample = bytesPerSampleAltList[alt-1];
|
||||
if (alt != 0) {
|
||||
bytesPerSample = bytesPerSampleAltList[alt - 1];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -196,14 +204,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
@ -215,14 +224,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -237,49 +247,45 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
// If request is for our feature unit
|
||||
if ( entityID == UAC2_ENTITY_FEATURE_UNIT )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Request uses format layout 1
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
|
||||
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
// Request uses format layout 2
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
|
||||
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == UAC2_ENTITY_CLOCK )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == UAC2_ENTITY_CLOCK) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t));
|
||||
|
||||
sampFreq = (uint32_t)((audio_control_cur_4_t *)pBuff)->bCur;
|
||||
sampFreq = (uint32_t) ((audio_control_cur_4_t *) pBuff)->bCur;
|
||||
|
||||
TU_LOG2("Clock set current freq: %" PRIu32 "\r\n", sampFreq);
|
||||
|
||||
return true;
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -288,12 +294,11 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -301,16 +306,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t ep = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) ep;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) ep;
|
||||
|
||||
// return tud_control_xfer(rhport, p_request, &tmp, 1);
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -318,14 +324,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t itf = TU_U16_LOW(p_request->wIndex);
|
||||
|
||||
(void) channelNum; (void) ctrlSel; (void) itf;
|
||||
(void) channelNum;
|
||||
(void) ctrlSel;
|
||||
(void) itf;
|
||||
|
||||
return false; // Yet not implemented
|
||||
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)
|
||||
{
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
// Page 91 in UAC2 specification
|
||||
@ -335,12 +342,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// Input terminal (Microphone input)
|
||||
if (entityID == UAC2_ENTITY_INPUT_TERMINAL)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR:
|
||||
{
|
||||
if (entityID == UAC2_ENTITY_INPUT_TERMINAL) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_TE_CTRL_CONNECTOR: {
|
||||
// The terminal connector control only has a get request with only the CUR attribute.
|
||||
audio_desc_channel_cluster_t ret;
|
||||
|
||||
@ -351,9 +355,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
TU_LOG2(" Get terminal connector\r\n");
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
break;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
} break;
|
||||
|
||||
// Unknown/Unsupported control selector
|
||||
default:
|
||||
@ -363,43 +366,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Feature unit
|
||||
if (entityID == UAC2_ENTITY_FEATURE_UNIT)
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
|
||||
|
||||
// Copy values - only for testing - better is version below
|
||||
audio_control_range_2_n_t(1)
|
||||
ret;
|
||||
ret;
|
||||
|
||||
ret.wNumSubRanges = 1;
|
||||
ret.subrange[0].bMin = -90; // -90 dB
|
||||
ret.subrange[0].bMax = 30; // +30 dB
|
||||
ret.subrange[0].bRes = 1; // 1 dB steps
|
||||
ret.subrange[0].bMin = -90;// -90 dB
|
||||
ret.subrange[0].bMax = 30; // +30 dB
|
||||
ret.subrange[0].bRes = 1; // 1 dB steps
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -409,46 +409,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
// Clock Source unit
|
||||
if ( entityID == UAC2_ENTITY_CLOCK )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
if (entityID == UAC2_ENTITY_CLOCK) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
{
|
||||
case AUDIO_CS_REQ_RANGE: {
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
audio_control_range_4_n_t(N_sampleRates) rangef =
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_sampleRates)
|
||||
};
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_sampleRates)};
|
||||
TU_LOG1("Clock get %d freq ranges\r\n", N_sampleRates);
|
||||
for(uint8_t i = 0; i < N_sampleRates; i++)
|
||||
{
|
||||
rangef.subrange[i].bMin = (int32_t)sampleRatesList[i];
|
||||
rangef.subrange[i].bMax = (int32_t)sampleRatesList[i];
|
||||
rangef.subrange[i].bRes = 0;
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
|
||||
for (uint8_t i = 0; i < N_sampleRates; i++) {
|
||||
rangef.subrange[i].bMin = (int32_t) sampleRatesList[i];
|
||||
rangef.subrange[i].bMax = (int32_t) sampleRatesList[i];
|
||||
rangef.subrange[i].bRes = 0;
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
|
||||
}
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
|
||||
}
|
||||
// Unknown/Unsupported control
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case AUDIO_CS_CTRL_CLK_VALID:
|
||||
// Only cur attribute exists for this request
|
||||
TU_LOG2(" Get Sample Freq. valid\r\n");
|
||||
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
@ -458,53 +452,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
||||
}
|
||||
|
||||
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
|
||||
return false; // Yet not implemented
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
tud_audio_write((uint8_t *)test_buffer_audio, (uint16_t)(sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000) * bytesPerSample));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) n_bytes_copied;
|
||||
(void) itf;
|
||||
(void) ep_in;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
// 16bit
|
||||
if(bytesPerSample == 2)
|
||||
{
|
||||
uint16_t* pData_16 = (uint16_t*)((void*)test_buffer_audio);
|
||||
for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++)
|
||||
{
|
||||
pData_16[cnt] = startVal++;
|
||||
}
|
||||
}
|
||||
// 24bit in 32bit slot
|
||||
else if(bytesPerSample == 4)
|
||||
{
|
||||
uint32_t* pData_32 = (uint32_t*)((void*)test_buffer_audio);
|
||||
for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++)
|
||||
{
|
||||
pData_32[cnt] = (uint32_t)startVal++ << 16U;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
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;
|
||||
@ -515,15 +466,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
|
||||
if (board_millis() - 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
|
||||
led_state = 1 - led_state;// toggle
|
||||
}
|
||||
|
||||
@ -133,7 +133,8 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example write FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -49,25 +49,34 @@ int main(void) {
|
||||
while (1) {
|
||||
uint32_t interval_ms = board_button_read() ? BLINK_PRESSED : BLINK_UNPRESSED;
|
||||
|
||||
int ch = board_getchar();
|
||||
if (ch > 0) {
|
||||
board_putchar(ch);
|
||||
#ifndef LOGGER_UART
|
||||
board_uart_write(&ch, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Blink and print every interval ms
|
||||
if (!(board_millis() - start_ms < interval_ms)) {
|
||||
board_uart_write(HELLO_STR, strlen(HELLO_STR));
|
||||
|
||||
start_ms = board_millis();
|
||||
|
||||
if (ch < 0) {
|
||||
// skip if echoing
|
||||
printf(HELLO_STR);
|
||||
|
||||
#ifndef LOGGER_UART
|
||||
board_uart_write(HELLO_STR, strlen(HELLO_STR));
|
||||
#endif
|
||||
}
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
}
|
||||
|
||||
// echo
|
||||
uint8_t ch;
|
||||
if (board_uart_read(&ch, 1) > 0) {
|
||||
board_uart_write(&ch, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void) {
|
||||
main();
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
|
||||
@ -98,27 +98,33 @@ void tud_umount_cb(void) {
|
||||
blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USB CDC
|
||||
//--------------------------------------------------------------------+
|
||||
static void cdc_task(void) {
|
||||
uint8_t itf;
|
||||
|
||||
for (itf = 0; itf < CFG_TUD_CDC; itf++) {
|
||||
for (uint8_t itf = 0; itf < CFG_TUD_CDC; itf++) {
|
||||
// connected() check for DTR bit
|
||||
// Most but not all terminal client set this when making connection
|
||||
// if ( tud_cdc_n_connected(itf) )
|
||||
{
|
||||
if (tud_cdc_n_available(itf)) {
|
||||
uint8_t buf[64];
|
||||
|
||||
uint32_t count = tud_cdc_n_read(itf, buf, sizeof(buf));
|
||||
|
||||
// echo back to both serial ports
|
||||
echo_serial_port(0, buf, count);
|
||||
echo_serial_port(1, buf, count);
|
||||
}
|
||||
|
||||
// Press on-board button to send Uart status notification
|
||||
static uint32_t btn_prev = 0;
|
||||
static cdc_notify_uart_state_t uart_state = { .value = 0 };
|
||||
const uint32_t btn = board_button_read();
|
||||
if (!btn_prev && btn) {
|
||||
uart_state.dsr ^= 1;
|
||||
tud_cdc_notify_uart_state(&uart_state);
|
||||
}
|
||||
btn_prev = btn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +97,8 @@
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
@ -42,8 +42,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
tusb_desc_device_t const desc_device = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
@ -68,16 +67,14 @@ tusb_desc_device_t const desc_device =
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
uint8_t const *tud_descriptor_device_cb(void) {
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
ITF_NUM_CDC_0 = 0,
|
||||
ITF_NUM_CDC_0_DATA,
|
||||
ITF_NUM_CDC_1,
|
||||
@ -130,36 +127,32 @@ enum
|
||||
#define EPNUM_CDC_1_IN 0x84
|
||||
#endif
|
||||
|
||||
uint8_t const desc_fs_configuration[] =
|
||||
{
|
||||
uint8_t const desc_fs_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),
|
||||
|
||||
// 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 16, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64),
|
||||
|
||||
// 2nd CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 16, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64),
|
||||
};
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
|
||||
|
||||
uint8_t const desc_hs_configuration[] =
|
||||
{
|
||||
uint8_t const desc_hs_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),
|
||||
|
||||
// 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 16, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512),
|
||||
|
||||
// 2nd CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 512),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 16, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 512),
|
||||
};
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
{
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
@ -177,34 +170,31 @@ tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb(void)
|
||||
{
|
||||
return (uint8_t const*) &desc_device_qualifier;
|
||||
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
|
||||
return (uint8_t const *) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
|
||||
(void) index;// for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration;
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration;
|
||||
}
|
||||
|
||||
#endif // highspeed
|
||||
#endif// highspeed
|
||||
|
||||
// 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)
|
||||
{
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
#else
|
||||
return desc_fs_configuration;
|
||||
#endif
|
||||
@ -223,8 +213,7 @@ enum {
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
char const *string_desc_arr[] = {
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
@ -254,14 +243,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
// 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;
|
||||
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;
|
||||
if ( chr_count > max_count ) { chr_count = max_count; }
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
@ -272,6 +261,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -119,6 +119,16 @@ void cdc_task(void) {
|
||||
tud_cdc_write(buf, count);
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
|
||||
// Press on-board button to send Uart status notification
|
||||
static uint32_t btn_prev = 0;
|
||||
static cdc_notify_uart_state_t uart_state = { .value = 0 };
|
||||
const uint32_t btn = board_button_read();
|
||||
if (!btn_prev && btn) {
|
||||
uart_state.dsr ^= 1;
|
||||
tud_cdc_notify_uart_state(&uart_state);
|
||||
}
|
||||
btn_prev = btn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,17 +40,15 @@ static bool ejected = false;
|
||||
If you find any bugs or get any questions, feel free to file an\r\n\
|
||||
issue at github.com/hathach/tinyusb"
|
||||
|
||||
enum
|
||||
{
|
||||
DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount
|
||||
enum {
|
||||
DISK_BLOCK_NUM = 16,// 8KB is the smallest size that windows allow to mount
|
||||
DISK_BLOCK_SIZE = 512
|
||||
};
|
||||
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
const
|
||||
#endif
|
||||
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
|
||||
{
|
||||
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
|
||||
//------------- Block0: Boot Sector -------------//
|
||||
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM;
|
||||
// sector_per_cluster = 1; reserved_sectors = 1;
|
||||
@ -59,85 +57,85 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
|
||||
// drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
|
||||
// filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
|
||||
// FAT magic code at offset 510-511
|
||||
{
|
||||
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
|
||||
0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
|
||||
'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
|
||||
{
|
||||
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
|
||||
0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U',
|
||||
'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
|
||||
|
||||
// Zero up to 2 last bytes of FAT magic code
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// Zero up to 2 last bytes of FAT magic code
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
|
||||
},
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA},
|
||||
|
||||
//------------- Block1: FAT12 Table -------------//
|
||||
{
|
||||
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
|
||||
{
|
||||
0xF8, 0xFF, 0xFF, 0xFF, 0x0F// // first 2 entries must be F8FF, third entry is cluster end of readme file
|
||||
},
|
||||
|
||||
//------------- Block2: Root Directory -------------//
|
||||
{
|
||||
// first entry is volume label
|
||||
'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// second entry is readme file
|
||||
'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
|
||||
0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
|
||||
sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
|
||||
{
|
||||
// first entry is volume label
|
||||
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// second entry is readme file
|
||||
'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D,
|
||||
0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
|
||||
sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00// readme's files size (4 Bytes)
|
||||
},
|
||||
|
||||
//------------- Block3: Readme Content -------------//
|
||||
README_CONTENTS
|
||||
};
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
|
||||
// Some inquiry_resp's fields are already filled with default values, application can update them
|
||||
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
|
||||
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
|
||||
(void) bufsize;
|
||||
const char vid[] = "TinyUSB";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id , vid, strlen(vid));
|
||||
memcpy(product_id , pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
|
||||
memcpy(inquiry_resp->product_id, pid, strlen(pid));
|
||||
memcpy(inquiry_resp->product_rev, rev, strlen(rev));
|
||||
|
||||
return sizeof(scsi_inquiry_resp_t); // 36 bytes
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
{
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
// RAM disk is ready until ejected
|
||||
@ -152,29 +150,24 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
|
||||
{
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
|
||||
(void) lun;
|
||||
|
||||
*block_count = DISK_BLOCK_NUM;
|
||||
*block_size = DISK_BLOCK_SIZE;
|
||||
*block_size = DISK_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
{
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
|
||||
if ( load_eject )
|
||||
{
|
||||
if (start)
|
||||
{
|
||||
if (load_eject) {
|
||||
if (start) {
|
||||
// load disk storage
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// unload disk storage
|
||||
ejected = true;
|
||||
}
|
||||
@ -185,52 +178,51 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
|
||||
// out of ramdisk
|
||||
if ( lba >= DISK_BLOCK_NUM ) {
|
||||
if (lba >= DISK_BLOCK_NUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for overflow of offset + bufsize
|
||||
if ( offset + bufsize > DISK_BLOCK_SIZE ) {
|
||||
if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t const* addr = msc_disk[lba] + offset;
|
||||
uint8_t const *addr = msc_disk[lba] + offset;
|
||||
memcpy(buffer, addr, bufsize);
|
||||
|
||||
return (int32_t) bufsize;
|
||||
}
|
||||
|
||||
bool tud_msc_is_writable_cb (uint8_t lun)
|
||||
{
|
||||
bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
return false;
|
||||
#else
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
|
||||
{
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
|
||||
// out of ramdisk
|
||||
if ( lba >= DISK_BLOCK_NUM ) return -1;
|
||||
if (lba >= DISK_BLOCK_NUM) return -1;
|
||||
|
||||
#ifndef CFG_EXAMPLE_MSC_READONLY
|
||||
uint8_t* addr = msc_disk[lba] + offset;
|
||||
#ifndef CFG_EXAMPLE_MSC_READONLY
|
||||
uint8_t *addr = msc_disk[lba] + offset;
|
||||
memcpy(addr, buffer, bufsize);
|
||||
#else
|
||||
(void) lba; (void) offset; (void) buffer;
|
||||
#endif
|
||||
#else
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
(void) buffer;
|
||||
#endif
|
||||
|
||||
return (int32_t) bufsize;
|
||||
}
|
||||
@ -238,42 +230,18 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
|
||||
{
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
|
||||
void const* response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
switch (scsi_cmd[0])
|
||||
{
|
||||
switch (scsi_cmd[0]) {
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if ( resplen > bufsize ) resplen = bufsize;
|
||||
|
||||
if ( response && (resplen > 0) )
|
||||
{
|
||||
if(in_xfer)
|
||||
{
|
||||
memcpy(buffer, response, (size_t) resplen);
|
||||
}else
|
||||
{
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return (int32_t) resplen;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
@ -97,6 +97,8 @@
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
@ -52,7 +52,6 @@ tusb_desc_device_t const desc_device = {
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
@ -131,7 +130,7 @@ uint8_t const desc_fs_configuration[] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
|
||||
@ -146,7 +145,7 @@ uint8_t const desc_hs_configuration[] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
|
||||
@ -197,7 +196,6 @@ uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
|
||||
|
||||
#endif // highspeed
|
||||
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
@ -256,14 +254,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
// 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;
|
||||
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;
|
||||
if ( chr_count > max_count ) { chr_count = max_count; }
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
@ -274,6 +272,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#else
|
||||
// Increase stack size when debug log is enabled
|
||||
@ -72,7 +72,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
static void usb_device_task(void *param);
|
||||
void led_blinking_task(void* param);
|
||||
void cdc_task(void *params);
|
||||
|
||||
extern void msc_disk_init(void);
|
||||
//--------------------------------------------------------------------+
|
||||
// Main
|
||||
//--------------------------------------------------------------------+
|
||||
@ -91,15 +91,15 @@ int main(void) {
|
||||
xTaskCreate(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
|
||||
#endif
|
||||
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#ifndef ESP_PLATFORM
|
||||
// only start scheduler for non-espressif mcu
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void) {
|
||||
main();
|
||||
}
|
||||
@ -123,6 +123,7 @@ static void usb_device_task(void *param) {
|
||||
board_init_after_tusb();
|
||||
}
|
||||
|
||||
msc_disk_init();
|
||||
// RTOS forever loop
|
||||
while (1) {
|
||||
// put this thread to waiting state until there is new events
|
||||
@ -188,6 +189,16 @@ void cdc_task(void *params) {
|
||||
}
|
||||
|
||||
tud_cdc_write_flush();
|
||||
|
||||
// Press on-board button to send Uart status notification
|
||||
static uint32_t btn_prev = 0;
|
||||
static cdc_notify_uart_state_t uart_state = { .value = 0 };
|
||||
const uint32_t btn = board_button_read();
|
||||
if (!btn_prev && btn) {
|
||||
uart_state.dsr ^= 1;
|
||||
tud_cdc_notify_uart_state(&uart_state);
|
||||
}
|
||||
btn_prev = btn;
|
||||
}
|
||||
|
||||
// For ESP32-Sx this delay is essential to allow idle how to run and reset watchdog
|
||||
@ -218,13 +229,11 @@ void tud_cdc_rx_cb(uint8_t itf) {
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void* param) {
|
||||
(void) param;
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
static bool led_state = false;
|
||||
|
||||
while (1) {
|
||||
// Blink every interval ms
|
||||
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
|
||||
@ -28,6 +28,37 @@
|
||||
|
||||
#if CFG_TUD_MSC
|
||||
|
||||
// Use async IO in example or not
|
||||
#define CFG_EXAMPLE_MSC_ASYNC_IO 1
|
||||
|
||||
// Simulate read/write operation delay
|
||||
#define CFG_EXAMPLE_MSC_IO_DELAY_MS 0
|
||||
|
||||
#if CFG_EXAMPLE_MSC_ASYNC_IO
|
||||
#define IO_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
|
||||
typedef struct {
|
||||
uint8_t lun;
|
||||
bool is_read;
|
||||
uint32_t lba;
|
||||
uint32_t offset;
|
||||
void* buffer;
|
||||
uint32_t bufsize;
|
||||
} io_ops_t;
|
||||
|
||||
QueueHandle_t io_queue;
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
uint8_t io_queue_buf[sizeof(io_ops_t)];
|
||||
StaticQueue_t io_queue_static;
|
||||
StackType_t io_stack[IO_STACK_SIZE];
|
||||
StaticTask_t io_taskdef;
|
||||
#endif
|
||||
|
||||
static void io_task(void *params);
|
||||
#endif
|
||||
|
||||
void msc_disk_init(void);
|
||||
|
||||
// whether host does safe-eject
|
||||
static bool ejected = false;
|
||||
|
||||
@ -40,8 +71,7 @@ static bool ejected = false;
|
||||
If you find any bugs or get any questions, feel free to file an\r\n\
|
||||
issue at github.com/hathach/tinyusb"
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount
|
||||
DISK_BLOCK_SIZE = 512
|
||||
};
|
||||
@ -119,25 +149,65 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
|
||||
README_CONTENTS
|
||||
};
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
(void) lun;
|
||||
#if CFG_EXAMPLE_MSC_ASYNC_IO
|
||||
void msc_disk_init(void) {
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
io_queue = xQueueCreateStatic(1, sizeof(io_ops_t), io_queue_buf, &io_queue_static);
|
||||
xTaskCreateStatic(io_task, "io", IO_STACK_SIZE, NULL, 2, io_stack, &io_taskdef);
|
||||
#else
|
||||
io_queue = xQueueCreate(1, sizeof(io_ops_t));
|
||||
xTaskCreate(io_task, "io", IO_STACK_SIZE, NULL, 2, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void io_task(void *params) {
|
||||
(void) params;
|
||||
io_ops_t io_ops;
|
||||
while (1) {
|
||||
if (xQueueReceive(io_queue, &io_ops, portMAX_DELAY)) {
|
||||
uint8_t* addr = (uint8_t*) (uintptr_t) (msc_disk[io_ops.lba] + io_ops.offset);
|
||||
int32_t nbytes = io_ops.bufsize;
|
||||
if (io_ops.is_read) {
|
||||
memcpy(io_ops.buffer, addr, io_ops.bufsize);
|
||||
} else {
|
||||
#ifndef CFG_EXAMPLE_MSC_READONLY
|
||||
memcpy((uint8_t*) addr, io_ops.buffer, io_ops.bufsize);
|
||||
#else
|
||||
nbytes = -1; // failed to write
|
||||
#endif
|
||||
}
|
||||
|
||||
tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS);
|
||||
tud_msc_async_io_done(nbytes, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void msc_disk_init() {}
|
||||
#endif
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
|
||||
// Some inquiry_resp's fields are already filled with default values, application can update them
|
||||
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
|
||||
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t* inquiry_resp, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) bufsize;
|
||||
const char vid[] = "TinyUSB";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id , vid, strlen(vid));
|
||||
memcpy(product_id , pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
|
||||
memcpy(inquiry_resp->product_id, pid, strlen(pid));
|
||||
memcpy(inquiry_resp->product_rev, rev, strlen(rev));
|
||||
|
||||
return sizeof(scsi_inquiry_resp_t); // 36 bytes
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
{
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
// RAM disk is ready until ejected
|
||||
@ -152,10 +222,8 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
|
||||
{
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
|
||||
(void) lun;
|
||||
|
||||
*block_count = DISK_BLOCK_NUM;
|
||||
*block_size = DISK_BLOCK_SIZE;
|
||||
}
|
||||
@ -163,18 +231,14 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
{
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
|
||||
if ( load_eject )
|
||||
{
|
||||
if (start)
|
||||
{
|
||||
if (load_eject) {
|
||||
if (start) {
|
||||
// load disk storage
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// unload disk storage
|
||||
ejected = true;
|
||||
}
|
||||
@ -185,90 +249,107 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
|
||||
// out of ramdisk
|
||||
if ( lba >= DISK_BLOCK_NUM ) {
|
||||
return -1;
|
||||
if (lba >= DISK_BLOCK_NUM) {
|
||||
return TUD_MSC_RET_ERROR;
|
||||
}
|
||||
|
||||
// Check for overflow of offset + bufsize
|
||||
if ( offset + bufsize > DISK_BLOCK_SIZE ) {
|
||||
return -1;
|
||||
if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) {
|
||||
return TUD_MSC_RET_ERROR;
|
||||
}
|
||||
|
||||
uint8_t const* addr = msc_disk[lba] + offset;
|
||||
memcpy(buffer, addr, bufsize);
|
||||
#if CFG_EXAMPLE_MSC_ASYNC_IO
|
||||
io_ops_t io_ops = {.is_read = true, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize};
|
||||
|
||||
return (int32_t) bufsize;
|
||||
// Send IO operation to IO task
|
||||
TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS);
|
||||
|
||||
return TUD_MSC_RET_ASYNC;
|
||||
#else
|
||||
uint8_t const *addr = msc_disk[lba] + offset;
|
||||
memcpy(buffer, addr, bufsize);
|
||||
return bufsize;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool tud_msc_is_writable_cb (uint8_t lun)
|
||||
{
|
||||
bool tud_msc_is_writable_cb (uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
return false;
|
||||
#else
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
// out of ramdisk
|
||||
if ( lba >= DISK_BLOCK_NUM ) return -1;
|
||||
if (lba >= DISK_BLOCK_NUM) {
|
||||
return TUD_MSC_RET_ERROR;
|
||||
}
|
||||
|
||||
#ifndef CFG_EXAMPLE_MSC_READONLY
|
||||
uint8_t* addr = msc_disk[lba] + offset;
|
||||
// Check for overflow of offset + bufsize
|
||||
if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) {
|
||||
return TUD_MSC_RET_ERROR;
|
||||
}
|
||||
|
||||
#ifdef CFG_EXAMPLE_MSC_READONLY
|
||||
(void) lun;
|
||||
(void) buffer;
|
||||
return bufsize;
|
||||
#endif
|
||||
|
||||
#if CFG_EXAMPLE_MSC_ASYNC_IO
|
||||
io_ops_t io_ops = {.is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize};
|
||||
|
||||
// Send IO operation to IO task
|
||||
TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS);
|
||||
|
||||
return TUD_MSC_RET_ASYNC;
|
||||
#else
|
||||
uint8_t *addr = msc_disk[lba] + offset;
|
||||
memcpy(addr, buffer, bufsize);
|
||||
#else
|
||||
(void) lba; (void) offset; (void) buffer;
|
||||
#endif
|
||||
tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS);
|
||||
|
||||
return (int32_t) bufsize;
|
||||
return bufsize;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
|
||||
{
|
||||
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
|
||||
void const* response = NULL;
|
||||
void const *response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
switch (scsi_cmd[0])
|
||||
{
|
||||
switch (scsi_cmd[0]) {
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if ( resplen > bufsize ) resplen = bufsize;
|
||||
if (resplen > bufsize) { resplen = bufsize; }
|
||||
|
||||
if ( response && (resplen > 0) )
|
||||
{
|
||||
if(in_xfer)
|
||||
{
|
||||
if (response && (resplen > 0)) {
|
||||
if (in_xfer) {
|
||||
memcpy(buffer, response, (size_t) resplen);
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
@ -104,6 +104,8 @@
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
@ -52,7 +52,6 @@ tusb_desc_device_t const desc_device = {
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
@ -131,7 +130,7 @@ uint8_t const desc_fs_configuration[] =
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
|
||||
@ -147,7 +146,7 @@ uint8_t const desc_hs_configuration[] =
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
|
||||
@ -176,16 +175,14 @@ tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb(void)
|
||||
{
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb(void) {
|
||||
return (uint8_t const*) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
{
|
||||
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
@ -204,13 +201,12 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
// 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)
|
||||
{
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
#else
|
||||
return desc_fs_configuration;
|
||||
#endif
|
||||
@ -229,8 +225,7 @@ enum {
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
char const *string_desc_arr[] = {
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
@ -261,14 +256,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
// 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;
|
||||
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;
|
||||
if ( chr_count > max_count ) { chr_count = max_count; }
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
@ -279,6 +274,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
8
examples/device/cdc_uac2/skip.txt
Normal file
8
examples/device/cdc_uac2/skip.txt
Normal file
@ -0,0 +1,8 @@
|
||||
mcu:LPC11UXX
|
||||
mcu:LPC13XX
|
||||
mcu:NUC121
|
||||
mcu:SAMD11
|
||||
mcu:SAME5X
|
||||
mcu:SAMG
|
||||
board:stm32l052dap52
|
||||
family:espressif
|
||||
@ -39,6 +39,7 @@ extern uint32_t blink_interval_ms;
|
||||
#endif
|
||||
|
||||
void led_blinking_task(void);
|
||||
void audio_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
@ -62,7 +63,7 @@ int main(void)
|
||||
{
|
||||
tud_task(); // TinyUSB device task
|
||||
led_blinking_task();
|
||||
|
||||
audio_task();
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_RP2040)
|
||||
// printf("Hello, world!\r\n");
|
||||
#endif
|
||||
|
||||
@ -145,8 +145,8 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
||||
@ -154,8 +154,8 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2
|
||||
@ -164,8 +164,11 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
// CDC Endpoint transfer buffer size, more is faster
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -49,17 +49,36 @@ uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
|
||||
// Buffer for microphone data
|
||||
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
|
||||
// Buffer for speaker data
|
||||
int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4];
|
||||
// Speaker data size received in the last frame
|
||||
int spk_data_size;
|
||||
uint16_t spk_data_size;
|
||||
// Resolution per format
|
||||
const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX};
|
||||
// Current resolution, update on format change
|
||||
uint8_t current_resolution;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
|
||||
// In a real application, this would be replaced with actual I2S send/receive callback.
|
||||
void audio_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
uint32_t curr_ms = board_millis();
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
// When new data arrived, copy data from speaker buffer, to microphone buffer
|
||||
// and send it over
|
||||
// Only support speaker & headphone both have the same resolution
|
||||
// If one is 16bit another is 24bit be care of LOUD noise !
|
||||
spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
|
||||
if (spk_data_size) {
|
||||
tud_audio_write((uint8_t *) spk_buf, spk_data_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
|
||||
{
|
||||
@ -238,7 +257,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
|
||||
@ -265,8 +284,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
}
|
||||
|
||||
// Clear buffer when streaming format is changed
|
||||
spk_data_size = 0;
|
||||
if(alt != 0)
|
||||
{
|
||||
current_resolution = resolutions_per_format[alt-1];
|
||||
@ -275,30 +292,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)func_id;
|
||||
(void)ep_out;
|
||||
(void)cur_alt_setting;
|
||||
|
||||
spk_data_size = tud_audio_read(spk_buf, n_bytes_received);
|
||||
tud_audio_write(spk_buf, n_bytes_received);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)itf;
|
||||
(void)ep_in;
|
||||
(void)cur_alt_setting;
|
||||
|
||||
// This callback could be used to fill microphone data separately
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@ -116,7 +116,7 @@ uint8_t const * tud_descriptor_device_cb(void)
|
||||
#define EPNUM_CDC_IN 0x84
|
||||
#endif
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
uint8_t const desc_fs_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),
|
||||
@ -128,13 +128,78 @@ uint8_t const desc_configuration[] =
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64)
|
||||
};
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
|
||||
|
||||
// high speed configuration
|
||||
uint8_t const desc_hs_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_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80),
|
||||
|
||||
// CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512)
|
||||
};
|
||||
|
||||
// other speed configuration
|
||||
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0100,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0x00
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
|
||||
return (uint8_t const *) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
// Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG
|
||||
memcpy(desc_other_speed_config,
|
||||
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
|
||||
CONFIG_TOTAL_LEN);
|
||||
|
||||
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
|
||||
|
||||
return desc_other_speed_config;
|
||||
}
|
||||
|
||||
#endif // highspeed
|
||||
|
||||
// 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;
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
#else
|
||||
return desc_fs_configuration;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@ -116,19 +116,21 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
|
||||
README_CONTENTS
|
||||
};
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
|
||||
// Some inquiry_resp's fields are already filled with default values, application can update them
|
||||
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
|
||||
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
|
||||
(void) bufsize;
|
||||
const char vid[] = "TinyUSB";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id , vid, strlen(vid));
|
||||
memcpy(product_id , pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
|
||||
memcpy(inquiry_resp->product_id, pid, strlen(pid));
|
||||
memcpy(inquiry_resp->product_rev, rev, strlen(rev));
|
||||
|
||||
return sizeof(scsi_inquiry_resp_t); // 36 bytes
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
// ESP-IDF need "freertos/" prefix in include path.
|
||||
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -112,17 +112,16 @@ int main(void)
|
||||
|
||||
xTimerStart(blinky_tm, 0);
|
||||
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// only start scheduler for non-espressif mcu
|
||||
#ifndef ESP_PLATFORM
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
void app_main(void)
|
||||
{
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void) {
|
||||
main();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define USBD_STACK_SIZE 4096
|
||||
#else
|
||||
// Increase stack size when debug log is enabled
|
||||
@ -95,15 +95,15 @@ int main(void) {
|
||||
xTaskCreate(midi_task, "midi", MIDI_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
|
||||
#endif
|
||||
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#ifndef ESP_PLATFORM
|
||||
// only start scheduler for non-espressif mcu
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void) {
|
||||
main();
|
||||
}
|
||||
@ -225,13 +225,11 @@ void midi_task(void* param) {
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void* param) {
|
||||
(void) param;
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
while (1) {
|
||||
// Blink every interval ms
|
||||
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
|
||||
@ -55,8 +55,7 @@ If you find any bugs or get any questions, feel free to file an\r\n\
|
||||
issue at github.com/hathach/tinyusb"
|
||||
|
||||
|
||||
MSC_CONST uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
|
||||
{
|
||||
MSC_CONST uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
|
||||
//------------- Block0: Boot Sector -------------//
|
||||
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM;
|
||||
// sector_per_cluster = 1; reserved_sectors = 1;
|
||||
@ -208,18 +207,21 @@ uint8_t tud_msc_get_maxlun_cb(void) {
|
||||
return 2; // dual LUN
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
(void) lun; // use same ID for both LUNs
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
|
||||
// Some inquiry_resp's fields are already filled with default values, application can update them
|
||||
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
|
||||
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) bufsize;
|
||||
const char vid[] = "TinyUSB";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id , vid, strlen(vid));
|
||||
memcpy(product_id , pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
|
||||
memcpy(inquiry_resp->product_id, pid, strlen(pid));
|
||||
memcpy(inquiry_resp->product_rev, rev, strlen(rev));
|
||||
|
||||
return sizeof(scsi_inquiry_resp_t); // 36 bytes
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
@ -283,9 +285,11 @@ bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
// out of ramdisk
|
||||
if (lba >= DISK_BLOCK_NUM) return -1;
|
||||
if (lba >= DISK_BLOCK_NUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY)
|
||||
#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY)
|
||||
(void) lun;
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
@ -302,11 +306,8 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks (MUST not be handled here)
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
|
||||
void const* response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
|
||||
switch (scsi_cmd[0]) {
|
||||
default:
|
||||
@ -316,19 +317,6 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, u
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize) resplen = bufsize;
|
||||
|
||||
if (response && (resplen > 0)) {
|
||||
if (in_xfer) {
|
||||
memcpy(buffer, response, (size_t) resplen);
|
||||
} else {
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return resplen;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
DEPS_SUBMODULES += lib/lwip
|
||||
|
||||
include ../../build_system/make/make.mk
|
||||
|
||||
# suppress warning caused by lwip
|
||||
|
||||
@ -10,9 +10,12 @@ mcu:SAMD11
|
||||
mcu:STM32L0
|
||||
mcu:STM32F0
|
||||
mcu:KINETIS_KL
|
||||
mcu:STM32H7RS
|
||||
mcu:STM32N6
|
||||
family:broadcom_64bit
|
||||
family:broadcom_32bit
|
||||
family:espressif
|
||||
board:at_start_f425
|
||||
board:curiosity_nano
|
||||
board:frdm_kl25z
|
||||
# lpc55 has weird error 'ncm_interface' causes a section type conflict with 'ntb_parameters'
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
|
||||
|
||||
#define LWIP_SINGLE_NETIF 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
|
||||
#define PBUF_POOL_SIZE 4
|
||||
|
||||
|
||||
@ -31,6 +31,12 @@ this appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS pi
|
||||
RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts
|
||||
|
||||
The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server.
|
||||
|
||||
Link State Control:
|
||||
- Press the user button to toggle the network link state (UP/DOWN)
|
||||
- This simulates "ethernet cable unplugged/plugged" events
|
||||
- The host OS will see the network interface as disconnected/connected accordingly
|
||||
- Use this to test network error handling and recovery in host applications
|
||||
*/
|
||||
/*
|
||||
Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers,
|
||||
@ -63,9 +69,6 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00
|
||||
/* lwip context */
|
||||
static struct netif netif_data;
|
||||
|
||||
/* shared between tud_network_recv_cb() and service_traffic() */
|
||||
static struct pbuf *received_frame;
|
||||
|
||||
/* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */
|
||||
/* ideally speaking, this should be generated from the hardware's unique ID (if available) */
|
||||
/* it is suggested that the first byte is 0x02 to indicate a link-local address */
|
||||
@ -137,6 +140,12 @@ static err_t netif_init_cb(struct netif *netif) {
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* notifies the USB host about the link state change. */
|
||||
static void usbnet_netif_link_callback(struct netif *netif) {
|
||||
bool link_up = netif_is_link_up(netif);
|
||||
tud_network_link_state(BOARD_TUD_RHPORT, link_up);
|
||||
}
|
||||
|
||||
static void init_lwip(void) {
|
||||
struct netif *netif = &netif_data;
|
||||
|
||||
@ -147,11 +156,19 @@ static void init_lwip(void) {
|
||||
memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address));
|
||||
netif->hwaddr[5] ^= 0x01;
|
||||
|
||||
netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input);
|
||||
netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ethernet_input);
|
||||
#if LWIP_IPV6
|
||||
netif_create_ip6_linklocal_address(netif, 1);
|
||||
#endif
|
||||
netif_set_default(netif);
|
||||
|
||||
#if LWIP_NETIF_LINK_CALLBACK
|
||||
// Set the link callback to notify USB host about link state changes
|
||||
netif_set_link_callback(netif, usbnet_netif_link_callback);
|
||||
netif_set_link_up(netif);
|
||||
#else
|
||||
tud_network_link_state(BOARD_TUD_RHPORT, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* handle any DNS requests from dns-server */
|
||||
@ -164,20 +181,29 @@ bool dns_query_proc(const char *name, ip4_addr_t *addr) {
|
||||
}
|
||||
|
||||
bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
|
||||
/* this shouldn't happen, but if we get another packet before
|
||||
parsing the previous, we must signal our inability to accept it */
|
||||
if (received_frame) return false;
|
||||
struct netif *netif = &netif_data;
|
||||
|
||||
if (size) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL);
|
||||
|
||||
if (p) {
|
||||
/* pbuf_alloc() has already initialized struct; all we need to do is copy the data */
|
||||
memcpy(p->payload, src, size);
|
||||
|
||||
/* store away the pointer for service_traffic() to later handle */
|
||||
received_frame = p;
|
||||
if (p == NULL) {
|
||||
printf("ERROR: Failed to allocate pbuf of size %d\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy buf to pbuf */
|
||||
pbuf_take(p, src, size);
|
||||
|
||||
// Surrender ownership of our pbuf unless there was an error
|
||||
// Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
|
||||
// or steal it from whatever took ownership of it with undefined consequences.
|
||||
// See: https://savannah.nongnu.org/patch/index.php?10121
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
printf("ERROR: netif input failed\n");
|
||||
pbuf_free(p);
|
||||
}
|
||||
// Signal tinyusb that the current frame has been processed.
|
||||
tud_network_recv_renew();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -191,29 +217,26 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
|
||||
return pbuf_copy_partial(p, dst, p->tot_len, 0);
|
||||
}
|
||||
|
||||
static void service_traffic(void) {
|
||||
/* handle any packet received by tud_network_recv_cb() */
|
||||
if (received_frame) {
|
||||
// Surrender ownership of our pbuf unless there was an error
|
||||
// Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
|
||||
// or steal it from whatever took ownership of it with undefined consequences.
|
||||
// See: https://savannah.nongnu.org/patch/index.php?10121
|
||||
if (ethernet_input(received_frame, &netif_data)!=ERR_OK) {
|
||||
pbuf_free(received_frame);
|
||||
static void handle_link_state_switch(void) {
|
||||
/* Check for button press to toggle link state */
|
||||
static bool last_link_state = true;
|
||||
static bool last_button_state = false;
|
||||
bool current_button_state = board_button_read();
|
||||
|
||||
if (current_button_state && !last_button_state) {
|
||||
/* Button pressed - toggle link state */
|
||||
last_link_state = !last_link_state;
|
||||
if (last_link_state) {
|
||||
printf("Link state: UP\n");
|
||||
netif_set_link_up(&netif_data);
|
||||
} else {
|
||||
printf("Link state: DOWN\n");
|
||||
netif_set_link_down(&netif_data);
|
||||
}
|
||||
received_frame = NULL;
|
||||
tud_network_recv_renew();
|
||||
/* LWIP callback will notify USB host about the change */
|
||||
}
|
||||
last_button_state = current_button_state;
|
||||
|
||||
sys_check_timeouts();
|
||||
}
|
||||
|
||||
void tud_network_init_cb(void) {
|
||||
/* if the network is re-initializing and we have a leftover packet, we must do a cleanup */
|
||||
if (received_frame) {
|
||||
pbuf_free(received_frame);
|
||||
received_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
@ -243,15 +266,23 @@ int main(void) {
|
||||
lwiperf_start_tcp_server_default(NULL, NULL);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_NCM
|
||||
printf("USB NCM network interface initialized\n");
|
||||
#elif CFG_TUD_ECM_RNDIS
|
||||
printf("USB RNDIS/ECM network interface initialized\n");
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
tud_task();
|
||||
service_traffic();
|
||||
sys_check_timeouts(); // service lwip
|
||||
handle_link_state_switch();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lwip has provision for using a mutex, when applicable */
|
||||
/* This implementation is for single-threaded use only */
|
||||
sys_prot_t sys_arch_protect(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -85,6 +85,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Use different configurations to test all net devices (also due to resource limitations)
|
||||
#ifndef USE_ECM
|
||||
#if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54)
|
||||
#define USE_ECM 1
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML21, OPT_MCU_SAML22)
|
||||
@ -97,6 +98,7 @@ extern "C" {
|
||||
#define USE_ECM 0
|
||||
#define INCLUDE_IPERF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
|
||||
|
||||
@ -37,9 +37,9 @@
|
||||
// List of supported sample rates
|
||||
const uint32_t sample_rates[] = {44100, 48000};
|
||||
|
||||
uint32_t current_sample_rate = 44100;
|
||||
uint32_t current_sample_rate = 44100;
|
||||
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
|
||||
/* Blink pattern
|
||||
* - 25 ms : streaming data
|
||||
@ -47,16 +47,14 @@ uint32_t current_sample_rate = 44100;
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
BLINK_STREAMING = 25,
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
VOLUME_CTRL_0_DB = 0,
|
||||
VOLUME_CTRL_10_DB = 2560,
|
||||
VOLUME_CTRL_20_DB = 5120,
|
||||
@ -75,8 +73,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
|
||||
// Audio controls
|
||||
// Current states
|
||||
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
|
||||
|
||||
// Buffer for microphone data
|
||||
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
|
||||
@ -95,15 +93,13 @@ void audio_task(void);
|
||||
void audio_control_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -112,9 +108,8 @@ int main(void)
|
||||
|
||||
TU_LOG1("Headset running\r\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // TinyUSB device task
|
||||
while (1) {
|
||||
tud_task();// TinyUSB device task
|
||||
audio_task();
|
||||
audio_control_task();
|
||||
led_blinking_task();
|
||||
@ -126,70 +121,57 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
|
||||
{
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
|
||||
{
|
||||
if (request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
|
||||
if (request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
|
||||
}
|
||||
else if (request->bRequest == AUDIO_CS_REQ_RANGE)
|
||||
{
|
||||
audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
|
||||
} else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
|
||||
audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
|
||||
};
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
|
||||
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
|
||||
for(uint8_t i = 0; i < N_SAMPLE_RATES; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) {
|
||||
rangef.subrange[i].bMin = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bMax = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bRes = 0;
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
|
||||
}
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
|
||||
}
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
|
||||
request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_1_t cur_valid = { .bCur = 1 };
|
||||
} else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
|
||||
request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_1_t cur_valid = {.bCur = 1};
|
||||
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
|
||||
}
|
||||
TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
@ -197,25 +179,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
|
||||
}
|
||||
|
||||
// Helper for clock set requests
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
|
||||
{
|
||||
(void)rhport;
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
|
||||
|
||||
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
|
||||
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
return false;
|
||||
@ -223,33 +201,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
|
||||
}
|
||||
|
||||
// Helper for feature unit get requests
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
|
||||
{
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
|
||||
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
|
||||
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
|
||||
{
|
||||
if (request->bRequest == AUDIO_CS_REQ_RANGE)
|
||||
{
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
|
||||
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
|
||||
if (request->bRequest == AUDIO_CS_REQ_RANGE) {
|
||||
audio_control_range_2_n_t(1) range_vol = {
|
||||
.wNumSubRanges = tu_htole16(1),
|
||||
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
|
||||
};
|
||||
.wNumSubRanges = tu_htole16(1),
|
||||
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
|
||||
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
|
||||
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
|
||||
}
|
||||
else if (request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
|
||||
} else if (request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
|
||||
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
|
||||
}
|
||||
}
|
||||
TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
@ -259,35 +229,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
|
||||
}
|
||||
|
||||
// Helper for feature unit set requests
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
|
||||
{
|
||||
(void)rhport;
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
|
||||
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur;
|
||||
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
|
||||
{
|
||||
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
|
||||
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
return false;
|
||||
@ -299,16 +263,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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)
|
||||
{
|
||||
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_get_request(rhport, request);
|
||||
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_get_request(rhport, request);
|
||||
else
|
||||
{
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
}
|
||||
@ -316,9 +278,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
}
|
||||
|
||||
// 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 *buf)
|
||||
{
|
||||
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
|
||||
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_set_request(rhport, request, buf);
|
||||
@ -330,58 +291,35 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0)
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0)
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
|
||||
// Clear buffer when streaming format is changed
|
||||
spk_data_size = 0;
|
||||
if(alt != 0)
|
||||
{
|
||||
current_resolution = resolutions_per_format[alt-1];
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)func_id;
|
||||
(void)ep_out;
|
||||
(void)cur_alt_setting;
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
spk_data_size = tud_audio_read(spk_buf, n_bytes_received);
|
||||
return true;
|
||||
}
|
||||
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)itf;
|
||||
(void)ep_in;
|
||||
(void)cur_alt_setting;
|
||||
// Clear buffer when streaming format is changed
|
||||
spk_data_size = 0;
|
||||
if (alt != 0) {
|
||||
current_resolution = resolutions_per_format[alt - 1];
|
||||
}
|
||||
|
||||
// This callback could be used to fill microphone data separately
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -389,49 +327,48 @@ bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, u
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
|
||||
// In a real application, this would be replaced with actual I2S send/receive callback.
|
||||
void audio_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
uint32_t curr_ms = board_millis();
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
// When new data arrived, copy data from speaker buffer, to microphone buffer
|
||||
// and send it over
|
||||
// Only support speaker & headphone both have the same resolution
|
||||
// If one is 16bit another is 24bit be care of LOUD noise !
|
||||
if (spk_data_size)
|
||||
{
|
||||
if (current_resolution == 16)
|
||||
{
|
||||
int16_t *src = (int16_t*)spk_buf;
|
||||
int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2;
|
||||
int16_t *dst = (int16_t*)mic_buf;
|
||||
while (src < limit)
|
||||
{
|
||||
spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
|
||||
if (spk_data_size) {
|
||||
if (current_resolution == 16) {
|
||||
int16_t *src = (int16_t *) spk_buf;
|
||||
int16_t *limit = (int16_t *) spk_buf + spk_data_size / 2;
|
||||
int16_t *dst = (int16_t *) mic_buf;
|
||||
while (src < limit) {
|
||||
// Combine two channels into one
|
||||
int32_t left = *src++;
|
||||
int32_t right = *src++;
|
||||
*dst++ = (int16_t) ((left >> 1) + (right >> 1));
|
||||
}
|
||||
tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
spk_data_size = 0;
|
||||
}
|
||||
else if (current_resolution == 24)
|
||||
{
|
||||
} else if (current_resolution == 24) {
|
||||
int32_t *src = spk_buf;
|
||||
int32_t *limit = spk_buf + spk_data_size / 4;
|
||||
int32_t *dst = mic_buf;
|
||||
while (src < limit)
|
||||
{
|
||||
while (src < limit) {
|
||||
// Combine two channels into one
|
||||
int32_t left = *src++;
|
||||
int32_t right = *src++;
|
||||
*dst++ = (int32_t) ((uint32_t) ((left >> 1) + (right >> 1)) & 0xffffff00ul);
|
||||
}
|
||||
tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
spk_data_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_control_task(void)
|
||||
{
|
||||
void audio_control_task(void) {
|
||||
// Press on-board button to control volume
|
||||
// Open host volume control, volume should switch between 10% and 100%
|
||||
|
||||
@ -440,27 +377,25 @@ void audio_control_task(void)
|
||||
static uint32_t start_ms = 0;
|
||||
static uint32_t btn_prev = 0;
|
||||
|
||||
if ( board_millis() - start_ms < interval_ms) return; // not enough time
|
||||
if (board_millis() - start_ms < interval_ms) return;// not enough time
|
||||
start_ms += interval_ms;
|
||||
|
||||
uint32_t btn = board_button_read();
|
||||
|
||||
if (!btn_prev && btn)
|
||||
{
|
||||
if (!btn_prev && btn) {
|
||||
// Adjust volume between 0dB (100%) and -30dB (10%)
|
||||
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++)
|
||||
{
|
||||
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
|
||||
volume[i] = volume[i] == 0 ? -VOLUME_CTRL_30_DB : 0;
|
||||
}
|
||||
|
||||
// 6.1 Interrupt Data Message
|
||||
const audio_interrupt_data_t data = {
|
||||
.bInfo = 0, // Class-specific interrupt, originated from an interface
|
||||
.bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
|
||||
.wValue_cn_or_mcn = 0, // CH0: master volume
|
||||
.wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
|
||||
.wIndex_ep_or_int = 0, // From the interface itself
|
||||
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT, // From feature unit
|
||||
.bInfo = 0, // Class-specific interrupt, originated from an interface
|
||||
.bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
|
||||
.wValue_cn_or_mcn = 0, // CH0: master volume
|
||||
.wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
|
||||
.wIndex_ep_or_int = 0, // From the interface itself
|
||||
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit
|
||||
};
|
||||
|
||||
tud_audio_int_write(&data);
|
||||
@ -472,8 +407,7 @@ void audio_control_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
|
||||
@ -146,8 +146,8 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*4
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
||||
@ -155,8 +155,8 @@ extern "C" {
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT)*2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
|
||||
|
||||
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "common_types.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "common_types.h"
|
||||
|
||||
#ifdef CFG_QUIRK_OS_GUESSING
|
||||
#include "quirk_os_guessing.h"
|
||||
#include "quirk_os_guessing.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -41,14 +41,14 @@
|
||||
|
||||
// List of supported sample rates
|
||||
#if defined(__RX__)
|
||||
const uint32_t sample_rates[] = {44100, 48000};
|
||||
const uint32_t sample_rates[] = {44100, 48000};
|
||||
#else
|
||||
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
|
||||
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
|
||||
#endif
|
||||
|
||||
uint32_t current_sample_rate = 44100;
|
||||
uint32_t current_sample_rate = 44100;
|
||||
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
|
||||
/* Blink pattern
|
||||
* - 25 ms : streaming data
|
||||
@ -56,16 +56,14 @@ uint32_t current_sample_rate = 44100;
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
BLINK_STREAMING = 25,
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
VOLUME_CTRL_0_DB = 0,
|
||||
VOLUME_CTRL_10_DB = 2560,
|
||||
VOLUME_CTRL_20_DB = 5120,
|
||||
@ -84,11 +82,11 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
|
||||
// Audio controls
|
||||
// Current states
|
||||
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
|
||||
|
||||
// Buffer for speaker data
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ/2];
|
||||
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 2];
|
||||
|
||||
void led_blinking_task(void);
|
||||
void audio_task(void);
|
||||
@ -96,20 +94,18 @@ void audio_task(void);
|
||||
#if CFG_AUDIO_DEBUG
|
||||
void audio_debug_task(void);
|
||||
uint8_t current_alt_settings;
|
||||
uint16_t fifo_count;
|
||||
uint32_t fifo_count_avg;
|
||||
volatile uint16_t fifo_count;
|
||||
volatile uint32_t fifo_count_avg;
|
||||
#endif
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
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
|
||||
};
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
if (board_init_after_tusb) {
|
||||
@ -118,9 +114,8 @@ int main(void)
|
||||
|
||||
TU_LOG1("Speaker running\r\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // TinyUSB device task
|
||||
while (1) {
|
||||
tud_task();// TinyUSB device task
|
||||
led_blinking_task();
|
||||
#if CFG_AUDIO_DEBUG
|
||||
audio_debug_task();
|
||||
@ -134,29 +129,25 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
@ -165,43 +156,34 @@ void tud_resume_cb(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
|
||||
{
|
||||
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
|
||||
{
|
||||
if (request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
|
||||
if (request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate);
|
||||
|
||||
audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
|
||||
}
|
||||
else if (request->bRequest == AUDIO_CS_REQ_RANGE)
|
||||
{
|
||||
audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
|
||||
} else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
|
||||
audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
|
||||
};
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
|
||||
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
|
||||
for(uint8_t i = 0; i < N_SAMPLE_RATES; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) {
|
||||
rangef.subrange[i].bMin = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bMax = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bRes = 0;
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
|
||||
}
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
|
||||
}
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
|
||||
request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_1_t cur_valid = { .bCur = 1 };
|
||||
} else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
|
||||
request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_1_t cur_valid = {.bCur = 1};
|
||||
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
|
||||
}
|
||||
TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
@ -209,25 +191,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
|
||||
}
|
||||
|
||||
// Helper for clock set requests
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
|
||||
{
|
||||
(void)rhport;
|
||||
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
|
||||
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
|
||||
|
||||
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
|
||||
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
return false;
|
||||
@ -235,33 +213,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
|
||||
}
|
||||
|
||||
// Helper for feature unit get requests
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
|
||||
{
|
||||
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) {
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
|
||||
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
|
||||
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
|
||||
{
|
||||
if (request->bRequest == AUDIO_CS_REQ_RANGE)
|
||||
{
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
|
||||
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
|
||||
if (request->bRequest == AUDIO_CS_REQ_RANGE) {
|
||||
audio_control_range_2_n_t(1) range_vol = {
|
||||
.wNumSubRanges = tu_htole16(1),
|
||||
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
|
||||
};
|
||||
.wNumSubRanges = tu_htole16(1),
|
||||
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
|
||||
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
|
||||
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
|
||||
}
|
||||
else if (request->bRequest == AUDIO_CS_REQ_CUR)
|
||||
{
|
||||
audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
|
||||
} else if (request->bRequest == AUDIO_CS_REQ_CUR) {
|
||||
audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
|
||||
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
|
||||
}
|
||||
}
|
||||
TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
@ -271,35 +241,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
|
||||
}
|
||||
|
||||
// Helper for feature unit set requests
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
|
||||
{
|
||||
(void)rhport;
|
||||
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
|
||||
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
|
||||
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE)
|
||||
{
|
||||
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
|
||||
|
||||
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur;
|
||||
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
|
||||
{
|
||||
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
|
||||
TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
|
||||
|
||||
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
|
||||
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
return false;
|
||||
@ -307,16 +271,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_CLOCK)
|
||||
return tud_audio_clock_get_request(rhport, request);
|
||||
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_get_request(rhport, request);
|
||||
else
|
||||
{
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
request->bEntityID, request->bControlSelector, request->bRequest);
|
||||
}
|
||||
@ -324,9 +286,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
}
|
||||
|
||||
// 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 *buf)
|
||||
{
|
||||
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
|
||||
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
|
||||
|
||||
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
|
||||
return tud_audio_feature_unit_set_request(rhport, request, buf);
|
||||
@ -338,28 +299,26 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0)
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
|
||||
{
|
||||
(void)rhport;
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
|
||||
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
|
||||
|
||||
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
|
||||
if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0)
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
current_alt_settings = alt;
|
||||
@ -368,37 +327,34 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
|
||||
return true;
|
||||
}
|
||||
|
||||
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param)
|
||||
{
|
||||
(void)func_id;
|
||||
(void)alt_itf;
|
||||
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
|
||||
(void) func_id;
|
||||
(void) alt_itf;
|
||||
// Set feedback method to fifo counting
|
||||
feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT;
|
||||
feedback_param->sample_freq = current_sample_rate;
|
||||
}
|
||||
|
||||
#if CFG_AUDIO_DEBUG
|
||||
bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)n_bytes_received;
|
||||
(void)func_id;
|
||||
(void)ep_out;
|
||||
(void)cur_alt_setting;
|
||||
bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
|
||||
(void) rhport;
|
||||
(void) n_bytes_received;
|
||||
(void) func_id;
|
||||
(void) ep_out;
|
||||
(void) cur_alt_setting;
|
||||
|
||||
fifo_count = tud_audio_available();
|
||||
// Same averaging method used in UAC2 class
|
||||
fifo_count_avg = (uint32_t)(((uint64_t)fifo_count_avg * 63 + ((uint32_t)fifo_count << 16)) >> 6);
|
||||
fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ((uint32_t) fifo_count << 16)) >> 6);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_QUIRK_OS_GUESSING
|
||||
bool tud_audio_feedback_format_correction_cb(uint8_t func_id)
|
||||
{
|
||||
(void)func_id;
|
||||
if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
|
||||
bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
|
||||
(void) func_id;
|
||||
if (tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -409,25 +365,21 @@ bool tud_audio_feedback_format_correction_cb(uint8_t func_id)
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void audio_task(void)
|
||||
{
|
||||
// Replace audio_task() with your I2S transmit callback.
|
||||
// Here we simulate a callback called every 1ms.
|
||||
// This task simulates an audio transmit callback, one frame is sent every 1ms.
|
||||
// In a real application, this would be replaced with actual I2S transmit callback.
|
||||
void audio_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
uint32_t curr_ms = board_millis();
|
||||
if ( start_ms == curr_ms ) return; // not enough time
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
|
||||
uint16_t length = (uint16_t) (current_sample_rate/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX);
|
||||
uint16_t length = (uint16_t) (current_sample_rate / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX);
|
||||
|
||||
if (current_sample_rate == 44100 && (curr_ms % 10 == 0))
|
||||
{
|
||||
if (current_sample_rate == 44100 && (curr_ms % 10 == 0)) {
|
||||
// Take one more sample every 10 cycles, to have a average reading speed of 44.1
|
||||
// This correction is not needed in real world cases
|
||||
length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX;
|
||||
} else
|
||||
if (current_sample_rate == 88200 && (curr_ms % 5 == 0))
|
||||
{
|
||||
} else if (current_sample_rate == 88200 && (curr_ms % 5 == 0)) {
|
||||
// Take one more sample every 5 cycles, to have a average reading speed of 88.2
|
||||
// This correction is not needed in real world cases
|
||||
length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX;
|
||||
@ -439,8 +391,7 @@ void audio_task(void)
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
@ -457,33 +408,30 @@ void led_blinking_task(void)
|
||||
// HID interface for audio debug
|
||||
//--------------------------------------------------------------------+
|
||||
// Every 1ms, we will sent 1 debug information report
|
||||
void audio_debug_task(void)
|
||||
{
|
||||
void audio_debug_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
uint32_t curr_ms = board_millis();
|
||||
if ( start_ms == curr_ms ) return; // not enough time
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
|
||||
audio_debug_info_t debug_info;
|
||||
debug_info.sample_rate = current_sample_rate;
|
||||
debug_info.alt_settings = current_alt_settings;
|
||||
debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ;
|
||||
debug_info.fifo_count = fifo_count;
|
||||
debug_info.sample_rate = current_sample_rate;
|
||||
debug_info.alt_settings = current_alt_settings;
|
||||
debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ;
|
||||
debug_info.fifo_count = fifo_count;
|
||||
debug_info.fifo_count_avg = (uint16_t) (fifo_count_avg >> 16);
|
||||
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++)
|
||||
{
|
||||
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
|
||||
debug_info.mute[i] = mute[i];
|
||||
debug_info.volume[i] = volume[i];
|
||||
}
|
||||
|
||||
if(tud_hid_ready())
|
||||
if (tud_hid_ready())
|
||||
tud_hid_report(0, &debug_info, sizeof(debug_info));
|
||||
}
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Unused here
|
||||
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
|
||||
{
|
||||
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
// TODO not Implemented
|
||||
(void) itf;
|
||||
(void) report_id;
|
||||
@ -496,8 +444,7 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// Unused here
|
||||
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
|
||||
{
|
||||
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
|
||||
// This example doesn't use multiple report and report ID
|
||||
(void) itf;
|
||||
(void) report_id;
|
||||
|
||||
@ -29,12 +29,12 @@ static tusb_desc_type_t desc_req_buf[2];
|
||||
static int desc_req_idx = 0;
|
||||
|
||||
// Place at the start of tud_descriptor_device_cb()
|
||||
void quirk_os_guessing_desc_device_cb() {
|
||||
void quirk_os_guessing_desc_device_cb(void) {
|
||||
desc_req_idx = 0;
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_configuration_cb()
|
||||
void quirk_os_guessing_desc_configuration_cb() {
|
||||
void quirk_os_guessing_desc_configuration_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_CONFIGURATION)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_CONFIGURATION;
|
||||
@ -42,7 +42,7 @@ void quirk_os_guessing_desc_configuration_cb() {
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_bos_cb()
|
||||
void quirk_os_guessing_desc_bos_cb() {
|
||||
void quirk_os_guessing_desc_bos_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_BOS)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_BOS;
|
||||
@ -50,7 +50,7 @@ void quirk_os_guessing_desc_bos_cb() {
|
||||
}
|
||||
|
||||
// Place at the start of tud_descriptor_string_cb()
|
||||
void quirk_os_guessing_desc_string_cb() {
|
||||
void quirk_os_guessing_desc_string_cb(void) {
|
||||
// Skip redundant request
|
||||
if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_STRING)) {
|
||||
desc_req_buf[desc_req_idx++] = TUSB_DESC_STRING;
|
||||
|
||||
@ -99,7 +99,7 @@ usbtmc_response_capabilities_488_t const *
|
||||
#else
|
||||
usbtmc_response_capabilities_t const *
|
||||
#endif
|
||||
tud_usbtmc_get_capabilities_cb()
|
||||
tud_usbtmc_get_capabilities_cb(void)
|
||||
{
|
||||
return &tud_usbtmc_app_capabilities;
|
||||
}
|
||||
@ -161,7 +161,7 @@ bool tud_usbtmc_msg_data_cb(void *data, size_t len, bool transfer_complete)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_usbtmc_msgBulkIn_complete_cb()
|
||||
bool tud_usbtmc_msgBulkIn_complete_cb(void)
|
||||
{
|
||||
if((buffer_tx_ix == buffer_len) || idnQuery) // done
|
||||
{
|
||||
|
||||
@ -292,7 +292,7 @@ void led_blinking_task(void* param) {
|
||||
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
#define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4)
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define USBD_STACK_SIZE 4096
|
||||
int main(void);
|
||||
void app_main(void) {
|
||||
@ -351,8 +351,8 @@ void freertos_init_task(void) {
|
||||
xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
|
||||
#endif
|
||||
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// only start scheduler for non-espressif mcu
|
||||
#ifndef ESP_PLATFORM
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
|
||||
@ -300,7 +300,7 @@ void led_blinking_task(void* param) {
|
||||
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
|
||||
#define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4)
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define USBD_STACK_SIZE 4096
|
||||
int main(void);
|
||||
void app_main(void) {
|
||||
@ -359,8 +359,8 @@ void freertos_init_task(void) {
|
||||
xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
|
||||
#endif
|
||||
|
||||
// skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
|
||||
#if !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// only start scheduler for non-espressif mcu
|
||||
#ifndef ESP_PLATFORM
|
||||
vTaskStartScheduler();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
#endif
|
||||
|
||||
// Espressif IDF requires "freertos/" prefix in include path
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#ifdef ESP_PLATFORM
|
||||
#define CFG_TUSB_OS_INC_PATH freertos/
|
||||
#endif
|
||||
|
||||
|
||||
4
examples/device/webusb_serial/src/CMakeLists.txt
Normal file
4
examples/device/webusb_serial/src/CMakeLists.txt
Normal file
@ -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)
|
||||
801
examples/device/webusb_serial/website/application.js
Normal file
801
examples/device/webusb_serial/website/application.js
Normal file
@ -0,0 +1,801 @@
|
||||
'use strict';
|
||||
|
||||
(async () => {
|
||||
// bind to the html
|
||||
const uiBody = document.body;
|
||||
const uiToggleThemeBtn = document.getElementById('theme-toggle');
|
||||
|
||||
const uiConnectWebUsbSerialBtn = document.getElementById('connect_webusb_serial_btn');
|
||||
const uiConnectSerialBtn = document.getElementById('connect_serial_btn');
|
||||
const uiDisconnectBtn = document.getElementById('disconnect_btn');
|
||||
|
||||
const uiNewlineModeSelect = document.getElementById('newline_mode_select');
|
||||
const uiAutoReconnectCheckbox = document.getElementById('auto_reconnect_checkbox');
|
||||
const uiForgetDeviceBtn = document.getElementById('forget_device_btn');
|
||||
const uiForgetAllDevicesBtn = document.getElementById('forget_all_devices_btn');
|
||||
const uiResetAllBtn = document.getElementById('reset_all_btn');
|
||||
const uiCopyOutputBtn = document.getElementById('copy_output_btn');
|
||||
const uiDownloadOutputCsvBtn = document.getElementById('download_csv_output_btn');
|
||||
|
||||
const uiStatusSpan = document.getElementById('status_span');
|
||||
|
||||
const uiCommandHistoryClearBtn = document.getElementById('clear_command_history_btn');
|
||||
const uiCommandHistoryScrollbox = document.getElementById('command_history_scrollbox');
|
||||
const uiCommandLineInput = document.getElementById('command_line_input');
|
||||
const uiSendModeBtn = document.getElementById('send_mode_btn');
|
||||
|
||||
const uiReceivedDataClearBtn = document.getElementById('clear_received_data_btn');
|
||||
const uiReceivedDataScrollbox = document.getElementById('received_data_scrollbox');
|
||||
|
||||
const uiNearTheBottomThreshold = 100; // pixels from the bottom to trigger scroll
|
||||
|
||||
const maxCommandHistoryLength = 123; // max number of command history entries
|
||||
const maxReceivedDataLength = 8192 / 8; // max number of received data entries
|
||||
|
||||
const THEME_STATES = ['auto', 'light', 'dark'];
|
||||
|
||||
/// https://stackoverflow.com/a/6234804/4479969
|
||||
const escapeHtml = unsafe => {
|
||||
if (typeof unsafe !== 'string') unsafe = String(unsafe);
|
||||
return unsafe
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
};
|
||||
|
||||
class CommandHistoryEntry {
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
this.time = Date.now();
|
||||
this.count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
class ReceivedDataEntry {
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
this.time = Date.now();
|
||||
this.terminated = false;
|
||||
}
|
||||
}
|
||||
|
||||
class Application {
|
||||
constructor() {
|
||||
this.currentPort = null;
|
||||
this.textEncoder = new TextEncoder();
|
||||
this.textDecoder = new TextDecoder();
|
||||
|
||||
this.reconnectTimeoutId = null;
|
||||
|
||||
this.commandHistory = [];
|
||||
this.uiCommandHistoryIndex = -1;
|
||||
|
||||
this.receivedData = [];
|
||||
|
||||
// bind the UI elements
|
||||
uiToggleThemeBtn.addEventListener('click', () => this.toggleTheme());
|
||||
// Listener for OS Theme Changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
const currentPreference = localStorage.getItem('theme') || 'auto';
|
||||
// Only act if the user is in automatic mode
|
||||
if (currentPreference === 'auto') {
|
||||
this.setTheme('auto');
|
||||
}
|
||||
});
|
||||
|
||||
uiConnectWebUsbSerialBtn.addEventListener('click', () => this.connectWebUsbSerialPort());
|
||||
uiConnectSerialBtn.addEventListener('click', () => this.connectSerialPort());
|
||||
uiDisconnectBtn.addEventListener('click', () => this.disconnectPort());
|
||||
uiNewlineModeSelect.addEventListener('change', () => this.setNewlineMode());
|
||||
uiAutoReconnectCheckbox.addEventListener('change', () => this.autoReconnectChanged());
|
||||
uiForgetDeviceBtn.addEventListener('click', () => this.forgetPort());
|
||||
uiForgetAllDevicesBtn.addEventListener('click', () => this.forgetAllPorts());
|
||||
uiResetAllBtn.addEventListener('click', () => this.resetAll());
|
||||
uiCopyOutputBtn.addEventListener('click', () => this.copyOutput());
|
||||
uiDownloadOutputCsvBtn.addEventListener('click', () => this.downloadOutputCsv());
|
||||
uiCommandHistoryClearBtn.addEventListener('click', () => this.clearCommandHistory());
|
||||
uiCommandLineInput.addEventListener('keydown', (e) => this.handleCommandLineInput(e));
|
||||
uiSendModeBtn.addEventListener('click', () => this.toggleSendMode());
|
||||
uiReceivedDataClearBtn.addEventListener('click', () => this.clearReceivedData());
|
||||
|
||||
window.addEventListener('beforeunload', () => this.beforeUnloadHandler());
|
||||
|
||||
// restore state from localStorage
|
||||
try {
|
||||
this.restoreState();
|
||||
} catch (error) {
|
||||
console.error('Failed to restore state from localStorage', error);
|
||||
this.resetAll();
|
||||
this.restoreState();
|
||||
}
|
||||
|
||||
this.updateUIConnectionState();
|
||||
this.connectWebUsbSerialPort(true);
|
||||
}
|
||||
|
||||
beforeUnloadHandler() {
|
||||
// Save the scroll position of the command history and received data
|
||||
localStorage.setItem('commandHistoryScrollTop', uiCommandHistoryScrollbox.scrollTop);
|
||||
localStorage.setItem('receivedDataScrollTop', uiReceivedDataScrollbox.scrollTop);
|
||||
}
|
||||
|
||||
restoreState() {
|
||||
// Restore theme choice
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme) {
|
||||
this.setTheme(savedTheme);
|
||||
}
|
||||
|
||||
// Restore command history
|
||||
let savedCommandHistory = JSON.parse(localStorage.getItem('commandHistory') || '[]');
|
||||
for (const cmd of savedCommandHistory) {
|
||||
this.addCommandToHistoryUI(cmd);
|
||||
}
|
||||
// Restore scroll position for command history
|
||||
const commandHistoryScrollTop = localStorage.getItem('commandHistoryScrollTop');
|
||||
if (commandHistoryScrollTop) {
|
||||
uiCommandHistoryScrollbox.scrollTop = parseInt(commandHistoryScrollTop, 10);
|
||||
}
|
||||
|
||||
// Restore received data
|
||||
let savedReceivedData = JSON.parse(localStorage.getItem('receivedData') || '[]');
|
||||
for (let line of savedReceivedData) {
|
||||
line.terminated = true;
|
||||
this.addReceivedDataEntryUI(line);
|
||||
}
|
||||
// Restore scroll position for received data
|
||||
const receivedDataScrollTop = localStorage.getItem('receivedDataScrollTop');
|
||||
if (receivedDataScrollTop) {
|
||||
uiReceivedDataScrollbox.scrollTop = parseInt(receivedDataScrollTop, 10);
|
||||
}
|
||||
|
||||
this.sendMode = localStorage.getItem('sendMode') || 'command';
|
||||
this.setSendMode(this.sendMode);
|
||||
|
||||
uiAutoReconnectCheckbox.checked = !(localStorage.getItem('autoReconnect') === 'false');
|
||||
|
||||
let savedNewlineMode = localStorage.getItem('newlineMode');
|
||||
if (savedNewlineMode) {
|
||||
uiNewlineModeSelect.value = savedNewlineMode;
|
||||
}
|
||||
}
|
||||
|
||||
setTheme(theme) {
|
||||
const modeName = theme.charAt(0).toUpperCase() + theme.slice(1);
|
||||
uiToggleThemeBtn.textContent = `Theme: ${modeName}`;
|
||||
|
||||
if (theme === 'auto') {
|
||||
// In auto mode, we rely on the OS preference.
|
||||
// We check the media query and add/remove the class accordingly.
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (prefersDark) {
|
||||
uiBody.classList.add('dark-mode');
|
||||
} else {
|
||||
uiBody.classList.remove('dark-mode');
|
||||
}
|
||||
} else if (theme === 'light') {
|
||||
// Force light mode by removing the class.
|
||||
uiBody.classList.remove('dark-mode');
|
||||
} else if (theme === 'dark') {
|
||||
// Force dark mode by adding the class.
|
||||
uiBody.classList.add('dark-mode');
|
||||
}
|
||||
|
||||
// Save the theme to localStorage
|
||||
localStorage.setItem('theme', theme);
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
const currentTheme = localStorage.getItem('theme') || 'auto';
|
||||
const nextThemeIndex = (THEME_STATES.indexOf(currentTheme) + 1) % THEME_STATES.length;
|
||||
const nextTheme = THEME_STATES[nextThemeIndex];
|
||||
this.setTheme(nextTheme);
|
||||
}
|
||||
|
||||
addCommandToHistoryUI(commandHistoryEntry) {
|
||||
let commandHistoryEntryBtn = null;
|
||||
|
||||
let lastCommandMatched = false;
|
||||
if (this.commandHistory.length > 0) {
|
||||
let lastCommandEntry = this.commandHistory[this.commandHistory.length - 1];
|
||||
if (lastCommandEntry.text === commandHistoryEntry.text) {
|
||||
lastCommandEntry.count++;
|
||||
lastCommandEntry.time = Date.now();
|
||||
lastCommandMatched = true;
|
||||
|
||||
// Update the last command entry
|
||||
commandHistoryEntryBtn = uiCommandHistoryScrollbox.lastElementChild;
|
||||
let time_str = new Date(lastCommandEntry.time).toLocaleString();
|
||||
commandHistoryEntryBtn.querySelector('.command-history-entry-time').textContent = time_str;
|
||||
commandHistoryEntryBtn.querySelector('.command-history-entry-text').textContent = lastCommandEntry.text;
|
||||
commandHistoryEntryBtn.querySelector('.command-history-entry-count').textContent = '×' + lastCommandEntry.count;
|
||||
}
|
||||
}
|
||||
if (!lastCommandMatched) {
|
||||
this.commandHistory.push(commandHistoryEntry);
|
||||
|
||||
// Create a new command history entry
|
||||
commandHistoryEntryBtn = document.createElement('button');
|
||||
commandHistoryEntryBtn.className = 'command-history-entry';
|
||||
commandHistoryEntryBtn.type = 'button';
|
||||
let time_str = new Date(commandHistoryEntry.time).toLocaleString();
|
||||
commandHistoryEntryBtn.innerHTML = `
|
||||
<span class="command-history-entry-time">${escapeHtml(time_str)}</span>
|
||||
<span class="command-history-entry-text">${escapeHtml(commandHistoryEntry.text)}</span>
|
||||
<span class="command-history-entry-count">×${escapeHtml(commandHistoryEntry.count)}</span>
|
||||
`;
|
||||
commandHistoryEntryBtn.addEventListener('click', () => {
|
||||
if (uiCommandLineInput.disabled) return;
|
||||
uiCommandLineInput.value = commandHistoryEntry.text;
|
||||
uiCommandLineInput.focus();
|
||||
});
|
||||
|
||||
uiCommandHistoryScrollbox.appendChild(commandHistoryEntryBtn);
|
||||
}
|
||||
|
||||
// Limit the command history length
|
||||
while (this.commandHistory.length > maxCommandHistoryLength) {
|
||||
this.commandHistory.shift();
|
||||
uiCommandHistoryScrollbox.removeChild(uiCommandHistoryScrollbox.firstElementChild);
|
||||
}
|
||||
}
|
||||
|
||||
appendNewCommandToHistory(commandHistoryEntry) {
|
||||
const wasNearBottom = this.isNearBottom(uiCommandHistoryScrollbox);
|
||||
|
||||
this.addCommandToHistoryUI(commandHistoryEntry);
|
||||
|
||||
// Save the command history to localStorage
|
||||
localStorage.setItem('commandHistory', JSON.stringify(this.commandHistory));
|
||||
|
||||
// Scroll to the new entry if near the bottom
|
||||
if (wasNearBottom) {
|
||||
this.scrollToBottom(uiCommandHistoryScrollbox);
|
||||
}
|
||||
}
|
||||
|
||||
clearCommandHistory() {
|
||||
this.commandHistory = [];
|
||||
uiCommandHistoryScrollbox.textContent = '';
|
||||
localStorage.removeItem('commandHistory');
|
||||
this.setStatus('Command history cleared', 'info');
|
||||
}
|
||||
|
||||
isNearBottom(container) {
|
||||
return container.scrollHeight - container.scrollTop <= container.clientHeight + uiNearTheBottomThreshold;
|
||||
}
|
||||
|
||||
scrollToBottom(container) {
|
||||
requestAnimationFrame(() => {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
});
|
||||
}
|
||||
|
||||
addReceivedDataEntryUI(receivedDataEntry) {
|
||||
let newReceivedDataEntries = [];
|
||||
let updateLastReceivedDataEntry = false;
|
||||
if (this.receivedData.length <= 0) {
|
||||
newReceivedDataEntries.push(receivedDataEntry);
|
||||
} else {
|
||||
let lastReceivedDataEntry = this.receivedData[this.receivedData.length - 1];
|
||||
// Check if the last entry is terminated
|
||||
if (lastReceivedDataEntry.terminated) {
|
||||
newReceivedDataEntries.push(receivedDataEntry);
|
||||
} else {
|
||||
if (!lastReceivedDataEntry.terminated) {
|
||||
updateLastReceivedDataEntry = true;
|
||||
this.receivedData.pop();
|
||||
receivedDataEntry.text = lastReceivedDataEntry.text + receivedDataEntry.text;
|
||||
}
|
||||
// split the text into lines
|
||||
let lines = receivedDataEntry.text.split(/\r?\n/);
|
||||
// check if the last line is terminated by checking if it ends with an empty string
|
||||
let lastLineTerminated = lines[lines.length - 1] === '';
|
||||
if (lastLineTerminated) {
|
||||
lines.pop(); // remove the last empty line
|
||||
}
|
||||
|
||||
// create new entries for each line
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
let entry = new ReceivedDataEntry(line);
|
||||
if (i === lines.length - 1) {
|
||||
entry.terminated = lastLineTerminated;
|
||||
} else {
|
||||
entry.terminated = true;
|
||||
}
|
||||
newReceivedDataEntries.push(entry);
|
||||
}
|
||||
// if the last line is terminated, modify the last entry
|
||||
if (lastLineTerminated) {
|
||||
newReceivedDataEntries[newReceivedDataEntries.length - 1].terminated = true;
|
||||
} else {
|
||||
newReceivedDataEntries[newReceivedDataEntries.length - 1].terminated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.receivedData.push(...newReceivedDataEntries);
|
||||
|
||||
if (updateLastReceivedDataEntry) {
|
||||
// update the rendering of the last entry
|
||||
let lastReceivedDataEntryBtn = uiReceivedDataScrollbox.lastElementChild;
|
||||
lastReceivedDataEntryBtn.querySelector('.received-data-entry-text').textContent = newReceivedDataEntries[0].text;
|
||||
lastReceivedDataEntryBtn.querySelector('.received-data-entry-time').textContent = new Date(newReceivedDataEntries[0].time).toLocaleString();
|
||||
newReceivedDataEntries.shift();
|
||||
}
|
||||
|
||||
// render the new entries
|
||||
let documentFragment = document.createDocumentFragment();
|
||||
for (const entry of newReceivedDataEntries) {
|
||||
let receivedDataEntryBtn = document.createElement('div');
|
||||
receivedDataEntryBtn.className = 'received-data-entry';
|
||||
receivedDataEntryBtn.innerHTML = `
|
||||
<span class="received-data-entry-time">${escapeHtml(new Date(entry.time).toLocaleString())}</span>
|
||||
<span class="received-data-entry-text">${escapeHtml(entry.text)}</span>
|
||||
`;
|
||||
documentFragment.appendChild(receivedDataEntryBtn);
|
||||
}
|
||||
uiReceivedDataScrollbox.appendChild(documentFragment);
|
||||
|
||||
// Limit the received data length
|
||||
while (this.receivedData.length > maxReceivedDataLength) {
|
||||
this.receivedData.shift();
|
||||
uiReceivedDataScrollbox.removeChild(uiReceivedDataScrollbox.firstElementChild);
|
||||
}
|
||||
}
|
||||
|
||||
appendNewReceivedData(receivedDataEntry) {
|
||||
const wasNearBottom = this.isNearBottom(uiReceivedDataScrollbox);
|
||||
|
||||
this.addReceivedDataEntryUI(receivedDataEntry);
|
||||
|
||||
// Save the received data to localStorage
|
||||
localStorage.setItem('receivedData', JSON.stringify(this.receivedData));
|
||||
|
||||
// Scroll to the new entry if near the bottom
|
||||
if (wasNearBottom) {
|
||||
this.scrollToBottom(uiReceivedDataScrollbox);
|
||||
}
|
||||
}
|
||||
|
||||
clearReceivedData() {
|
||||
this.receivedData = [];
|
||||
uiReceivedDataScrollbox.textContent = '';
|
||||
localStorage.removeItem('receivedData');
|
||||
this.setStatus('Received data cleared', 'info');
|
||||
}
|
||||
|
||||
setStatus(msg, level = 'info') {
|
||||
console.error(msg);
|
||||
uiStatusSpan.textContent = msg;
|
||||
uiStatusSpan.className = 'status status-' + level;
|
||||
}
|
||||
|
||||
/// force_connected is used to instantly change the UI to the connected state while the device is still connecting
|
||||
/// Otherwise we would have to wait for the connection to be established.
|
||||
/// This can take until the device sends the first data packet.
|
||||
updateUIConnectionState(force_connected = false) {
|
||||
if (force_connected || (this.currentPort && this.currentPort.isConnected)) {
|
||||
uiConnectWebUsbSerialBtn.style.display = 'none';
|
||||
uiConnectSerialBtn.style.display = 'none';
|
||||
uiDisconnectBtn.style.display = 'block';
|
||||
uiCommandLineInput.disabled = false;
|
||||
|
||||
if (this.currentPort instanceof SerialPort) {
|
||||
uiDisconnectBtn.textContent = 'Disconnect from WebSerial';
|
||||
} else if (this.currentPort instanceof WebUsbSerialPort) {
|
||||
uiDisconnectBtn.textContent = 'Disconnect from WebUSB';
|
||||
} else {
|
||||
uiDisconnectBtn.textContent = 'Disconnect';
|
||||
}
|
||||
} else {
|
||||
if (serial.isWebUsbSupported()) {
|
||||
uiConnectWebUsbSerialBtn.style.display = 'block';
|
||||
}
|
||||
if (serial.isWebSerialSupported()) {
|
||||
uiConnectSerialBtn.style.display = 'block';
|
||||
}
|
||||
if (!serial.isWebUsbSupported() && !serial.isWebSerialSupported()) {
|
||||
this.setStatus('Your browser does not support WebUSB or WebSerial', 'error');
|
||||
}
|
||||
uiDisconnectBtn.style.display = 'none';
|
||||
uiCommandLineInput.disabled = true;
|
||||
uiCommandLineInput.value = '';
|
||||
uiCommandLineInput.blur();
|
||||
}
|
||||
}
|
||||
|
||||
async disconnectPort() {
|
||||
this.stopAutoReconnect();
|
||||
|
||||
if (!this.currentPort) {
|
||||
this.updateUIConnectionState();
|
||||
return;
|
||||
};
|
||||
|
||||
try {
|
||||
await this.currentPort.disconnect();
|
||||
this.setStatus('Disconnected', 'info');
|
||||
}
|
||||
catch (error) {
|
||||
this.setStatus(`Disconnect error: ${error.message}`, 'error');
|
||||
}
|
||||
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
|
||||
async onReceive(dataView) {
|
||||
this.updateUIConnectionState();
|
||||
|
||||
let text = this.textDecoder.decode(dataView);
|
||||
let receivedDataEntry = new ReceivedDataEntry(text);
|
||||
this.appendNewReceivedData(receivedDataEntry);
|
||||
}
|
||||
|
||||
async onReceiveError(error) {
|
||||
this.setStatus(`Read error: ${error.message}`, 'error');
|
||||
await this.disconnectPort();
|
||||
// Start auto reconnect on error if enabled
|
||||
this.tryAutoReconnect();
|
||||
}
|
||||
|
||||
async connectSerialPort() {
|
||||
if (!serial.isWebSerialSupported()) {
|
||||
this.setStatus('Serial not supported on this browser', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.setStatus('Requesting device...', 'info');
|
||||
this.currentPort = await serial.requestSerialPort();
|
||||
this.updateUIConnectionState(true);
|
||||
this.currentPort.onReceiveError = error => this.onReceiveError(error);
|
||||
this.currentPort.onReceive = dataView => this.onReceive(dataView);
|
||||
await this.currentPort.connect();
|
||||
this.setStatus('Connected', 'info');
|
||||
} catch (error) {
|
||||
this.setStatus(`Connection failed: ${error.message}`, 'error');
|
||||
if (this.currentPort) {
|
||||
await this.currentPort.forgetDevice();
|
||||
this.currentPort = null;
|
||||
}
|
||||
} finally {
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
}
|
||||
|
||||
async connectWebUsbSerialPort(initial = false) {
|
||||
if (!serial.isWebUsbSupported()) {
|
||||
this.setStatus('WebUSB not supported on this browser', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let first_time_connection = false;
|
||||
let grantedDevices = await serial.getWebUsbSerialPorts();
|
||||
if (initial) {
|
||||
if (!uiAutoReconnectCheckbox.checked || grantedDevices.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to the device that was saved to localStorage otherwise use the first one
|
||||
const savedPortInfo = JSON.parse(localStorage.getItem('webUSBSerialPort'));
|
||||
if (savedPortInfo) {
|
||||
for (const device of grantedDevices) {
|
||||
if (device._device.vendorId === savedPortInfo.vendorId && device._device.productId === savedPortInfo.productId) {
|
||||
this.currentPort = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.currentPort) {
|
||||
this.currentPort = grantedDevices[0];
|
||||
}
|
||||
|
||||
this.setStatus('Connecting to first device...', 'info');
|
||||
} else {
|
||||
// Prompt the user to select a device
|
||||
this.setStatus('Requesting device...', 'info');
|
||||
this.currentPort = await serial.requestWebUsbSerialPort();
|
||||
first_time_connection = true;
|
||||
}
|
||||
|
||||
this.currentPort.onReceiveError = error => this.onReceiveError(error);
|
||||
this.currentPort.onReceive = dataView => this.onReceive(dataView);
|
||||
|
||||
try {
|
||||
this.updateUIConnectionState(true);
|
||||
await this.currentPort.connect();
|
||||
|
||||
// save the port to localStorage
|
||||
const portInfo = {
|
||||
vendorId: this.currentPort._device.vendorId,
|
||||
productId: this.currentPort._device.productId,
|
||||
}
|
||||
localStorage.setItem('webUSBSerialPort', JSON.stringify(portInfo));
|
||||
|
||||
this.setStatus('Connected', 'info');
|
||||
uiCommandLineInput.focus();
|
||||
} catch (error) {
|
||||
if (first_time_connection) {
|
||||
// Forget the device if a first time connection fails
|
||||
await this.currentPort.forgetDevice();
|
||||
this.currentPort = null;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
|
||||
this.updateUIConnectionState();
|
||||
} catch (error) {
|
||||
this.setStatus(`Connection failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async reconnectPort() {
|
||||
if (this.currentPort) {
|
||||
this.setStatus('Reconnecting...', 'info');
|
||||
try {
|
||||
await this.currentPort.connect();
|
||||
this.setStatus('Reconnected', 'info');
|
||||
} catch (error) {
|
||||
this.setStatus(`Reconnect failed: ${error.message}`, 'error');
|
||||
} finally {
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
}
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
|
||||
async forgetPort() {
|
||||
this.stopAutoReconnect();
|
||||
if (this.currentPort) {
|
||||
await this.currentPort.forgetDevice();
|
||||
this.currentPort = null;
|
||||
this.setStatus('Device forgotten', 'info');
|
||||
} else {
|
||||
this.setStatus('No device to forget', 'error');
|
||||
}
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
|
||||
async forgetAllPorts() {
|
||||
this.stopAutoReconnect();
|
||||
await this.forgetPort();
|
||||
if (serial.isWebUsbSupported()) {
|
||||
let ports = await serial.getWebUsbSerialPorts();
|
||||
for (const p of ports) {
|
||||
await p.forgetDevice();
|
||||
}
|
||||
}
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
|
||||
setNewlineMode() {
|
||||
localStorage.setItem('newlineMode', uiNewlineModeSelect.value);
|
||||
}
|
||||
|
||||
autoReconnectChanged() {
|
||||
if (uiAutoReconnectCheckbox.checked) {
|
||||
this.setStatus('Auto-reconnect enabled', 'info');
|
||||
this.tryAutoReconnect();
|
||||
} else {
|
||||
this.setStatus('Auto-reconnect disabled', 'info');
|
||||
this.stopAutoReconnect();
|
||||
}
|
||||
localStorage.setItem('autoReconnect', uiAutoReconnectCheckbox.checked);
|
||||
}
|
||||
|
||||
stopAutoReconnect() {
|
||||
if (this.reconnectTimeoutId !== null) {
|
||||
clearTimeout(this.reconnectTimeoutId);
|
||||
this.reconnectTimeoutId = null;
|
||||
this.setStatus('Auto-reconnect stopped.', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
async autoReconnectTimeout() {
|
||||
this.reconnectTimeoutId = null;
|
||||
if (!uiAutoReconnectCheckbox.checked) {
|
||||
this.setStatus('Auto-reconnect stopped.', 'info');
|
||||
return;
|
||||
}
|
||||
if (this.currentPort && !this.currentPort.isConnected) {
|
||||
try {
|
||||
await this.currentPort.connect();
|
||||
this.setStatus('Reconnected successfully', 'info');
|
||||
} catch (error) {
|
||||
this.setStatus(`Reconnect failed: ${error.message}`, 'error');
|
||||
// Try again after a delay
|
||||
this.tryAutoReconnect();
|
||||
} finally {
|
||||
this.updateUIConnectionState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryAutoReconnect() {
|
||||
this.updateUIConnectionState();
|
||||
if (!uiAutoReconnectCheckbox.checked) return;
|
||||
if (this.reconnectTimeoutId !== null) return; // already trying
|
||||
this.setStatus('Attempting to auto-reconnect...', 'info');
|
||||
this.reconnectTimeoutId = setTimeout(async () => {
|
||||
await this.autoReconnectTimeout();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async handleCommandLineInput(e) {
|
||||
// Instant mode: send key immediately including special keys like Backspace, arrows, enter, etc.
|
||||
if (this.sendMode === 'instant') {
|
||||
e.preventDefault();
|
||||
|
||||
// Ignore only pure modifier keys without text representation
|
||||
if (e.key.length === 1 ||
|
||||
e.key === 'Enter' ||
|
||||
e.key === 'Backspace' ||
|
||||
e.key === 'Tab' ||
|
||||
e.key === 'Escape' ||
|
||||
e.key === 'Delete' ) {
|
||||
|
||||
let sendText = '';
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
switch (uiNewlineModeSelect.value) {
|
||||
case 'CR': sendText = '\r'; break;
|
||||
case 'CRLF': sendText = '\r\n'; break;
|
||||
default: sendText = '\n'; break;
|
||||
}
|
||||
break;
|
||||
case 'Backspace':
|
||||
// Usually no straightforward char to send for Backspace,
|
||||
// but often ASCII DEL '\x7F' or '\b' (0x08) is sent.
|
||||
sendText = '\x08'; // backspace
|
||||
break;
|
||||
case 'Tab':
|
||||
sendText = '\t';
|
||||
break;
|
||||
case 'Escape':
|
||||
// Ignore or send ESC control char if needed
|
||||
sendText = '\x1B';
|
||||
break;
|
||||
case 'Delete':
|
||||
sendText = '\x7F'; // DEL char
|
||||
break;
|
||||
default:
|
||||
sendText = e.key;
|
||||
}
|
||||
try {
|
||||
await this.currentPort.send(this.textEncoder.encode(sendText));
|
||||
} catch (error) {
|
||||
this.setStatus(`Send error: ${error.message}`, 'error');
|
||||
this.tryAutoReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Command mode: handle up/down arrow keys for history
|
||||
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (this.commandHistory.length === 0) return;
|
||||
if (e.key === 'ArrowUp') {
|
||||
if (this.uiCommandHistoryIndex === -1) this.uiCommandHistoryIndex = this.commandHistory.length - 1;
|
||||
else if (this.uiCommandHistoryIndex > 0) this.uiCommandHistoryIndex--;
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
if (this.uiCommandHistoryIndex !== -1) this.uiCommandHistoryIndex++;
|
||||
if (this.uiCommandHistoryIndex >= this.commandHistory.length) this.uiCommandHistoryIndex = -1;
|
||||
}
|
||||
uiCommandLineInput.value = this.uiCommandHistoryIndex === -1 ? '' : this.commandHistory[this.uiCommandHistoryIndex].text;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key !== 'Enter' || !this.currentPort.isConnected) return;
|
||||
e.preventDefault();
|
||||
const text = uiCommandLineInput.value;
|
||||
if (!text) return;
|
||||
|
||||
// Convert to Uint8Array with newline based on config
|
||||
let sendText = text;
|
||||
switch (uiNewlineModeSelect.value) {
|
||||
case 'CR':
|
||||
sendText += '\r';
|
||||
break;
|
||||
case 'CRLF':
|
||||
sendText += '\r\n';
|
||||
break;
|
||||
case 'ANY':
|
||||
sendText += '\n';
|
||||
break;
|
||||
}
|
||||
const data = this.textEncoder.encode(sendText);
|
||||
|
||||
try {
|
||||
await this.currentPort.send(data);
|
||||
this.uiCommandHistoryIndex = -1;
|
||||
let history_cmd_text = sendText.replace(/[\r\n]+$/, '');
|
||||
let history_entry = new CommandHistoryEntry(history_cmd_text);
|
||||
this.appendNewCommandToHistory(history_entry);
|
||||
uiCommandLineInput.value = '';
|
||||
} catch (error) {
|
||||
this.setStatus(`Send error: ${error.message}`, 'error');
|
||||
this.tryAutoReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
toggleSendMode() {
|
||||
if (this.sendMode === 'instant') {
|
||||
this.setSendMode('command');
|
||||
} else {
|
||||
this.setSendMode('instant');
|
||||
}
|
||||
}
|
||||
|
||||
setSendMode(mode) {
|
||||
this.sendMode = mode;
|
||||
if (mode === 'instant') {
|
||||
uiSendModeBtn.classList.remove('send-mode-command');
|
||||
uiSendModeBtn.classList.add('send-mode-instant');
|
||||
uiSendModeBtn.textContent = 'Instant mode';
|
||||
} else {
|
||||
uiSendModeBtn.classList.remove('send-mode-instant');
|
||||
uiSendModeBtn.classList.add('send-mode-command');
|
||||
uiSendModeBtn.textContent = 'Command mode';
|
||||
}
|
||||
localStorage.setItem('sendMode', this.sendMode);
|
||||
}
|
||||
|
||||
copyOutput() {
|
||||
let text = '';
|
||||
for (const entry of this.receivedData) {
|
||||
text += entry.text;
|
||||
if (entry.terminated) {
|
||||
text += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.setStatus('Output copied to clipboard', 'info');
|
||||
}, () => {
|
||||
this.setStatus('Failed to copy output', 'error');
|
||||
});
|
||||
} else {
|
||||
this.setStatus('No output to copy', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
downloadOutputCsv() {
|
||||
// save <iso_date_time>,<received_line>
|
||||
let csvContent = 'data:text/csv;charset=utf-8,';
|
||||
for (const entry of this.receivedData) {
|
||||
let sanitizedText = entry.text.replace(/"/g, '""').replace(/[\r\n]+$/, '');
|
||||
let line = new Date(entry.time).toISOString() + ',"' + sanitizedText + '"';
|
||||
csvContent += line + '\n';
|
||||
}
|
||||
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', encodedUri);
|
||||
const filename = new Date().toISOString().replace(/:/g, '-') + '_tinyusb_received_serial_data.csv';
|
||||
link.setAttribute('download', filename);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
async resetAll() {
|
||||
await this.forgetAllPorts();
|
||||
|
||||
// Clear localStorage
|
||||
localStorage.clear();
|
||||
|
||||
// reload the page
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const app = new Application();
|
||||
})()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user