Merge branch 'master' into refactor-irqhandler

This commit is contained in:
Ha Thach 2020-04-11 15:49:34 +07:00 committed by GitHub
commit 04a06ec401
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 2527 additions and 104 deletions

View File

@ -3,11 +3,12 @@ name: Build
on: [pull_request, push, repository_dispatch]
jobs:
# Unit testing with Ceedling
unit-test:
runs-on: ubuntu-latest
steps:
- name: Setup Ruby
uses: actions/setup-ruby@v1.0.0
uses: actions/setup-ruby@v1
- name: Checkout TinyUSB
uses: actions/checkout@v2
@ -19,18 +20,22 @@ jobs:
cd test
ceedling test:all
# Build most of the ports
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example: ['board_test', 'cdc_dual_ports', 'cdc_msc', 'cdc_msc_freertos', 'dfu_rt',
'hid_composite', 'hid_generic_inout', 'midi_test', 'msc_dual_lun', 'net_lwip_webserver',
'hid_composite', 'hid_composite_freertos', 'hid_generic_inout', 'midi_test', 'msc_dual_lun', 'net_lwip_webserver',
'usbtmc', 'webusb_serial']
steps:
- name: Setup Python
uses: actions/setup-python@v1
- name: Setup Node.js
uses: actions/setup-node@v1
- name: Cache MSP430 Toolchain
id: cache-msp430
uses: actions/cache@v1
@ -38,22 +43,22 @@ jobs:
path: /tmp/dl/
# Increment serial number at end when updating downloads
key: msp430-${{ runner.os }}-0
- name: Setup Node.js
uses: actions/setup-node@v1.1.0
- name: Install Toolchains
run: |
# ARM & RISC-V GCC from xpack
npm install --global xpm
xpm install --global @xpack-dev-tools/arm-none-eabi-gcc@latest
xpm install --global @xpack-dev-tools/riscv-none-embed-gcc@latest
echo "::add-path::`echo $HOME/opt/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/*/.content/bin`"
echo "::add-path::`echo $HOME/opt/xPacks/@xpack-dev-tools/riscv-none-embed-gcc/*/.content/bin`"
# TI MSP430 GCC
mkdir -p /tmp/dl/
[ -f "/tmp/dl/msp430-gcc.tar.bz2" ] || wget --progress=dot:mega http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/8_3_0_0/exports/msp430-gcc-8.3.0.16_linux64.tar.bz2 -O /tmp/dl/msp430-gcc.tar.bz2
tar -C $HOME -xaf /tmp/dl/msp430-gcc.tar.bz2
echo "::add-path::`echo $HOME/opt/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/*/.content/bin`"
echo "::add-path::`echo $HOME/opt/xPacks/@xpack-dev-tools/riscv-none-embed-gcc/*/.content/bin`"
echo "::add-path::`echo $HOME/msp430-gcc-*_linux64/bin`"
- name: Checkout TinyUSB
uses: actions/checkout@v2
with:
@ -68,5 +73,28 @@ jobs:
git submodule update --init --recursive --depth 1
- name: Build
run: python3 tools/build_all.py ${{ matrix.example }}
run: |
python3 tools/build_all.py ${{ matrix.example }}
# Build ESP32S
build-esp32s:
runs-on: ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v1
- name: Install Toolchains
run: |
git clone --depth 1 https://github.com/espressif/esp-idf.git $HOME/esp-idf
cd $HOME/esp-idf
./install.sh
- name: Checkout TinyUSB
uses: actions/checkout@v2
with:
submodules: 'false'
- name: Build
run: |
. $HOME/esp-idf/export.sh
python3 tools/build_esp32s.py

View File

@ -30,6 +30,7 @@ Special thanks to all the people who spent their precious time and effort to hel
The stack supports the following MCUs:
- **Espressif:** ESP32-S2
- **MicroChip:** SAMD21, SAMD51 (device only)
- **NordicSemi:** nRF52840, nRF52833
- **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505

View File

@ -9,6 +9,10 @@ The board support code is only used for self-contained examples and testing. It
This code base already had supported for a handful of following boards (sorted alphabetically)
### Espressif ESP32-S2
- [ESP32-S2-Saola-1](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html)
### MicroChip SAMD
- [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)

View File

@ -17,11 +17,11 @@ It is relatively simple to incorporate tinyusb to your (existing) project
1. Copy or `git submodule` this repo into your project in a subfolder. Let's say it is *your_project/tinyusb*
2. Add all the .c in the src folder to your project settings (uvproj, ewp, makefile)
3. Add *your_project/tinysb* to your include path. Also make sure your current include path also contains the configuration file tusb_config.h. Or you could simply put the tusb_config.h into the tinyusb folder as well.
4. 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 demo projects).
5. If you use the device stack, make sure you have created/modified usb descriptors for your own need. Ultimately you need to fill out required pointers in tusbd_descriptor_pointers for that stack to work.
4. 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).
5. 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 that stack to work.
6. Add tusb_init() call to your reset initialization code.
7. Implement all enabled classes's callbacks.
8. If you don't use any RTOSes at all, you need to continuously and/or periodically call tud_task()/tuh_task() function. Most of the callbacks and functionality are handled and invoke within the call of that task runner.
8. 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 invoke within the call of that task runner.
~~~{.c}
int main(void)

View File

@ -6,7 +6,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_hid 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
@ -19,7 +19,7 @@ Once this is done, create a directory in `hw/bsp/<your board name>` for the spec
## 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_hid BOARD=<board>`
`make -C examples/device/cdc_msc BOARD=<board>`
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.
@ -62,6 +62,7 @@ All of the code for the low-level device API is in `src/portable/<vendor>/<chip
##### dcd_init
Initializes the USB peripheral for device mode and enables it.
This function should leave an internal D+/D- pull-up in its default power-on state. `dcd_connect` will be called by the USBD core following `dcd_init`.
##### dcd_int_enable / dcd_int_disable
@ -85,6 +86,10 @@ Called when the device received SET_CONFIG request, you can leave this empty if
Called to remote wake up host when suspended (e.g hid keyboard)
##### 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.)
#### 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.

View File

@ -0,0 +1,15 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# TOP is absolute path to root directory of TinyUSB git repo
set(TOP "../../..")
get_filename_component(TOP "${TOP}" REALPATH)
# Add example src and bsp directories
set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2_saola_1")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32s2)
project(board_test)

View File

@ -0,0 +1,4 @@
CONFIG_IDF_CMAKE=y
CONFIG_IDF_TARGET="esp32s2"
CONFIG_IDF_TARGET_ESP32S2=y

View File

@ -0,0 +1,15 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES freertos soc)
target_compile_options(${COMPONENT_TARGET} PUBLIC
"-DCFG_TUSB_MCU=OPT_MCU_ESP32S2"
"-DCFG_TUSB_OS=OPT_OS_FREERTOS"
)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_include_directories(${COMPONENT_TARGET} PUBLIC
"${FREERTOS_ORIG_INCLUDE_PATH}"
"${TOP}/hw"
"${TOP}/src"
)

View File

@ -73,3 +73,10 @@ int main(void)
return 0;
}
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
void app_main(void)
{
main();
}
#endif

View File

@ -39,8 +39,11 @@
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE
#define CFG_TUSB_OS OPT_OS_NONE
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0

View File

@ -0,0 +1,15 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# TOP is absolute path to root directory of TinyUSB git repo
set(TOP "../../..")
get_filename_component(TOP "${TOP}" REALPATH)
# Add example src and bsp directories
set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2_saola_1")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32s2)
project(cdc_msc_freertos)

View File

@ -0,0 +1,4 @@
CONFIG_IDF_CMAKE=y
CONFIG_IDF_TARGET="esp32s2"
CONFIG_IDF_TARGET_ESP32S2=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y

View File

@ -0,0 +1,30 @@
idf_component_register(SRCS "main.c" "usb_descriptors.c" "msc_disk.c"
INCLUDE_DIRS "."
REQUIRES freertos soc)
target_compile_options(${COMPONENT_TARGET} PUBLIC
"-DCFG_TUSB_MCU=OPT_MCU_ESP32S2"
)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_include_directories(${COMPONENT_TARGET} PUBLIC
"${FREERTOS_ORIG_INCLUDE_PATH}"
"${TOP}/hw"
"${TOP}/src"
)
target_sources(${COMPONENT_TARGET} PUBLIC
"${TOP}/src/tusb.c"
"${TOP}/src/common/tusb_fifo.c"
"${TOP}/src/device/usbd.c"
"${TOP}/src/device/usbd_control.c"
"${TOP}/src/class/cdc/cdc_device.c"
"${TOP}/src/class/dfu/dfu_rt_device.c"
"${TOP}/src/class/hid/hid_device.c"
"${TOP}/src/class/midi/midi_device.c"
"${TOP}/src/class/msc/msc_device.c"
"${TOP}/src/class/net/net_device.c"
"${TOP}/src/class/usbtmc/usbtmc_device.c"
"${TOP}/src/class/vendor/vendor_device.c"
"${TOP}/src/portable/espressif/esp32s2/dcd_esp32s2.c"
)

View File

@ -52,49 +52,64 @@ enum {
};
// static timer
StaticTimer_t static_blink;
TimerHandle_t blink_tm;
StaticTimer_t blinky_tmdef;
TimerHandle_t blinky_tm;
// static task for usbd
#define USBD_STACK_SIZE 150
StackType_t stack_usbd[USBD_STACK_SIZE];
StaticTask_t static_task_usbd;
// Increase stack size when debug log is enabled
#if CFG_TUSB_DEBUG
#define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE)
#else
#define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
StackType_t usb_device_stack[USBD_STACK_SIZE];
StaticTask_t usb_device_taskdef;
// static task for cdc
#define CDC_STACK_SZIE 128
StackType_t stack_cdc[CDC_STACK_SZIE];
StaticTask_t static_task_cdc;
#define CDC_STACK_SZIE configMINIMAL_STACK_SIZE
StackType_t cdc_stack[CDC_STACK_SZIE];
StaticTask_t cdc_taskdef;
void led_blinky_cb(TimerHandle_t xTimer);
void usb_device_task(void* param);
void cdc_task(void* params);
/*------------- MAIN -------------*/
//--------------------------------------------------------------------+
// Main
//--------------------------------------------------------------------+
int main(void)
{
board_init();
// soft timer for blinky
blink_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &static_blink);
xTimerStart(blink_tm, 0);
tusb_init();
// soft timer for blinky
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTimerStart(blinky_tm, 0);
// Create a task for tinyusb device stack
(void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, stack_usbd, &static_task_usbd);
(void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
// Create task
#if CFG_TUD_CDC
(void) xTaskCreateStatic( cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, stack_cdc, &static_task_cdc);
#endif
// Create CDC task
(void) xTaskCreateStatic( cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
// skip starting scheduler (and return) for ESP32-S2
#if CFG_TUSB_MCU != OPT_MCU_ESP32S2
vTaskStartScheduler();
NVIC_SystemReset();
return 0;
#endif
}
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
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)
@ -116,13 +131,13 @@ void usb_device_task(void* param)
// Invoked when device is mounted
void tud_mount_cb(void)
{
xTimerChangePeriod(blink_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
xTimerChangePeriod(blink_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0);
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0);
}
// Invoked when usb bus is suspended
@ -131,20 +146,18 @@ void tud_umount_cb(void)
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
xTimerChangePeriod(blink_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0);
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0);
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
xTimerChangePeriod(blink_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
}
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
#if CFG_TUD_CDC
void cdc_task(void* params)
{
(void) params;
@ -173,7 +186,8 @@ void cdc_task(void* params)
}
}
taskYIELD();
// For ESP32-S2 this delay is essential to allow idle how to run and reset wdt
vTaskDelay(pdMS_TO_TICKS(10));
}
}
@ -186,7 +200,7 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
if ( dtr && rts )
{
// print initial message when connected
tud_cdc_write_str("\r\nTinyUSB CDC MSC HID device with FreeRTOS example\r\n");
tud_cdc_write_str("\r\nTinyUSB CDC MSC device with FreeRTOS example\r\n");
}
}
@ -196,8 +210,6 @@ void tud_cdc_rx_cb(uint8_t itf)
(void) itf;
}
#endif // CFG_TUD_CDC
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+

View File

@ -56,7 +56,6 @@ void hid_task(void);
int main(void)
{
board_init();
tusb_init();
while (1)

View File

@ -0,0 +1,15 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# TOP is absolute path to root directory of TinyUSB git repo
set(TOP "../../..")
get_filename_component(TOP "${TOP}" REALPATH)
# Add example src and bsp directories
set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2_saola_1")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32s2)
project(hid_composite_freertos)

View File

@ -0,0 +1,27 @@
include ../../../tools/top.mk
include ../../make.mk
FREERTOS_SRC = lib/FreeRTOS/FreeRTOS/Source
INC += \
src \
$(TOP)/hw \
$(TOP)/$(FREERTOS_SRC)/include \
$(TOP)/$(FREERTOS_SRC)/portable/GCC/$(FREERTOS_PORT)
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
# FreeRTOS source, all files in port folder
SRC_C += \
$(FREERTOS_SRC)/list.c \
$(FREERTOS_SRC)/queue.c \
$(FREERTOS_SRC)/tasks.c \
$(FREERTOS_SRC)/timers.c \
$(subst ../../../,,$(wildcard ../../../$(FREERTOS_SRC)/portable/GCC/$(FREERTOS_PORT)/*.c))
# FreeRTOS (lto + Os) linker issue
LDFLAGS += -Wl,--undefined=vTaskSwitchContext
include ../../rules.mk

View File

@ -0,0 +1,4 @@
CONFIG_IDF_CMAKE=y
CONFIG_IDF_TARGET="esp32s2"
CONFIG_IDF_TARGET_ESP32S2=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y

View File

@ -0,0 +1,30 @@
idf_component_register(SRCS "main.c" "usb_descriptors.c"
INCLUDE_DIRS "."
REQUIRES freertos soc)
target_compile_options(${COMPONENT_TARGET} PUBLIC
"-DCFG_TUSB_MCU=OPT_MCU_ESP32S2"
)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_include_directories(${COMPONENT_TARGET} PUBLIC
"${FREERTOS_ORIG_INCLUDE_PATH}"
"${TOP}/hw"
"${TOP}/src"
)
target_sources(${COMPONENT_TARGET} PUBLIC
"${TOP}/src/tusb.c"
"${TOP}/src/common/tusb_fifo.c"
"${TOP}/src/device/usbd.c"
"${TOP}/src/device/usbd_control.c"
"${TOP}/src/class/cdc/cdc_device.c"
"${TOP}/src/class/dfu/dfu_rt_device.c"
"${TOP}/src/class/hid/hid_device.c"
"${TOP}/src/class/midi/midi_device.c"
"${TOP}/src/class/msc/msc_device.c"
"${TOP}/src/class/net/net_device.c"
"${TOP}/src/class/usbtmc/usbtmc_device.c"
"${TOP}/src/class/vendor/vendor_device.c"
"${TOP}/src/portable/espressif/esp32s2/dcd_esp32s2.c"
)

View File

@ -0,0 +1,233 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html.
*----------------------------------------------------------*/
// for OPT_MCU_
#include "tusb_option.h"
#if CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX || \
CFG_TUSB_MCU == OPT_MCU_LPC15XX || CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || \
CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
CFG_TUSB_MCU == OPT_MCU_LPC40XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
#include "chip.h"
#elif CFG_TUSB_MCU == OPT_MCU_LPC51UXX || CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
CFG_TUSB_MCU == OPT_MCU_LPC55XX
#include "fsl_device_registers.h"
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
#include "nrf.h"
#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 || CFG_TUSB_MCU == OPT_MCU_SAMD51
#include "sam.h"
#elif CFG_TUSB_MCU == OPT_MCU_SAMG
#undef LITTLE_ENDIAN // hack to suppress "LITTLE_ENDIAN" redefined
#include "sam.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F0
#include "stm32f0xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
#include "stm32f2xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
#include "stm32f3xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
#include "stm32f4xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
#include "stm32h7xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
#include "stm32l0xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
#elif CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
#include "fsl_device_registers.h"
#elif CFG_TUSB_MCU == OPT_MCU_NUC120
#include "NUC100Series.h"
#elif CFG_TUSB_MCU == OPT_MCU_NUC121 || CFG_TUSB_MCU == OPT_MCU_NUC126
#include "NuMicro.h"
#elif CFG_TUSB_MCU == OPT_MCU_NUC505
#include "NUC505Series.h"
#else
#error "FreeRTOSConfig.h need to include low level mcu header for configuration"
#endif
extern uint32_t SystemCoreClock;
/* Cortex M23/M33 port configuration. */
#define configENABLE_MPU 0
#define configENABLE_FPU 1
#define configENABLE_TRUSTZONE 0
#define configMINIMAL_SECURE_STACK_SIZE ( 1024 )
#define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configCPU_CLOCK_HZ SystemCoreClock
#define configTICK_RATE_HZ ( 1000 )
#define configMAX_PRIORITIES ( 5 )
#define configMINIMAL_STACK_SIZE ( 128 )
#define configTOTAL_HEAP_SIZE ( 0*1024 ) // dynamic is not used
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configQUEUE_REGISTRY_SIZE 2
#define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 0
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning
#define configCHECK_FOR_STACK_OVERFLOW 2
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 1 // legacy trace
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 2
/* Software timer related definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2)
#define configTIMER_QUEUE_LENGTH 32
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 0
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
#define INCLUDE_xResumeFromISR 0
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 0
#define INCLUDE_xTaskGetCurrentTaskHandle 0
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
#define INCLUDE_pcTaskGetTaskName 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 0
#define INCLUDE_xTimerPendFunctionCall 0
/* Define to trap errors during development. */
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
#define configASSERT(_exp) \
do {\
if ( !(_exp) ) { \
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
if ( (*ARM_CM_DHCSR) & 1UL ) { /* Only halt mcu if debugger is attached */ \
taskDISABLE_INTERRUPTS(); \
__asm("BKPT #0\n"); \
}\
}\
} while(0)
#else
#define configASSERT( x )
#endif
/* FreeRTOS hooks to NVIC vectors */
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#define vPortSVCHandler SVC_Handler
//--------------------------------------------------------------------+
// Interrupt nesting behavior configuration.
//--------------------------------------------------------------------+
/* Cortex-M specific definitions. __NVIC_PRIO_BITS is defined in core_cmx.h */
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#error "This port requires __NVIC_PRIO_BITS to be defined"
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<<configPRIO_BITS) - 1)
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#endif /* __FREERTOS_CONFIG__H */

View File

@ -0,0 +1,95 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "FreeRTOS.h"
#include "task.h"
#include "common/tusb_common.h"
void vApplicationMallocFailedHook(void)
{
taskDISABLE_INTERRUPTS();
TU_ASSERT(false, );
}
void vApplicationStackOverflowHook(xTaskHandle pxTask, signed char *pcTaskName)
{
(void) pxTask;
(void) pcTaskName;
taskDISABLE_INTERRUPTS();
TU_ASSERT(false, );
}
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an
* implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
* used by the Idle task. */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits. */
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
state will be stored. */
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task's stack. */
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configMINIMAL_STACK_SIZE is specified in words, not bytes. */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
* application must provide an implementation of vApplicationGetTimerTaskMemory()
* to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits. */
static StaticTask_t xTimerTaskTCB;
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
/* Pass out a pointer to the StaticTask_t structure in which the Timer
task's state will be stored. */
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task's stack. */
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

View File

@ -0,0 +1,251 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "queue.h"
#include "semphr.h"
#include "bsp/board.h"
#include "tusb.h"
#include "usb_descriptors.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
// static timer
StaticTimer_t blinky_tmdef;
TimerHandle_t blinky_tm;
// static task for usbd
#define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
StackType_t usb_device_stack[USBD_STACK_SIZE];
StaticTask_t usb_device_taskdef;
// static task for hid
#define HID_STACK_SZIE configMINIMAL_STACK_SIZE
StackType_t hid_stack[HID_STACK_SZIE];
StaticTask_t hid_taskdef;
void led_blinky_cb(TimerHandle_t xTimer);
void usb_device_task(void* param);
void hid_task(void* params);
//--------------------------------------------------------------------+
// Main
//--------------------------------------------------------------------+
int main(void)
{
board_init();
tusb_init();
// soft timer for blinky
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTimerStart(blinky_tm, 0);
// Create a task for tinyusb device stack
(void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
// Create HID task
(void) xTaskCreateStatic( hid_task, "hid", HID_STACK_SZIE, NULL, configMAX_PRIORITIES-2, hid_stack, &hid_taskdef);
// skip starting scheduler (and return) for ESP32-S2
#if CFG_TUSB_MCU != OPT_MCU_ESP32S2
vTaskStartScheduler();
NVIC_SystemReset();
return 0;
#endif
}
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
void app_main(void)
{
main();
}
#endif
// USB Device Driver task
// This top level thread process all usb events and invoke callbacks
void usb_device_task(void* param)
{
(void) param;
// RTOS forever loop
while (1)
{
// tinyusb device task
tud_task();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0);
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0);
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
}
//--------------------------------------------------------------------+
// USB HID
//--------------------------------------------------------------------+
void hid_task(void* param)
{
(void) param;
while(1)
{
// Poll every 10ms
vTaskDelay(pdMS_TO_TICKS(10));
uint32_t const btn = board_button_read();
// Remote wakeup
if ( tud_suspended() && btn )
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
/*------------- Mouse -------------*/
if ( tud_hid_ready() )
{
if ( btn )
{
int8_t const delta = 5;
// no button, right + down, no scroll pan
tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
// delay a bit before attempt to send keyboard report
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/*------------- Keyboard -------------*/
if ( tud_hid_ready() )
{
// use to avoid send multiple consecutive zero report for keyboard
static bool has_key = false;
if ( btn )
{
uint8_t keycode[6] = { 0 };
keycode[0] = HID_KEY_A;
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
has_key = true;
}else
{
// send empty key report if previously has key pressed
if (has_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
has_key = false;
}
}
}
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
// TODO not Implemented
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
// TODO set LED based on CAPLOCK, NUMLOCK etc...
(void) report_id;
(void) report_type;
(void) buffer;
(void) bufsize;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinky_cb(TimerHandle_t xTimer)
{
(void) xTimer;
static bool led_state = false;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}

View File

@ -0,0 +1,90 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#else
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#endif
#define CFG_TUSB_OS OPT_OS_FREERTOS
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_BUFSIZE 16
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@ -0,0 +1,169 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// HID Report Descriptor
//--------------------------------------------------------------------+
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(REPORT_ID_MOUSE), )
};
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(void)
{
return desc_hid_report;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum
{
ITF_NUM_HID,
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN)
#define EPNUM_HID 0x81
uint8_t const desc_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_BUFSIZE, 10)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if ( index == 0)
{
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
}else
{
// Convert ASCII string into UTF-16
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
if ( chr_count > 31 ) chr_count = 31;
for(uint8_t i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}

View File

@ -15,22 +15,26 @@ TinyUSB examples includes external repos aka submodules to provide low-level MCU
$ git submodule update --init --recursive
```
It will takes a bit of time due to the number of supported MCUs, luckily we only need to do this once.
It will takes a bit of time due to the number of supported MCUs, luckily we only need to do this once. Or if you only want to test with a specific mcu, you could only update its driver submodule
## Build
[Here is the list of supported Boards](docs/boards.md) that should work out of the box with provided examples.
To build example, go to its folder project then type `make BOARD=[our_board] all` e.g
[Here is the list of supported Boards](docs/boards.md) that should work out of the box with provided examples (hopefully).
To build example, first change directory to example folder.
```
$ cd examples/device/cdc_msc
```
Then compile with `make BOARD=[your_board] all`, for example
```
$ cd examples/device/cdc_msc_hid
$ make BOARD=feather_nrf52840_express all
```
## Flash
`flash` target will use the on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary. We should install those debugger/programmer software in advance. Futhermore, since external jlink can be used with most of the board, there is also `flash-jlink` target for out convenience.
`flash` target will use the on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary. We should install those debugger/programmer software in advance. Furthermore, since external jlink can be used with most of the board, there is also `flash-jlink` target for your convenience.
```
$ make BOARD=feather_nrf52840_express flash

View File

@ -1,6 +1,25 @@
#
# Common make definition for all examples
#
# ---------------------------------------
# Common make rules for all examples
# ---------------------------------------
ifeq ($(CROSS_COMPILE),xtensa-esp32s2-elf-)
# Espressif IDF use CMake build system, this add wrapper target to call idf.py
.PHONY: all clean flash
.DEFAULT_GOAL := all
all:
idf.py -B$(BUILD) -DBOARD=$(BOARD) build
clean:
idf.py -B$(BUILD) clean
flash:
@:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyUSB0)
idf.py -B$(BUILD) -p $(SERIAL) flash
else
# GNU Make build system
# libc
LIBS += -lgcc -lm -lnosys
@ -15,21 +34,22 @@ SRC_C += \
src/common/tusb_fifo.c \
src/device/usbd.c \
src/device/usbd_control.c \
src/class/msc/msc_device.c \
src/class/cdc/cdc_device.c \
src/class/dfu/dfu_rt_device.c \
src/class/hid/hid_device.c \
src/class/midi/midi_device.c \
src/class/msc/msc_device.c \
src/class/net/net_device.c \
src/class/usbtmc/usbtmc_device.c \
src/class/vendor/vendor_device.c \
src/class/net/net_device.c \
src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c
# TinyUSB stack include
INC += $(TOP)/src
#
CFLAGS += $(addprefix -I,$(INC))
# TODO Skip nanolib for MSP430
ifeq ($(BOARD), msp_exp430f5529lp)
LDFLAGS += $(CFLAGS) -fshort-enums -Wl,-T,$(TOP)/$(LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections
else
@ -135,3 +155,5 @@ flash-jlink: $(BUILD)/$(BOARD)-firmware.hex
# flash STM32 MCU using stlink with STM32 Cube Programmer CLI
flash-stlink: $(BUILD)/$(BOARD)-firmware.elf
STM32_Programmer_CLI --connect port=swd --write $< --go
endif # Make target

View File

@ -0,0 +1,16 @@
idf_component_register(SRCS "esp32s2_saola_1.c" "led_strip/src/led_strip_rmt_ws2812.c"
INCLUDE_DIRS "led_strip/include"
PRIV_REQUIRES "driver"
REQUIRES freertos src)
target_compile_options(${COMPONENT_TARGET} PUBLIC
"-DCFG_TUSB_MCU=OPT_MCU_ESP32S2"
"-DCFG_TUSB_OS=OPT_OS_FREERTOS"
)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_include_directories(${COMPONENT_TARGET} PUBLIC
"${FREERTOS_ORIG_INCLUDE_PATH}"
"${TOP}/hw"
"${TOP}/src"
)

View File

@ -0,0 +1,2 @@
# Cross Compiler for ESP32
CROSS_COMPILE = xtensa-esp32s2-elf-

View File

@ -0,0 +1,102 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020, Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "../board.h"
#include "driver/gpio.h"
#include "hal/usb_hal.h"
#include "driver/rmt.h"
#include "led_strip/include/led_strip.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// Note: On the production version (v1.2) WS2812 is connected to GPIO 18,
// however earlier revision v1.1 WS2812 is connected to GPIO 17
#define LED_PIN 18 // v1.2 and later
//#define LED_PIN 17 // v1.1
#define BUTTON_PIN 0
#define BUTTON_STATE_ACTIVE 0
static led_strip_t *strip;
// Initialize on-board peripherals : led, button, uart and USB
void board_init(void)
{
// WS2812 Neopixel driver with RMT peripheral
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(LED_PIN, RMT_CHANNEL_0);
config.clk_div = 2; // set counter clock to 40MHz
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t) config.channel);
strip = led_strip_new_rmt_ws2812(&strip_config);
strip->clear(strip, 100); // off led
// Button
gpio_pad_select_gpio(BUTTON_PIN);
gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode(BUTTON_PIN, BUTTON_STATE_ACTIVE ? GPIO_PULLDOWN_ONLY : GPIO_PULLUP_ONLY);
// USB Controller Hal init
usb_hal_context_t hal = {
.use_external_phy = false // use built-in PHY
};
usb_hal_init(&hal);
}
// Turn LED on or off
void board_led_write(bool state)
{
strip->set_pixel(strip, 0, (state ? 0x88 : 0x00), 0x00, 0x00);
strip->refresh(strip, 100);
}
// Get the current state of button
// a '1' means active (pressed), a '0' means inactive.
uint32_t board_button_read(void)
{
return gpio_get_level(BUTTON_PIN) == BUTTON_STATE_ACTIVE;
}
// Get characters from UART
int board_uart_read(uint8_t* buf, int len)
{
(void) buf; (void) len;
return 0;
}
// Send characters to UART
int board_uart_write(void const * buf, int len)
{
(void) buf; (void) len;
return 0;
}

View File

@ -0,0 +1,126 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief LED Strip Type
*
*/
typedef struct led_strip_s led_strip_t;
/**
* @brief LED Strip Device Type
*
*/
typedef void *led_strip_dev_t;
/**
* @brief Declare of LED Strip Type
*
*/
struct led_strip_s {
/**
* @brief Set RGB for a specific pixel
*
* @param strip: LED strip
* @param index: index of pixel to set
* @param red: red part of color
* @param green: green part of color
* @param blue: blue part of color
*
* @return
* - ESP_OK: Set RGB for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
*/
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
/**
* @brief Refresh memory colors to LEDs
*
* @param strip: LED strip
* @param timeout_ms: timeout value for refreshing task
*
* @return
* - ESP_OK: Refresh successfully
* - ESP_ERR_TIMEOUT: Refresh failed because of timeout
* - ESP_FAIL: Refresh failed because some other error occurred
*
* @note:
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
*/
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Clear LED strip (turn off all LEDs)
*
* @param strip: LED strip
* @param timeout_ms: timeout value for clearing task
*
* @return
* - ESP_OK: Clear LEDs successfully
* - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
* - ESP_FAIL: Clear LEDs failed because some other error occurred
*/
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Free LED strip resources
*
* @param strip: LED strip
*
* @return
* - ESP_OK: Free resources successfully
* - ESP_FAIL: Free resources failed because error occurred
*/
esp_err_t (*del)(led_strip_t *strip);
};
/**
* @brief LED Strip Configuration Type
*
*/
typedef struct {
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
} led_strip_config_t;
/**
* @brief Default configuration for LED strip
*
*/
#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
{ \
.max_leds = number, \
.dev = dev_hdl, \
}
/**
* @brief Install a new ws2812 driver (based on RMT peripheral)
*
* @param config: LED strip configuration
* @return
* LED strip instance or NULL
*/
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,171 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_attr.h"
#include "led_strip.h"
#include "driver/rmt.h"
static const char *TAG = "ws2812";
#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
#define WS2812_T0H_NS (350)
#define WS2812_T0L_NS (1000)
#define WS2812_T1H_NS (1000)
#define WS2812_T1L_NS (350)
#define WS2812_RESET_US (280)
static uint32_t ws2812_t0h_ticks = 0;
static uint32_t ws2812_t1h_ticks = 0;
static uint32_t ws2812_t0l_ticks = 0;
static uint32_t ws2812_t1l_ticks = 0;
typedef struct {
led_strip_t parent;
rmt_channel_t rmt_channel;
uint32_t strip_len;
uint8_t buffer[0];
} ws2812_t;
/**
* @brief Conver RGB data to RMT format.
*
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
*
* @param[in] src: source data, to converted to RMT format
* @param[in] dest: place where to store the convert result
* @param[in] src_size: size of source data
* @param[in] wanted_num: number of RMT items that want to get
* @param[out] translated_size: number of source data that got converted
* @param[out] item_num: number of RMT items which are converted from source data
*/
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
size_t wanted_num, size_t *translated_size, size_t *item_num)
{
if (src == NULL || dest == NULL) {
*translated_size = 0;
*item_num = 0;
return;
}
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
size_t size = 0;
size_t num = 0;
uint8_t *psrc = (uint8_t *)src;
rmt_item32_t *pdest = dest;
while (size < src_size && num < wanted_num) {
for (int i = 0; i < 8; i++) {
// MSB first
if (*psrc & (1 << (7 - i))) {
pdest->val = bit1.val;
} else {
pdest->val = bit0.val;
}
num++;
pdest++;
}
size++;
psrc++;
}
*translated_size = size;
*item_num = num;
}
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
uint32_t start = index * 3;
// In thr order of GRB
ws2812->buffer[start + 0] = green & 0xFF;
ws2812->buffer[start + 1] = red & 0xFF;
ws2812->buffer[start + 2] = blue & 0xFF;
return ESP_OK;
err:
return ret;
}
static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
"transmit RMT samples failed", err, ESP_FAIL);
return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
err:
return ret;
}
static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
// Write zero to turn off all leds
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
return ws2812_refresh(strip, timeout_ms);
}
static esp_err_t ws2812_del(led_strip_t *strip)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
free(ws2812);
return ESP_OK;
}
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
{
led_strip_t *ret = NULL;
STRIP_CHECK(config, "configuration can't be null", err, NULL);
// 24 bits per led
uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
ws2812_t *ws2812 = calloc(1, ws2812_size);
STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
uint32_t counter_clk_hz = 0;
STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
"get rmt counter clock failed", err, NULL);
// ns -> ticks
float ratio = (float)counter_clk_hz / 1e9;
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
// set ws2812 to rmt adapter
rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
ws2812->rmt_channel = (rmt_channel_t)config->dev;
ws2812->strip_len = config->max_leds;
ws2812->parent.set_pixel = ws2812_set_pixel;
ws2812->parent.refresh = ws2812_refresh;
ws2812->parent.clear = ws2812_clear;
ws2812->parent.del = ws2812_del;
return &ws2812->parent;
err:
return ret;
}

View File

@ -106,6 +106,12 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num);
// Wake up host
void dcd_remote_wakeup(uint8_t rhport);
// Connect or disconnect D+/D- line pull-up resistor.
// Defined in dcd source if MCU has internal pull-up.
// Otherwise, may be defined in BSP.
void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+

View File

@ -332,6 +332,7 @@ bool tud_init (void)
// Init device controller driver
dcd_init(TUD_OPT_RHPORT);
tud_connect();
dcd_int_enable(TUD_OPT_RHPORT);
return true;
@ -421,7 +422,7 @@ void tud_task (void)
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const ep_dir = tu_edpt_dir(ep_addr);
TU_LOG2(" Endpoint: 0x%02X, Bytes: %ld\r\n", ep_addr, event.xfer_complete.len);
TU_LOG2(" Endpoint: 0x%02X, Bytes: %u\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
_usbd_dev.ep_status[epnum][ep_dir].busy = false;

View File

@ -65,6 +65,20 @@ static inline bool tud_ready(void)
// Remote wake up host, only if suspended and enabled by host
bool tud_remote_wakeup(void);
static inline bool tud_disconnect(void)
{
TU_VERIFY(dcd_disconnect);
dcd_disconnect(TUD_OPT_RHPORT);
return true;
}
static inline bool tud_connect(void)
{
TU_VERIFY(dcd_connect);
dcd_connect(TUD_OPT_RHPORT);
return true;
}
// Carry out Data and Status stage of control transfer
// - If len = 0, it is equivalent to sending status only
// - If len > wLength : it will be truncated

View File

@ -0,0 +1,740 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 && TUSB_OPT_DEVICE_ENABLED
// Espressif
#include "driver/periph_ctrl.h"
#include "freertos/xtensa_api.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp32s2/rom/gpio.h"
#include "soc/dport_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/usb_periph.h"
#include "device/dcd.h"
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#define USE_SOF 0
// Max number of bi-directional endpoints including EP0
// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0
// We should probably prohibit enabling Endpoint IN > 4 (not done yet)
#define EP_MAX USB_OUT_EP_NUM
// FIFO size in bytes
#define EP_FIFO_SIZE 1024
typedef struct {
uint8_t *buffer;
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
bool short_packet;
} xfer_ctl_t;
static const char *TAG = "TUSB:DCD";
static intr_handle_t usb_ih;
static uint32_t _setup_packet[2];
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
static xfer_ctl_t xfer_status[EP_MAX][2];
// Setup the control endpoint 0.
static void bus_reset(void)
{
for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) {
USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
USB0.dcfg &= ~USB_DEVADDR_M; // reset address
USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M;
USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK;
USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits):
// - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN
//
// - All EP OUT shared a unique OUT FIFO which uses
// * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets).
// * 2 locations for OUT endpoint control words.
// * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
// * 1 location for global NAK (not required/used here).
// * It is recommended to allocate 2 times the largest packet size, therefore
// Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52
USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10,
USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo
USB0.grxfsiz = 52;
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL);
// Ready to receive SETUP packet
USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M;
USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M;
}
static void enum_done_processing(void)
{
ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then");
// On current silicon on the Full Speed core, speed is fixed to Full Speed.
// However, keep for debugging and in case Low Speed is ever supported.
uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V);
// Maximum packet size for EP 0 is set for both directions by writing DIEPCTL
if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz)
USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 64;
xfer_status[0][TUSB_DIR_IN].max_size = 64;
} else {
USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 8;
xfer_status[0][TUSB_DIR_IN].max_size = 8;
}
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
void dcd_init(uint8_t rhport)
{
(void)rhport;
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up");
USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect
// B. Programming DCFG
/* If USB host misbehaves during status portion of control xfer
(non zero-length packet), send STALL back and discard. Full speed. */
USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL
(3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476)
USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON
USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode
USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides
// C. Setting SNAKs, then connect
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
// D. Interruption masking
USB0.gintmsk = 0; //mask all
USB0.gotgint = ~0U; //clear OTG ints
USB0.gintsts = ~0U; //clear pending ints
USB0.gintmsk = USB_MODEMISMSK_M |
#if USE_SOF
USB_SOFMSK_M |
#endif
USB_RXFLVIMSK_M |
USB_ERLYSUSPMSK_M |
USB_USBSUSPMSK_M |
USB_USBRSTMSK_M |
USB_ENUMDONEMSK_M |
USB_RESETDETMSK_M |
USB_DISCONNINTMSK_M;
ESP_LOGV(TAG, "DCD init - Soft CONNECT");
USB0.dctl &= ~USB_SFTDISCON_M; // Connect
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr);
USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S);
// Response with status after changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
(void)rhport;
(void)config_num;
// Nothing to do
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
}
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
USB0.dctl |= USB_SFTDISCON_M;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
USB0.dctl &= ~USB_SFTDISCON_M;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
{
ESP_LOGV(TAG, "DCD endpoint opened");
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 64);
TU_ASSERT(epnum < EP_MAX);
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = desc_edpt->wMaxPacketSize.size;
if (dir == TUSB_DIR_OUT) {
out_ep[epnum].doepctl |= USB_USBACTEP0_M |
desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S |
desc_edpt->wMaxPacketSize.size << USB_MPS0_S;
USB0.daintmsk |= (1 << (16 + epnum));
} else {
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints
// - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1)
// - Offset: GRXFSIZ + 16 + Size*(epnum-1)
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
epnum << USB_D_TXFNUM1_S |
desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
desc_edpt->wMaxPacketSize.size << 0;
USB0.daintmsk |= (1 << (0 + epnum));
// Both TXFD and TXSA are in unit of 32-bit words.
// IN FIFO 0 was configured during enumeration, hence the "+ 16".
uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16;
uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_MAX-1);
uint32_t const fifo_offset = allocated_size + fifo_size*(epnum-1);
// DIEPTXF starts at FIFO #1.
USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset;
}
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if (short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
num_packets, total_bytes);
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them
// here.
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
} else {
// Each complete packet for OUT xfers triggers XFRC.
USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) {
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M);
} else {
// Stop transmitting packets and NAK IN xfers.
in_ep[epnum].diepctl |= USB_DI_SNAK1_M;
while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ;
// Disable the endpoint. Note that both SNAK and STALL are set here.
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M);
while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ;
in_ep[epnum].diepint = USB_D_EPDISBLD0_M;
}
// Flush the FIFO, and wait until we have confirmed it cleared.
USB0.grstctl |= ((epnum - 1) << USB_TXFNUM_S);
USB0.grstctl |= USB_TXFFLSH_M;
while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ;
} else {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) {
out_ep[epnum].doepctl |= USB_STALL0_M;
} else {
// Asserting GONAK is required to STALL an OUT endpoint.
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
// anyway, and it can't be cleared by user code. If this while loop never
// finishes, we have bigger problems than just the stack.
USB0.dctl |= USB_SGOUTNAK_M;
while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ;
// Ditto here- disable the endpoint. Note that only STALL and not SNAK
// is set here.
out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M);
while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ;
out_ep[epnum].doepint = USB_EPDISBLD0_M;
// Allow other OUT endpoints to keep receiving.
USB0.dctl |= USB_CGOUTNAK_M;
}
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
in_ep[epnum].diepctl &= ~USB_D_STALL1_M;
uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M;
}
} else {
out_ep[epnum].doepctl &= ~USB_STALL1_M;
uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M;
}
}
}
/*------------------------------------------------------------------*/
static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size)
{
ESP_EARLY_LOGV(TAG, "USB - receive_packet");
volatile uint32_t *rx_fifo = USB0.fifo[0];
// See above TODO
// uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos;
// xfer->queued_len = xfer->total_len - remaining;
uint16_t remaining = xfer->total_len - xfer->queued_len;
uint16_t to_recv_size;
if (remaining <= xfer->max_size) {
// Avoid buffer overflow.
to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
} else {
// Room for full packet, choose recv_size based on what the microcontroller
// claims.
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
uint8_t to_recv_rem = to_recv_size % 4;
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
// Do not assume xfer buffer is aligned.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to collect.
if (to_recv_size >= 4) {
for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
uint32_t tmp = (*rx_fifo);
base[i] = tmp & 0x000000FF;
base[i + 1] = (tmp & 0x0000FF00) >> 8;
base[i + 2] = (tmp & 0x00FF0000) >> 16;
base[i + 3] = (tmp & 0xFF000000) >> 24;
}
}
// Do not read invalid bytes from RX FIFO.
if (to_recv_rem != 0) {
uint32_t tmp = (*rx_fifo);
uint8_t *last_32b_bound = base + to_recv_size_aligned;
last_32b_bound[0] = tmp & 0x000000FF;
if (to_recv_rem > 1) {
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
}
if (to_recv_rem > 2) {
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
}
}
xfer->queued_len += xfer_size;
// Per USB spec, a short OUT packet (including length 0) is always
// indicative of the end of a transfer (at least for ctl, bulk, int).
xfer->short_packet = (xfer_size < xfer->max_size);
}
static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num)
{
ESP_EARLY_LOGV(TAG, "USB - transmit_packet");
volatile uint32_t *tx_fifo = USB0.fifo[fifo_num];
uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S;
xfer->queued_len = xfer->total_len - remaining;
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
uint8_t to_xfer_rem = to_xfer_size % 4;
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
// Buffer might not be aligned to 32b, so we need to force alignment
// by copying to a temp var.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to send off.
if (to_xfer_size >= 4) {
for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
uint32_t tmp = base[i] | (base[i + 1] << 8) |
(base[i + 2] << 16) | (base[i + 3] << 24);
(*tx_fifo) = tmp;
}
}
// Do not read beyond end of buffer if not divisible by 4.
if (to_xfer_rem != 0) {
uint32_t tmp = 0;
uint8_t *last_32b_bound = base + to_xfer_size_aligned;
tmp |= last_32b_bound[0];
if (to_xfer_rem > 1) {
tmp |= (last_32b_bound[1] << 8);
}
if (to_xfer_rem > 2) {
tmp |= (last_32b_bound[2] << 16);
}
(*tx_fifo) = tmp;
}
}
static void read_rx_fifo(void)
{
// Pop control word off FIFO (completed xfers will have 2 control words,
// we only pop one ctl word each interrupt).
uint32_t const ctl_word = USB0.grxstsp;
uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S;
uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S;
uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S;
switch (pktsts) {
case 0x01: // Global OUT NAK (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK");
break;
case 0x02: { // Out packet recvd
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet");
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
receive_packet(xfer, bcnt);
}
break;
case 0x03: // Out packet done (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done");
break;
case 0x04: // Step 2: Setup transaction completed (Interrupt)
// After this event, OEPINT interrupt will occur with SETUP bit set
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done");
USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M;
break;
case 0x06: { // Step1: Setup data packet received
volatile uint32_t *rx_fifo = USB0.fifo[0];
// We can receive up to three setup packets in succession, but
// only the last one is valid. Therefore we just overwrite it
_setup_packet[0] = (*rx_fifo);
_setup_packet[1] = (*rx_fifo);
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]);
}
break;
default: // Invalid, do something here, like breakpoint?
TU_BREAKPOINT();
break;
}
}
static void handle_epout_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DOEPINTx is cleared.
// DOEPINT will be cleared when DAINT's out bits are cleared.
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
if (USB0.daint & (1 << (16 + n))) {
// SETUP packet Setup Phase done.
if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) {
USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear
dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
}
// OUT XFER complete (single packet).q
if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)");
USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M;
// Transfer complete if short packet or total len is transferred
if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
xfer->short_packet = false;
dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
} else {
// Schedule another packet to be received.
USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
}
}
}
}
static void handle_epin_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DIEPINTx is cleared.
// IEPINT will be cleared when DAINT's out bits are cleared.
for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) {
xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
if (USB0.daint & (1 << (0 + n))) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n);
// IN XFER complete (entire xfer).
if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!");
USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M;
USB0.dtknqr4_fifoemptymsk &= ~(1 << n); // Turn off TXFE b/c xfer inactive.
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
// XFER FIFO empty
if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!");
USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M;
transmit_packet(xfer, &USB0.in_ep_reg[n], n);
}
}
}
}
static void dcd_int_handler(void* arg)
{
(void) arg;
const uint32_t int_status = USB0.gintsts;
//const uint32_t int_msk = USB0.gintmsk;
if (int_status & USB_DISCONNINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected");
USB0.gintsts = USB_DISCONNINT_M;
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
}
if (int_status & USB_USBRST_M) {
// start of reset
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
USB0.gintsts = USB_USBRST_M;
bus_reset();
}
if (int_status & USB_RESETDET_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend");
USB0.gintsts = USB_RESETDET_M;
bus_reset();
}
if (int_status & USB_ENUMDONE_M) {
// ENUMDNE detects speed of the link. For full-speed, we
// always expect the same value. This interrupt is considered
// the end of reset.
USB0.gintsts = USB_ENUMDONE_M;
enum_done_processing();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
}
#if USE_SOF
if (int_status & USB_SOF_M) {
USB0.gintsts = USB_SOF_M;
dcd_event_bus_signal(0, DCD_EVENT_SOF, true); // do nothing actually
}
#endif
if (int_status & USB_RXFLVI_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!");
USB0.gintsts = USB_RXFLVI_M;
// disable RXFLVI interrupt until we read data from FIFO
USB0.gintmsk &= ~USB_RXFLVIMSK_M;
read_rx_fifo();
// re-enable RXFLVI
USB0.gintmsk |= USB_RXFLVIMSK_M;
}
// OUT endpoint interrupt handling.
if (int_status & USB_OEPINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!");
handle_epout_ints();
}
// IN endpoint interrupt handling.
if (int_status & USB_IEPINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!");
handle_epin_ints();
}
// Without handling
USB0.gintsts |= USB_CURMOD_INT_M |
USB_MODEMIS_M |
USB_OTGINT_M |
USB_NPTXFEMP_M |
USB_GINNAKEFF_M |
USB_GOUTNAKEFF |
USB_ERLYSUSP_M |
USB_USBSUSP_M |
USB_ISOOUTDROP_M |
USB_EOPF_M |
USB_EPMIS_M |
USB_INCOMPISOIN_M |
USB_INCOMPIP_M |
USB_FETSUSP_M |
USB_PTXFEMP_M;
}
void dcd_int_enable (uint8_t rhport)
{
(void) rhport;
esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) dcd_int_handler, NULL, &usb_ih);
}
void dcd_int_disable (uint8_t rhport)
{
(void) rhport;
esp_intr_free(usb_ih);
}
#endif // OPT_MCU_ESP32S2

View File

@ -154,10 +154,23 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
USB->DEVICE.CTRLB.bit.UPRSM = 1;
}
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
USB->DEVICE.CTRLB.reg |= USB_DEVICE_CTRLB_DETACH;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
(void) rhport;
USB->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/

View File

@ -237,6 +237,20 @@ void dcd_remote_wakeup(uint8_t rhport)
// We may manually raise DCD_EVENT_RESUME event here
}
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
NRF_USBD->USBPULLUP = 0;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
(void) rhport;
NRF_USBD->USBPULLUP = 1;
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+

View File

@ -46,22 +46,22 @@ struct usbdcd_driver_s
static struct usbdcd_driver_s usbdcd_driver;
static struct usbdev_s *usbdev;
static int dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void dcd_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static int dcd_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen);
static void dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void dcd_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static int _dcd_bind (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void _dcd_unbind (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static int _dcd_setup (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen);
static void _dcd_disconnect (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void _dcd_suspend (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static void _dcd_resume (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
static const struct usbdevclass_driverops_s g_driverops =
{
dcd_bind, /* bind */
dcd_unbind, /* unbind */
dcd_setup, /* setup */
dcd_disconnect, /* disconnect */
dcd_suspend, /* suspend */
dcd_resume, /* resume */
_dcd_bind, /* bind */
_dcd_unbind, /* unbind */
_dcd_setup, /* setup */
_dcd_disconnect, /* disconnect */
_dcd_suspend, /* suspend */
_dcd_resume, /* resume */
};
static void usbdcd_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
@ -86,7 +86,7 @@ static void usbdcd_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_r
}
}
static int dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
static int _dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
{
(void) driver;
@ -111,13 +111,13 @@ static int dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s
return 0;
}
static void dcd_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
static void _dcd_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
{
(void) driver;
(void) dev;
}
static int dcd_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
static int _dcd_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen)
{
(void) driver;
@ -130,7 +130,7 @@ static int dcd_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_
return 0;
}
static void dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
static void _dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
{
(void) driver;
@ -138,7 +138,7 @@ static void dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct u
DEV_CONNECT(dev);
}
static void dcd_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
static void _dcd_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
{
(void) driver;
(void) dev;
@ -146,7 +146,7 @@ static void dcd_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbd
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
static void dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
static void _dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
{
(void) driver;
(void) dev;

View File

@ -239,17 +239,29 @@ void dcd_init (uint8_t rhport)
}
USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_SOFM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
dcd_handle_bus_reset();
// And finally enable pull-up, which may trigger the RESET IRQ if the host is connected.
// (if this MCU has an internal pullup)
#if defined(USB_BCDR_DPPU)
USB->BCDR |= USB_BCDR_DPPU;
#else
// FIXME: callback to the user to ask them to twiddle a GPIO to disable/enable D+???
#endif
// Data-line pull-up is left disconnected.
}
// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
#if defined(USB_BCDR_DPPU)
// Disable internal D+ PU
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
USB->BCDR &= ~(USB_BCDR_DPPU);
}
// Enable internal D+ PU
void dcd_connect(uint8_t rhport)
{
(void) rhport;
USB->BCDR |= USB_BCDR_DPPU;
}
#endif
// Enable device interrupt
void dcd_int_enable (uint8_t rhport)
{

View File

@ -45,6 +45,20 @@ void dcd_init (uint8_t rhport)
(void) rhport;
}
#if HAS_INTERNAL_PULLUP
// Enable internal D+/D- pullup
void dcd_connect(uint8_t rhport) TU_ATTR_WEAK
{
(void) rhport;
}
// Disable internal D+/D- pullup
void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK
{
(void) rhport;
}
#endif
// Enable device interrupt
void dcd_int_enable (uint8_t rhport)
{

View File

@ -96,9 +96,9 @@ void tu_print_mem(void const *buf, uint16_t count, uint8_t indent)
char format[] = "%00lX";
format[2] += 2*size;
const uint8_t item_per_line = 16 / size;
const uint8_t item_per_line = 16 / size;
for(uint32_t i=0; i<count; i++)
for(uint16_t i=0; i<count; i++)
{
uint32_t value=0;
@ -115,7 +115,7 @@ void tu_print_mem(void const *buf, uint16_t count, uint8_t indent)
for(uint8_t s=0; s < indent; s++) tu_printf(" ");
// print offset or absolute address
tu_printf("%03lX: ", 16*i/item_per_line);
tu_printf("%03X: ", 16*i/item_per_line);
}
memcpy(&value, buf8, size);

View File

@ -199,6 +199,7 @@ void setUp(void)
if ( !tusb_inited() )
{
dcd_init_Expect(rhport);
dcd_connect_Expect(rhport);
tusb_init();
}

View File

@ -127,6 +127,7 @@ void setUp(void)
{
mscd_init_Expect();
dcd_init_Expect(rhport);
dcd_connect_Expect(rhport);
tusb_init();
}
}

View File

@ -11,6 +11,9 @@ exit_status = 0
total_time = time.monotonic()
build_format = '| {:23} | {:30} | {:9} | {:7} | {:6} | {:6} |'
build_separator = '-' * 100
# 1st Argument is Example, build all examples if not existed
all_examples = []
if len(sys.argv) > 1:
@ -39,7 +42,8 @@ def build_example(example, board):
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def build_size(example, board):
elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
#elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
elf_file = 'examples/device/{}/_build/build-{}/*.elf'.format(example, board)
size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
size_list = size_output.split('\n')[1].split('\t')
flash_size = int(size_list[0])
@ -49,16 +53,22 @@ def build_size(example, board):
def skip_example(example, board):
ex_dir = 'examples/device/' + example
board_mk = 'hw/bsp/{}/board.mk'.format(board)
for skip_file in glob.iglob(ex_dir + '/.skip.MCU_*'):
mcu_cflag = '-DCFG_TUSB_MCU=OPT_' + os.path.basename(skip_file).split('.')[2]
with open(board_mk) as mk:
if mcu_cflag in mk.read():
with open(board_mk) as mk:
mk_contents = mk.read()
# Skip all ESP32-S2 board for CI
if 'CROSS_COMPILE = xtensa-esp32s2-elf-' in mk_contents:
return 1
# Skip if CFG_TUSB_MCU in board.mk to match skip file
for skip_file in glob.iglob(ex_dir + '/.skip.MCU_*'):
mcu_cflag = '-DCFG_TUSB_MCU=OPT_' + os.path.basename(skip_file).split('.')[2]
if mcu_cflag in mk_contents:
return 1
return 0
build_format = '| {:20} | {:30} | {:9} | {:5} | {:6} | {:6} |'
build_separator = '-' * 95
print(build_separator)
print(build_format.format('Example', 'Board', 'Result', 'Time', 'Flash', 'SRAM'))
@ -93,10 +103,7 @@ for example in all_examples:
if build_result.returncode != 0:
print(build_result.stdout.decode("utf-8"))
# FreeRTOS example
# example = 'cdc_msc_hid_freertos'
# board = 'pca10056'
# build_example(example, board)
total_time = time.monotonic() - total_time
print(build_separator)

101
tools/build_esp32s.py Normal file
View File

@ -0,0 +1,101 @@
import os
import glob
import sys
import subprocess
import time
success_count = 0
fail_count = 0
skip_count = 0
exit_status = 0
total_time = time.monotonic()
build_format = '| {:23} | {:30} | {:9} | {:7} | {:6} | {:6} |'
build_separator = '-' * 100
# 1st Argument is Example, build all examples if not existed
all_examples = []
if len(sys.argv) > 1:
all_examples.append(sys.argv[1])
else:
for entry in os.scandir("examples/device"):
# Only includes example with CMakeLists.txt for esp32s
if entry.is_dir() and os.path.exists(entry.path + "/CMakeLists.txt"):
all_examples.append(entry.name)
all_examples.sort()
# 2nd Argument is Board, build all boards if not existed
all_boards = []
if len(sys.argv) > 2:
all_boards.append(sys.argv[2])
else:
for entry in os.scandir("hw/bsp"):
if entry.is_dir():
with open(entry.path + '/board.mk') as mk:
# Only includes ESP32-S2 board
if 'CROSS_COMPILE = xtensa-esp32s2-elf-' in mk.read():
all_boards.append(entry.name)
all_boards.sort()
def build_example(example, board):
subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return subprocess.run("make -j 4 -C examples/device/{} BOARD={} all".format(example, board), shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def build_size(example, board):
#elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
elf_file = 'examples/device/{}/_build/build-{}/*.elf'.format(example, board)
size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
size_list = size_output.split('\n')[1].split('\t')
flash_size = int(size_list[0])
sram_size = int(size_list[1]) + int(size_list[2])
return (flash_size, sram_size)
def skip_example(example, board):
return 0
print(build_separator)
print(build_format.format('Example', 'Board', 'Result', 'Time', 'Flash', 'SRAM'))
print(build_separator)
for example in all_examples:
for board in all_boards:
start_time = time.monotonic()
flash_size = "-"
sram_size = "-"
# Check if board is skipped
if skip_example(example, board):
success = "\033[33mskipped\033[0m "
skip_count += 1
print(build_format.format(example, board, success, '-', flash_size, sram_size))
else:
build_result = build_example(example, board)
if build_result.returncode == 0:
success = "\033[32msucceeded\033[0m"
success_count += 1
(flash_size, sram_size) = build_size(example, board)
else:
exit_status = build_result.returncode
success = "\033[31mfailed\033[0m "
fail_count += 1
build_duration = time.monotonic() - start_time
print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
if build_result.returncode != 0:
print(build_result.stdout.decode("utf-8"))
total_time = time.monotonic() - total_time
print(build_separator)
print("Build Sumamary: {} \033[32msucceeded\033[0m, {} \033[31mfailed\033[0m, {} \033[33mskipped\033[0m and took {:.2f}s".format(success_count, fail_count, skip_count, total_time))
print(build_separator)
sys.exit(exit_status)