Consolidate FreeRTOS host examples

Merge paired host FreeRTOS/no-OS examples into their base directories.
This commit is contained in:
Zixun LI
2026-06-29 14:26:12 +02:00
committed by HiFiPhile
parent 5a46cd91f7
commit b1db17ce18
43 changed files with 486 additions and 2368 deletions

View File

@ -9,13 +9,11 @@ family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR})
set(EXAMPLE_LIST
bare_api
cdc_msc_hid
cdc_msc_hid_freertos
device_info
hid_controller
midi_rx
midi2_host
msc_file_explorer
msc_file_explorer_freertos
)
foreach (example ${EXAMPLE_LIST})

View File

@ -27,6 +27,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# Configure compilation flags and libraries for the selected RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_host_example(${PROJECT_NAME} noos)
family_configure_host_example(${PROJECT_NAME} ${RTOS})

View File

@ -52,7 +52,7 @@ make BOARD=raspberry_pi_pico all
## FreeRTOS variant
A FreeRTOS build is in `examples/host/cdc_msc_hid_freertos` — identical CDC/MSC/HID host behavior, running the host stack in a FreeRTOS task with a software-timer LED.
Build this example with `RTOS=freertos` / `-DRTOS=freertos` for the FreeRTOS variant. It keeps the same CDC/MSC/HID host behavior, running the host stack in a FreeRTOS task with a software-timer LED.
## How to use

View File

@ -1,3 +1,4 @@
family:espressif
family:hpmicro
family:samd21
family:samd5x_e5x

View File

@ -1 +1,11 @@
board:lpcxpresso54114
rtos:noos family:espressif
rtos:noos board:lpcxpresso54114
rtos:freertos mcu:CH32F20X
rtos:freertos mcu:CH32V20X
rtos:freertos mcu:FT90X
rtos:freertos mcu:KINETIS_KL
rtos:freertos mcu:RAXXX
rtos:freertos mcu:RP2040
rtos:freertos board:lpcxpresso54114
rtos:freertos family:hpmicro

View File

@ -27,8 +27,16 @@
#define TUSB_TINYUSB_EXAMPLES_APP_H
#include <stdio.h>
#include "tusb.h"
void msc_app_init(void);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
void cdc_app_init(void);
void hid_app_init(void);
#else
void cdc_app_task(void);
void hid_app_task(void);
#endif
#endif

View File

@ -28,6 +28,82 @@
#include "bsp/board_api.h"
#include "app.h"
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#ifdef ESP_PLATFORM
#define CDC_STACK_SIZE 2048
#else
#define CDC_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
#if configSUPPORT_STATIC_ALLOCATION
StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
#endif
static void cdc_app_task(void* param);
void cdc_app_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
(void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
#else
(void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
#endif
}
static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
if (ch <= 0) {
break;
}
buf[count] = (uint8_t) ch;
count++;
}
return count;
}
static void cdc_app_task(void* param) {
(void) param;
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
while (1) {
uint32_t count = console_read(buf, bufsize);
buf[count] = 0;
if (count) {
// loop over all mounted interfaces
for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
if (tuh_cdc_mounted(idx)) {
// console --> cdc interfaces
tuh_cdc_write(idx, buf, count);
tuh_cdc_write_flush(idx);
}
}
}
vTaskDelay(1);
}
}
// Invoked when received new data
void tuh_cdc_rx_cb(uint8_t idx) {
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
// forward cdc interfaces -> console
uint32_t count = tuh_cdc_read(idx, buf, bufsize);
buf[count] = 0;
printf("%s", (char *) buf);
}
#else
static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
@ -81,6 +157,7 @@ void cdc_app_task(void) {
tuh_cdc_write_flush(idx);
}
#endif
//--------------------------------------------------------------------+
// TinyUSB callbacks
@ -96,7 +173,7 @@ void tuh_cdc_mount_cb(uint8_t idx) {
itf_info.desc.bInterfaceNumber);
// If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
// while eneumerating new cdc device
// while enumerating new cdc device
cdc_line_coding_t line_coding = {0};
if (tuh_cdc_get_line_coding_local(idx, &line_coding)) {
printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);

View File

@ -43,9 +43,15 @@ static void process_kbd_report(hid_keyboard_report_t const *report);
static void process_mouse_report(hid_mouse_report_t const *report);
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
void hid_app_init(void) {
// nothing to do
}
#else
void hid_app_task(void) {
// nothing to do
}
#endif
//--------------------------------------------------------------------+
// TinyUSB Callbacks

View File

@ -31,10 +31,39 @@
#include "tusb.h"
#include "app.h"
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#ifdef ESP_PLATFORM
#include "freertos/timers.h"
#define USBH_STACK_SIZE 4096
#else
#include "timers.h"
// Increase stack size when debug log is enabled
#define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
enum {
BLINK_MOUNTED = 1000,
};
#if configSUPPORT_STATIC_ALLOCATION
static StaticTimer_t blinky_tmdef;
static StackType_t usb_host_stack[USBH_STACK_SIZE];
static StaticTask_t usb_host_taskdef;
#endif
static TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
#else
void led_blinking_task(void);
#endif
/*------------- MAIN -------------*/
int main(void) {
@ -42,6 +71,23 @@ int main(void) {
printf("TinyUSB Host CDC MSC HID Example\r\n");
#if CFG_TUSB_OS == OPT_OS_FREERTOS
// Create soft timer for blinky, task for tinyusb stack
#if configSUPPORT_STATIC_ALLOCATION
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_host_stack, &usb_host_taskdef);
#else
blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb);
xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL);
#endif
xTimerStart(blinky_tm, 0);
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
#else
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
@ -51,11 +97,11 @@ int main(void) {
board_init_after_tusb();
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E use MAX3421E's GPIO0 for VBUS enable
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
enum { IOPINS1_ADDR = 20u << 3, /* 0xA0 */ };
tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
#endif
#endif
while (1) {
// tinyusb host task
@ -65,8 +111,54 @@ int main(void) {
cdc_app_task();
hid_app_task();
}
#endif
return 0;
}
#ifdef ESP_PLATFORM
void app_main(void) {
main();
}
#endif
#if CFG_TUSB_OS == OPT_OS_FREERTOS
// USB Host task
// This top level thread processes all USB events and invokes callbacks.
static void usb_host_task(void *param) {
(void) param;
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_AUTO
};
if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
printf("Failed to init USB Host Stack\r\n");
vTaskSuspend(NULL);
}
board_init_after_tusb();
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
enum { IOPINS1_ADDR = 20u << 3, /* 0xA0 */ };
tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
#endif
cdc_app_init();
hid_app_init();
msc_app_init();
// RTOS forever loop
while (1) {
// put this thread to waiting state until there is new events
tuh_task();
}
}
#endif
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
@ -85,6 +177,15 @@ void tuh_umount_cb(uint8_t dev_addr) {
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static 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
}
#else
void led_blinking_task(void) {
const uint32_t interval_ms = 1000;
static uint32_t start_ms = 0;
@ -93,10 +194,11 @@ void led_blinking_task(void) {
// Blink every interval ms
if (tusb_time_millis_api() - start_ms < interval_ms) {
return;// not enough time
return; // not enough time
}
start_ms += interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}
#endif

View File

@ -25,11 +25,19 @@
#include <stdio.h>
#include "tusb.h"
#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
static scsi_inquiry_resp_t inquiry_resp;
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry);
} scsi_resp;
void msc_app_init(void) {
// nothing to do
}
static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data) {
msc_cbw_t const* cbw = cb_data->cbw;
@ -41,7 +49,7 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
}
// Print out Vendor ID, Product ID and Rev
printf("%.8s %.16s %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
// Get capacity of device
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
@ -58,7 +66,7 @@ void tuh_msc_mount_cb(uint8_t dev_addr) {
printf("A MassStorage device is mounted\r\n");
uint8_t const lun = 0;
tuh_msc_inquiry(dev_addr, lun, &inquiry_resp, inquiry_complete_cb, 0);
tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
}
void tuh_msc_umount_cb(uint8_t dev_addr) {

View File

@ -43,6 +43,11 @@
#define CFG_TUSB_OS OPT_OS_NONE
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif

View File

@ -1,32 +0,0 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(cdc_msc_hid_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_host_example(${PROJECT_NAME} freertos)

View File

@ -1,6 +0,0 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}

View File

@ -1,17 +0,0 @@
RTOS = freertos
include ../../../hw/bsp/family_support.mk
INC += \
src \
# Example source
EXAMPLE_SOURCE = \
src/cdc_app.c \
src/hid_app.c \
src/main.c \
src/msc_app.c \
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
include ../../../hw/bsp/family_rules.mk

View File

@ -1,28 +0,0 @@
family:espressif
family:samd21
family:samd5x_e5x
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
mcu:LPC54
mcu:LPC55
mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
mcu:MIMXRT1XXX
mcu:MSP432E4
mcu:RW61X
mcu:RX65X
mcu:STM32C0
mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
mcu:STM32G0
mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
mcu:STM32U3
mcu:STM32U5

View File

@ -1,3 +0,0 @@
mcu:CH32F20X
board:lpcxpresso54114
mcu:FT90X

View File

@ -1,35 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2025 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.
*/
#ifndef TUSB_TINYUSB_EXAMPLES_APP_H
#define TUSB_TINYUSB_EXAMPLES_APP_H
#include <stdio.h>
void cdc_app_init(void);
void hid_app_init(void);
void msc_app_init(void);
#endif

View File

@ -1,134 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022, 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 "tusb.h"
#include "bsp/board_api.h"
#include "app.h"
#ifdef ESP_PLATFORM
#define CDC_STACK_SIZE 2048
#else
#define CDC_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
#endif
static void cdc_app_task(void* param);
void cdc_app_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
(void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
#else
(void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
#endif
}
// helper
static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
if (ch <= 0) {
break;
}
buf[count] = (uint8_t) ch;
count++;
}
return count;
}
static void cdc_app_task(void* param) {
(void) param;
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
while (1) {
uint32_t count = console_read(buf, bufsize);
buf[count] = 0;
if (count) {
// loop over all mounted interfaces
for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
if (tuh_cdc_mounted(idx)) {
// console --> cdc interfaces
tuh_cdc_write(idx, buf, count);
tuh_cdc_write_flush(idx);
}
}
}
vTaskDelay(1);
}
}
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
// Invoked when received new data
void tuh_cdc_rx_cb(uint8_t idx) {
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
// forward cdc interfaces -> console
uint32_t count = tuh_cdc_read(idx, buf, bufsize);
buf[count] = 0;
printf("%s", (char *) buf);
}
void tuh_cdc_mount_cb(uint8_t idx) {
tuh_itf_info_t itf_info = { 0 };
tuh_cdc_itf_get_info(idx, &itf_info);
printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
// CFG_TUH_CDC_LINE_CODING_ON_ENUM must be defined for line coding is set by tinyusb in enumeration
// otherwise you need to call tuh_cdc_set_line_coding() first
cdc_line_coding_t line_coding = { 0 };
if (tuh_cdc_get_local_line_coding(idx, &line_coding)) {
printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
}
#endif
}
void tuh_cdc_umount_cb(uint8_t idx) {
tuh_itf_info_t itf_info = { 0 };
tuh_cdc_itf_get_info(idx, &itf_info);
printf("CDC Interface is unmounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
}

View File

@ -1,269 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021, Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "bsp/board_api.h"
#include "tusb.h"
#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// If your host terminal support ansi escape code such as TeraTerm
// it can be use to simulate mouse cursor movement within terminal
#define USE_ANSI_ESCAPE 0
#define MAX_REPORT 4
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
// Each HID instance can has multiple reports
static struct {
uint8_t report_count;
tuh_hid_report_info_t report_info[MAX_REPORT];
} hid_info[CFG_TUH_HID];
static void process_kbd_report(hid_keyboard_report_t const *report);
static void process_mouse_report(hid_mouse_report_t const *report);
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len);
void hid_app_init(void) {
// nothing to do
}
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
// Interface protocol (hid_interface_protocol_enum_t)
const char *protocol_str[] = { "None", "Keyboard", "Mouse" };
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
// By default host stack will use activate boot protocol on supported interface.
// Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
if (itf_protocol == HID_ITF_PROTOCOL_NONE) {
hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT,
desc_report, desc_len);
printf("HID has %u reports \r\n", hid_info[instance].report_count);
}
// request to receive report
// tuh_hid_report_received_cb() will be invoked when report is available
if (!tuh_hid_receive_report(dev_addr, instance)) {
printf("Error: cannot request to receive report\r\n");
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD:
TU_LOG2("HID receive boot keyboard report\r\n");
process_kbd_report((hid_keyboard_report_t const *) report);
break;
case HID_ITF_PROTOCOL_MOUSE:
TU_LOG2("HID receive boot mouse report\r\n");
process_mouse_report((hid_mouse_report_t const *) report);
break;
default:
// Generic report requires matching ReportID and contents with previous parsed report info
process_generic_report(dev_addr, instance, report, len);
break;
}
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
printf("Error: cannot request to receive report\r\n");
}
}
//--------------------------------------------------------------------+
// Keyboard
//--------------------------------------------------------------------+
// look up new key in previous keys
static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode) {
for (uint8_t i = 0; i < 6; i++) {
if (report->keycode[i] == keycode) return true;
}
return false;
}
static void process_kbd_report(hid_keyboard_report_t const *report) {
static hid_keyboard_report_t prev_report = { 0, 0, { 0 } }; // previous report to check key released
//------------- example code ignore control (non-printable) key affects -------------//
for (uint8_t i = 0; i < 6; i++) {
if (report->keycode[i]) {
if (find_key_in_report(&prev_report, report->keycode[i])) {
// exist in previous report means the current key is holding
} else {
// not existed in previous report means the current key is pressed
bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
uint8_t ch = keycode2ascii[report->keycode[i]][is_shift ? 1 : 0];
putchar(ch);
if (ch == '\r') putchar('\n'); // added new line for enter key
#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
fflush(stdout); // flush right away, else nanolib will wait for newline
#endif
}
}
// TODO example skips key released
}
prev_report = *report;
}
//--------------------------------------------------------------------+
// Mouse
//--------------------------------------------------------------------+
static void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
#if USE_ANSI_ESCAPE
// Move X using ansi escape
if ( x < 0) {
printf(ANSI_CURSOR_BACKWARD(%d), (-x)); // move left
}else if ( x > 0) {
printf(ANSI_CURSOR_FORWARD(%d), x); // move right
}
// Move Y using ansi escape
if ( y < 0) {
printf(ANSI_CURSOR_UP(%d), (-y)); // move up
}else if ( y > 0) {
printf(ANSI_CURSOR_DOWN(%d), y); // move down
}
// Scroll using ansi escape
if (wheel < 0) {
printf(ANSI_SCROLL_UP(%d), (-wheel)); // scroll up
}else if (wheel > 0) {
printf(ANSI_SCROLL_DOWN(%d), wheel); // scroll down
}
printf("\r\n");
#else
printf("(%d %d %d)\r\n", x, y, wheel);
#endif
}
static void process_mouse_report(hid_mouse_report_t const *report) {
static hid_mouse_report_t prev_report = { 0 };
//------------- button state -------------//
uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
if (button_changed_mask & report->buttons) {
printf(" %c%c%c ",
report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-',
report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-',
report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-');
}
//------------- cursor movement -------------//
cursor_movement(report->x, report->y, report->wheel);
}
//--------------------------------------------------------------------+
// Generic Report
//--------------------------------------------------------------------+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
(void) dev_addr;
(void) len;
uint8_t const rpt_count = hid_info[instance].report_count;
tuh_hid_report_info_t *rpt_info_arr = hid_info[instance].report_info;
tuh_hid_report_info_t *rpt_info = NULL;
if (rpt_count == 1 && rpt_info_arr[0].report_id == 0) {
// Simple report without report ID as 1st byte
rpt_info = &rpt_info_arr[0];
} else {
// Composite report, 1st byte is report ID, data starts from 2nd byte
uint8_t const rpt_id = report[0];
// Find report id in the array
for (uint8_t i = 0; i < rpt_count; i++) {
if (rpt_id == rpt_info_arr[i].report_id) {
rpt_info = &rpt_info_arr[i];
break;
}
}
report++;
len--;
}
if (!rpt_info) {
printf("Couldn't find report info !\r\n");
return;
}
// For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
// - Keyboard : Desktop, Keyboard
// - Mouse : Desktop, Mouse
// - Gamepad : Desktop, Gamepad
// - Consumer Control (Media Key) : Consumer, Consumer Control
// - System Control (Power key) : Desktop, System Control
// - Generic (vendor) : 0xFFxx, xx
if (rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP) {
switch (rpt_info->usage) {
case HID_USAGE_DESKTOP_KEYBOARD:
TU_LOG1("HID receive keyboard report\r\n");
// Assume keyboard follow boot report layout
process_kbd_report((hid_keyboard_report_t const *) report);
break;
case HID_USAGE_DESKTOP_MOUSE:
TU_LOG1("HID receive mouse report\r\n");
// Assume mouse follow boot report layout
process_mouse_report((hid_mouse_report_t const *) report);
break;
default:
break;
}
}
}

View File

@ -1,161 +0,0 @@
/*
* 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 "bsp/board_api.h"
#include "tusb.h"
#include "app.h"
#ifdef ESP_PLATFORM
#define USBH_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
#define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
/* 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 & task
#if configSUPPORT_STATIC_ALLOCATION
StaticTimer_t blinky_tmdef;
StackType_t usb_host_stack[USBH_STACK_SIZE];
StaticTask_t usb_host_taskdef;
#endif
TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
/*------------- MAIN -------------*/
int main(void) {
board_init();
printf("TinyUSB Host CDC MSC HID with FreeRTOS Example\r\n");
// Create soft timer for blinky, task for tinyusb stack
#if configSUPPORT_STATIC_ALLOCATION
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_host_stack, &usb_host_taskdef);
#else
blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb);
xTaskCreate(usb_host_task, "usbd", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL);
#endif
xTimerStart(blinky_tm, 0);
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
return 0;
}
#ifdef ESP_PLATFORM
void app_main(void) {
main();
}
#endif
// USB Host task
// This top level thread process all usb events and invoke callbacks
static void usb_host_task(void *param) {
(void) param;
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_AUTO
};
if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
printf("Failed to init USB Host Stack\r\n");
vTaskSuspend(NULL);
}
board_init_after_tusb();
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E use MAX3421E's GPIO0 for VBUS enable
enum { IOPINS1_ADDR = 20u << 3, /* 0xA0 */ };
tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
#endif
cdc_app_init();
hid_app_init();
msc_app_init();
// RTOS forever loop
while (1) {
// put this thread to waiting state until there is new events
tuh_task();
// following code only run if tuh_task() process at least 1 event
}
}
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
void tuh_mount_cb(uint8_t dev_addr) {
// application set-up
printf("A device with address %d is mounted\r\n", dev_addr);
}
void tuh_umount_cb(uint8_t dev_addr) {
// application tear-down
printf("A device with address %d is unmounted \r\n", dev_addr);
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
static 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

@ -1,73 +0,0 @@
/*
* 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 <stdio.h>
#include "tusb.h"
#include "app.h"
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry);
} scsi_resp;
void msc_app_init(void) {
// nothing to do
}
static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const *cb_data) {
msc_cbw_t const *cbw = cb_data->cbw;
msc_csw_t const *csw = cb_data->csw;
if (csw->status != 0) {
printf("Inquiry failed\r\n");
return false;
}
// Print out Vendor ID, Product ID and Rev
printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
// Get capacity of device
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
block_count, block_size, block_count / ((1024 * 1024) / block_size));
return true;
}
//------------- IMPLEMENTATION -------------//
void tuh_msc_mount_cb(uint8_t dev_addr) {
printf("A MassStorage device is mounted\r\n");
uint8_t const lun = 0;
tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
}
void tuh_msc_umount_cb(uint8_t dev_addr) {
(void) dev_addr;
printf("A MassStorage device is unmounted\r\n");
}

View File

@ -1,142 +0,0 @@
/*
* 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
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_FREERTOS
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
/* 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_TUH_MEM_SECTION
#define CFG_TUH_MEM_SECTION
#endif
#ifndef CFG_TUH_MEM_ALIGN
#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// Host Configuration
//--------------------------------------------------------------------
// Enable Host stack
#define CFG_TUH_ENABLED 1
// #define CFG_TUH_MAX3421 1 // use max3421 as host controller
#if CFG_TUSB_MCU == OPT_MCU_RP2040
// #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
// host roothub port is 1 if using either pio-usb or max3421
#if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
#define BOARD_TUH_RHPORT 1
#endif
#endif
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
//------------------------- Board Specific --------------------------
// RHPort number used for host can be defined by board.mk, default to port 0
#ifndef BOARD_TUH_RHPORT
#define BOARD_TUH_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUH_MAX_SPEED
#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 256
#define CFG_TUH_HUB 1 // number of supported hubs
#define CFG_TUH_CDC 1 // number of supported CDC devices. also activates CDC ACM
#define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_PL2303 1 // PL2303 Serial. PL2303 is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_MSC 1
#define CFG_TUH_VENDOR 0
// max device support (excluding hub device): 1 hub typically has 4 ports
#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
//------------- HID -------------//
#define CFG_TUH_HID_EPIN_BUFSIZE 64
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
//------------- CDC -------------//
// Set Line Control state on enumeration/mounted:
// DTR ( bit 0), RTS (bit 1)
#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM (CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS)
// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
// bit rate = 115200, 1 stop bit, no parity, 8 bit data width
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */

View File

@ -0,0 +1,11 @@
# Host FreeRTOS/No-OS Example Merge TODO
Goal: shrink paired host no-OS and FreeRTOS examples into one directory where the build selects the RTOS with `RTOS=freertos` / `-DRTOS=freertos`.
- [x] Merge `cdc_msc_hid_freertos` into `cdc_msc_hid`.
- [x] Merge `msc_file_explorer_freertos` into `msc_file_explorer`.
- [x] Add RTOS-aware `skip.txt` entries for host FreeRTOS-only exclusions.
- [x] Add ESP-IDF component metadata to the merged host examples.
- [x] Remove merged `_freertos` entries from the host CMake example list.
- [x] Keep Make defaulting to no-OS while allowing `RTOS=freertos`.
- [x] Verify CMake builds for no-OS and FreeRTOS host examples.

View File

@ -30,9 +30,9 @@ target_include_directories(${PROJECT_NAME} PUBLIC
${TOP}/lib/embedded-cli
)
# Configure compilation flags and libraries for the example without RTOS.
# Configure compilation flags and libraries for the selected RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_host_example(${PROJECT_NAME} noos)
family_configure_host_example(${PROJECT_NAME} ${RTOS})
# Suppress warnings on fatfs
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")

View File

@ -57,7 +57,7 @@ make BOARD=raspberry_pi_pico all
## FreeRTOS variant
A FreeRTOS build is in `examples/host/msc_file_explorer_freertos` — identical MSC FatFS file-explorer CLI, running the host stack and CLI as FreeRTOS tasks.
Build this example with `RTOS=freertos` / `-DRTOS=freertos` for the FreeRTOS variant. It keeps the same MSC FatFS file-explorer CLI, running the host stack and CLI as FreeRTOS tasks.
## Usage

View File

@ -1,3 +1,4 @@
family:espressif
family:hpmicro
family:samd21
family:samd5x_e5x

View File

@ -1 +1,11 @@
board:lpcxpresso54114
rtos:noos family:espressif
rtos:noos board:lpcxpresso54114
rtos:freertos mcu:CH32F20X
rtos:freertos mcu:CH32V20X
rtos:freertos mcu:FT90X
rtos:freertos mcu:KINETIS_KL
rtos:freertos mcu:RAXXX
rtos:freertos board:lpcxpresso54114
rtos:freertos board:stm32h7s3nucleo
rtos:freertos family:hpmicro

View File

@ -28,12 +28,41 @@
#include "bsp/board_api.h"
#include "tusb.h"
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#ifdef ESP_PLATFORM
#include "freertos/timers.h"
#define USBH_STACK_SIZE 4096
#else
#include "timers.h"
// Increase stack size when debug log is enabled.
#define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 3))
#endif
#endif
#include "msc_app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
enum {
BLINK_MOUNTED = 1000,
};
#if configSUPPORT_STATIC_ALLOCATION
static StaticTimer_t blinky_tmdef;
static StackType_t usb_host_stack[USBH_STACK_SIZE];
static StaticTask_t usb_host_taskdef;
#endif
static TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
#else
void led_blinking_task(void);
#endif
/*------------- MAIN -------------*/
int main(void) {
@ -41,6 +70,24 @@ int main(void) {
printf("TinyUSB Host MassStorage Explorer Example\r\n");
#if CFG_TUSB_OS == OPT_OS_FREERTOS
// Create soft timer for blinky and task for TinyUSB host stack.
#if configSUPPORT_STATIC_ALLOCATION
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_host_stack,
&usb_host_taskdef);
#else
blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb);
xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
xTimerStart(blinky_tm, 0);
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
#else
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
@ -59,8 +106,54 @@ int main(void) {
msc_app_task();
led_blinking_task();
}
#endif
return 0;
}
#ifdef ESP_PLATFORM
void app_main(void) {
main();
}
#endif
#if CFG_TUSB_OS == OPT_OS_FREERTOS
// USB Host task
// This top-level thread processes all USB events and invokes callbacks.
static void usb_host_task(void* param) {
(void) param;
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_AUTO
};
if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
printf("Failed to init USB Host Stack\r\n");
vTaskSuspend(NULL);
}
board_init_after_tusb();
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
enum { IOPINS1_ADDR = 20u << 3 };
tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
#endif
if (!msc_app_init()) {
printf("Failed to init MSC app\r\n");
vTaskSuspend(NULL);
}
while (1) {
// TinyUSB host task.
tuh_task();
}
}
#endif
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
@ -76,6 +169,15 @@ void tuh_umount_cb(uint8_t dev_addr) {
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static 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
}
#else
void led_blinking_task(void) {
const uint32_t interval_ms = 1000;
static uint32_t start_ms = 0;
@ -83,9 +185,12 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
if (tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
if (tusb_time_millis_api() - start_ms < interval_ms) {
return; // not enough time
}
start_ms += interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}
#endif

View File

@ -48,12 +48,30 @@
#define CLI_HISTORY_SIZE 32
#define CLI_BINDING_COUNT 9
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#ifdef ESP_PLATFORM
#define MSC_APP_STACK_SIZE 4096
#else
#define MSC_APP_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
#endif
#endif
static EmbeddedCli *_cli;
static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#if configSUPPORT_STATIC_ALLOCATION
static StackType_t msc_app_stack[MSC_APP_STACK_SIZE];
static StaticTask_t msc_app_taskdef;
#endif
#endif
//------------- Elm Chan FatFS -------------//
static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static volatile bool _mount_pending[CFG_TUH_DEVICE_MAX];
#endif
static CFG_TUH_MEM_SECTION FIL file1, file2;
@ -73,10 +91,17 @@ CFG_TUH_MEM_SECTION static struct {
//--------------------------------------------------------------------+
bool cli_init(void);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static void msc_app_task(void* param);
static void process_pending_mount(void);
#endif
bool msc_app_init(void) {
for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
_disk_busy[i] = false;
#if CFG_TUSB_OS == OPT_OS_FREERTOS
_mount_pending[i] = false;
#endif
}
// disable stdout buffered for echoing typing command
@ -86,9 +111,73 @@ bool msc_app_init(void) {
cli_init();
#if CFG_TUSB_OS == OPT_OS_FREERTOS
#if configSUPPORT_STATIC_ALLOCATION
TaskHandle_t task_hdl = xTaskCreateStatic(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL,
configMAX_PRIORITIES - 2, msc_app_stack, &msc_app_taskdef);
TU_ASSERT(task_hdl != NULL);
#else
TU_ASSERT(xTaskCreate(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL) == pdPASS);
#endif
#endif
return true;
}
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static void msc_app_task(void* param) {
(void) param;
while (1) {
process_pending_mount();
if (!_cli) {
vTaskDelay(1);
continue;
}
int ch = board_getchar();
if (ch > 0) {
while (ch > 0) {
embeddedCliReceiveChar(_cli, (char) ch);
ch = board_getchar();
}
embeddedCliProcess(_cli);
}
vTaskDelay(1);
}
}
static void process_pending_mount(void) {
for (uint8_t drive_num = 0; drive_num < CFG_TUH_DEVICE_MAX; drive_num++) {
if (!_mount_pending[drive_num]) {
continue;
}
_mount_pending[drive_num] = false;
const uint8_t dev_addr = drive_num + 1;
if (!tuh_msc_mounted(dev_addr)) {
continue;
}
char drive_path[3] = "0:";
drive_path[0] += drive_num;
if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
printf("mount failed\r\n");
continue;
}
f_chdrive(drive_path);
FRESULT rc = f_chdir("/");
if (rc != FR_OK) {
printf("chdir failed: %d\r\n", rc);
}
}
}
#else
void msc_app_task(void) {
if (!_cli) {
return;
@ -103,6 +192,7 @@ void msc_app_task(void) {
embeddedCliProcess(_cli);
}
}
#endif
//--------------------------------------------------------------------+
//
@ -130,6 +220,9 @@ static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t
// For simplicity: we only mount 1 LUN per device
const uint8_t drive_num = dev_addr - 1;
#if CFG_TUSB_OS == OPT_OS_FREERTOS
_mount_pending[drive_num] = true;
#else
char drive_path[3] = "0:";
drive_path[0] += drive_num;
@ -144,6 +237,7 @@ static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t
if (rc != FR_OK) {
printf("chdir failed: %d\r\n", rc);
}
#endif
// print the drive label
// char label[34];
@ -170,6 +264,10 @@ void tuh_msc_umount_cb(uint8_t dev_addr) {
char drive_path[3] = "0:";
drive_path[0] += drive_num;
#if CFG_TUSB_OS == OPT_OS_FREERTOS
_mount_pending[drive_num] = false;
#endif
f_unmount(drive_path);
// if ( phy_disk == f_get_current_drive() )
@ -191,7 +289,11 @@ void tuh_msc_umount_cb(uint8_t dev_addr) {
static void wait_for_disk_io(BYTE pdrv) {
while (_disk_busy[pdrv]) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(1);
#else
tuh_task();
#endif
}
}
@ -387,14 +489,26 @@ void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
const uint32_t start_ms = tusb_time_millis_api();
const uint8_t pdrv = dev_addr - 1;
bool submit_failed = false;
for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
_disk_busy[pdrv] = true;
tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0);
if (!tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0)) {
_disk_busy[pdrv] = false;
printf("dd: failed to submit read at sector %" PRIu32 " (%u sectors)\r\n", i, n);
submit_failed = true;
break;
}
wait_for_disk_io(pdrv);
}
if (submit_failed) {
return;
}
const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
const uint32_t total_data = count * block_size;
// each SCSI transaction has 31-byte CBW + data + 13-byte CSW
@ -491,6 +605,7 @@ void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
printf("cannot create '%s'\r\n", dst);
f_close(f_src);
return;
} else {
UINT rd_count = 0;
@ -566,6 +681,7 @@ void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
char path[256];
if (FR_OK != f_getcwd(path, sizeof(path))) {
printf("cannot get current working directory\r\n");
return;
}
puts(path);

View File

@ -28,9 +28,11 @@
#include <stdbool.h>
#include <stdio.h>
#include "tusb.h"
bool msc_app_init(void);
#if CFG_TUSB_OS != OPT_OS_FREERTOS
void msc_app_task(void);
#endif
#endif

View File

@ -43,6 +43,11 @@
#define CFG_TUSB_OS OPT_OS_NONE
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif

View File

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(msc_file_explorer_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
${TOP}/lib/fatfs/source/ff.c
${TOP}/lib/fatfs/source/ffsystem.c
${TOP}/lib/fatfs/source/ffunicode.c
)
# Example include
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${TOP}/lib/fatfs/source
${TOP}/lib/embedded-cli
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_host_example(${PROJECT_NAME} freertos)
# Suppress warnings on fatfs
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set_source_files_properties(${TOP}/lib/fatfs/source/ff.c PROPERTIES
COMPILE_OPTIONS "-Wno-conversion;-Wno-cast-qual"
)
endif ()

View File

@ -1,6 +0,0 @@
{
"version": 6,
"include": [
"../../../hw/bsp/BoardPresets.json"
]
}

View File

@ -1,27 +0,0 @@
RTOS = freertos
include ../../../hw/bsp/family_support.mk
FATFS_PATH = lib/fatfs/source
INC += \
src \
$(TOP)/$(FATFS_PATH) \
$(TOP)/lib/embedded-cli \
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
# FatFS source
SRC_C += \
$(FATFS_PATH)/ff.c \
$(FATFS_PATH)/ffsystem.c \
$(FATFS_PATH)/ffunicode.c \
# suppress warning caused by fatfs
CFLAGS += -Wno-error=cast-qual
include ../../../hw/bsp/family_rules.mk

View File

@ -1,28 +0,0 @@
family:espressif
family:samd21
family:samd5x_e5x
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
mcu:LPC54
mcu:LPC55
mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
mcu:MIMXRT1XXX
mcu:MSP432E4
mcu:RP2040
mcu:RW61X
mcu:RX65X
mcu:STM32C0
mcu:STM32F4
mcu:STM32F7
mcu:STM32G0
mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
mcu:STM32U3
mcu:STM32U5

View File

@ -1,4 +0,0 @@
mcu:CH32F20X
board:lpcxpresso54114
mcu:FT90X
board:stm32h7s3nucleo

View File

@ -1,313 +0,0 @@
/*---------------------------------------------------------------------------/
/ Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 80386 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 0
/* This option switches f_mkfs(). (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand(). (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 0
/* This option switches attribute control API functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label API functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward(). (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 0
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 0
/* FF_USE_STRFUNC switches string API functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 437
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
/ 0 - Include all code pages above and configured by f_setcp()
*/
#define FF_USE_LFN 1
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be sufficient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_FS_RPATH 2
/* This option configures support for relative path feature.
/
/ 0: Disable relative path and remove related API functions.
/ 1: Enable relative path and dot names. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() is available in addition to 1.
*/
#define FF_PATH_DEPTH 10
/* This option defines maximum depth of directory in the exFAT volume. It is NOT
/ relevant to FAT/FAT32 volume.
/ For example, FF_PATH_DEPTH = 3 will able to follow a path "/dir1/dir2/dir3/file"
/ but a sub-directory in the dir3 will not able to be followed and set current
/ directory.
/ The size of filesystem object (FATFS) increases FF_PATH_DEPTH * 24 bytes.
/ When FF_FS_EXFAT == 0 or FF_FS_RPATH == 0, this option has no effect.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 4
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table is needed as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this feature is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
/ configured for variable sector size mode and disk_ioctl() needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_LBA64 0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable this feature, also CTRL_TRIM command should be implemented to
/ the disk_ioctl(). */
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is reduced FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 1
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2025
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
/ to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_CRTIME 0
/* This option enables(1)/disables(0) the timestamp of the file created. When
/ set 1, the file created time is available in FILINFO structure. */
#define FF_FS_NOFSINFO 0
/* If you need to know the correct free space on the FAT32 volume, set bit 0 of
/ this option, and f_getfree() on the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk(), are always not re-entrant. Only file/directory access to
/ the same volume is under control of this featuer.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
/ must be added to the project. Samples are available in ffsystem.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/
/*--- End of configuration options ---*/

View File

@ -1,158 +0,0 @@
/*
* 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 <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
#ifdef ESP_PLATFORM
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#endif
#include "msc_app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#ifdef ESP_PLATFORM
#define USBH_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled.
#define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 3))
#endif
enum {
BLINK_MOUNTED = 1000,
};
#if configSUPPORT_STATIC_ALLOCATION
StaticTimer_t blinky_tmdef;
StackType_t usb_host_stack[USBH_STACK_SIZE];
StaticTask_t usb_host_taskdef;
#endif
TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
/*------------- MAIN -------------*/
int main(void) {
board_init();
printf("TinyUSB Host MassStorage Explorer FreeRTOS Example\r\n");
// Create soft timer for blinky and task for TinyUSB host stack.
#if configSUPPORT_STATIC_ALLOCATION
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_host_stack,
&usb_host_taskdef);
#else
blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb);
xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
xTimerStart(blinky_tm, 0);
// only start scheduler for non-espressif mcu
#ifndef ESP_PLATFORM
vTaskStartScheduler();
#endif
return 0;
}
#ifdef ESP_PLATFORM
void app_main(void) {
main();
}
#endif
// USB Host task
// This top-level thread processes all USB events and invokes callbacks.
static void usb_host_task(void* param) {
(void) param;
// init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_AUTO
};
if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
printf("Failed to init USB Host Stack\r\n");
vTaskSuspend(NULL);
}
board_init_after_tusb();
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
enum { IOPINS1_ADDR = 20u << 3 };
tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
#endif
if (!msc_app_init()) {
printf("Failed to init MSC app\r\n");
vTaskSuspend(NULL);
}
while (1) {
// TinyUSB host task.
tuh_task();
}
}
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
void tuh_mount_cb(uint8_t dev_addr) {
(void) dev_addr;
}
void tuh_umount_cb(uint8_t dev_addr) {
(void) dev_addr;
}
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
static 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

@ -1,709 +0,0 @@
/*
* 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 <ctype.h>
#include "tusb.h"
#include "bsp/board_api.h"
#ifdef ESP_PLATFORM
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#endif
#include "ff.h"
#include "diskio.h"
// lib/embedded-cli
#define EMBEDDED_CLI_IMPL
#include "embedded_cli.h"
#include "msc_app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
//------------- embedded-cli -------------//
#define CLI_BUFFER_SIZE 512
#define CLI_RX_BUFFER_SIZE 16
#define CLI_CMD_BUFFER_SIZE 64
#define CLI_HISTORY_SIZE 32
#define CLI_BINDING_COUNT 9
#ifdef ESP_PLATFORM
#define MSC_APP_STACK_SIZE 4096
#else
#define MSC_APP_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
#endif
static EmbeddedCli *_cli;
static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
#if configSUPPORT_STATIC_ALLOCATION
StackType_t msc_app_stack[MSC_APP_STACK_SIZE];
StaticTask_t msc_app_taskdef;
#endif
//------------- Elm Chan FatFS -------------//
static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
static volatile bool _mount_pending[CFG_TUH_DEVICE_MAX];
static CFG_TUH_MEM_SECTION FIL file1, file2;
#ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
#define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
#endif
static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE];
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry);
} scsi_resp;
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
static bool cli_init(void);
static void msc_app_task(void* param);
static void process_pending_mount(void);
bool msc_app_init(void) {
for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
_disk_busy[i] = false;
_mount_pending[i] = false;
}
// disable stdout buffered for echoing typing command
#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
setbuf(stdout, NULL);
#endif
cli_init();
#if configSUPPORT_STATIC_ALLOCATION
TaskHandle_t task_hdl = xTaskCreateStatic(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL,
configMAX_PRIORITIES - 2, msc_app_stack, &msc_app_taskdef);
TU_ASSERT(task_hdl != NULL);
#else
TU_ASSERT(xTaskCreate(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL) == pdPASS);
#endif
return true;
}
static void msc_app_task(void* param) {
(void) param;
while (1) {
process_pending_mount();
if (!_cli) {
vTaskDelay(1);
continue;
}
int ch = board_getchar();
if (ch > 0) {
while (ch > 0) {
embeddedCliReceiveChar(_cli, (char) ch);
ch = board_getchar();
}
embeddedCliProcess(_cli);
}
vTaskDelay(1);
}
}
static void process_pending_mount(void) {
for (uint8_t drive_num = 0; drive_num < CFG_TUH_DEVICE_MAX; drive_num++) {
if (!_mount_pending[drive_num]) {
continue;
}
_mount_pending[drive_num] = false;
const uint8_t dev_addr = drive_num + 1;
if (!tuh_msc_mounted(dev_addr)) {
continue;
}
char drive_path[3] = "0:";
drive_path[0] += drive_num;
if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
printf("mount failed\r\n");
continue;
}
f_chdrive(drive_path);
FRESULT rc = f_chdir("/");
if (rc != FR_OK) {
printf("chdir failed: %d\r\n", rc);
}
}
}
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
const msc_cbw_t *cbw = cb_data->cbw;
const msc_csw_t *csw = cb_data->csw;
if (csw->status != 0) {
printf("Inquiry failed\r\n");
return false;
}
// Print out Vendor ID, Product ID and Rev
printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
scsi_resp.inquiry.product_rev);
// Get capacity of device
const uint32_t block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
const uint32_t block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
block_count, block_size, block_count / ((1024 * 1024) / block_size));
// For simplicity: we only mount 1 LUN per device
const uint8_t drive_num = dev_addr - 1;
_mount_pending[drive_num] = true;
// print the drive label
// char label[34];
// if ( FR_OK == f_getlabel(drive_path, label, NULL) )
// {
// puts(label);
// }
return true;
}
//------------- IMPLEMENTATION -------------//
void tuh_msc_mount_cb(uint8_t dev_addr) {
printf("A MassStorage device (addr = %u) is mounted\r\n", dev_addr);
const uint8_t lun = 0;
tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
}
void tuh_msc_umount_cb(uint8_t dev_addr) {
printf("A MassStorage device is unmounted\r\n");
const uint8_t drive_num = dev_addr - 1;
char drive_path[3] = "0:";
drive_path[0] += drive_num;
_mount_pending[drive_num] = false;
f_unmount(drive_path);
// if ( phy_disk == f_get_current_drive() )
// { // active drive is unplugged --> change to other drive
// for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
// {
// if ( disk_is_ready(i) )
// {
// f_chdrive(i);
// cli_init(); // refractor, rename
// }
// }
// }
}
//--------------------------------------------------------------------+
// DiskIO
//--------------------------------------------------------------------+
static void wait_for_disk_io(BYTE pdrv) {
while (_disk_busy[pdrv]) {
vTaskDelay(1);
}
}
static bool disk_io_complete(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
(void)dev_addr;
(void)cb_data;
_disk_busy[dev_addr - 1] = false;
return true;
}
DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
uint8_t dev_addr = pdrv + 1;
return tuh_msc_mounted(dev_addr) ? 0 : STA_NODISK;
}
DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
(void)pdrv;
return 0; // nothing to do
}
DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
) {
const uint8_t dev_addr = pdrv + 1;
const uint8_t lun = 0;
_disk_busy[pdrv] = true;
tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
wait_for_disk_io(pdrv);
return RES_OK;
}
#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
) {
const uint8_t dev_addr = pdrv + 1;
const uint8_t lun = 0;
_disk_busy[pdrv] = true;
tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
wait_for_disk_io(pdrv);
return RES_OK;
}
#endif
DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
) {
const uint8_t dev_addr = pdrv + 1;
const uint8_t lun = 0;
switch (cmd) {
case CTRL_SYNC:
// nothing to do since we do blocking
return RES_OK;
case GET_SECTOR_COUNT:
*((DWORD *)buff) = (DWORD)tuh_msc_get_block_count(dev_addr, lun);
return RES_OK;
case GET_SECTOR_SIZE:
*((WORD *)buff) = (WORD)tuh_msc_get_block_size(dev_addr, lun);
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD *)buff) = 1; // erase block size in units of sector size
return RES_OK;
default:
return RES_PARERR;
}
}
//--------------------------------------------------------------------+
// CLI Commands
//--------------------------------------------------------------------+
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context);
static void cli_write_char(EmbeddedCli *cli, char c) {
(void)cli;
putchar((int)c);
}
bool cli_init(void) {
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
config->cliBuffer = cli_buffer;
config->cliBufferSize = CLI_BUFFER_SIZE;
config->rxBufferSize = CLI_RX_BUFFER_SIZE;
config->cmdBufferSize = CLI_CMD_BUFFER_SIZE;
config->historyBufferSize = CLI_HISTORY_SIZE;
config->maxBindingCount = CLI_BINDING_COUNT;
TU_ASSERT(embeddedCliRequiredSize(config) <= CLI_BUFFER_SIZE);
_cli = embeddedCliNew(config);
TU_ASSERT(_cli != NULL);
_cli->writeChar = cli_write_char;
embeddedCliAddBinding(_cli,
(CliCommandBinding){"cat", "Usage: cat [FILE]...\r\n\tConcatenate FILE(s) to standard output..",
true, NULL, cli_cmd_cat});
embeddedCliAddBinding(_cli, (CliCommandBinding){"cd", "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
true, NULL, cli_cmd_cd});
embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
cli_cmd_cp});
embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
cli_cmd_dd});
embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
"Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
"current directory by default).",
true, NULL, cli_cmd_ls});
embeddedCliAddBinding(_cli,
(CliCommandBinding){"pwd", "Usage: pwd\r\n\tPrint the name of the current working directory.",
true, NULL, cli_cmd_pwd});
embeddedCliAddBinding(_cli, (CliCommandBinding){"mkdir",
"Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not "
"already exist..",
true, NULL, cli_cmd_mkdir});
embeddedCliAddBinding(_cli, (CliCommandBinding){"mv", "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.", true,
NULL, cli_cmd_mv});
embeddedCliAddBinding(_cli, (CliCommandBinding){"rm", "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).", true,
NULL, cli_cmd_rm});
return true;
}
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint32_t count = 1024; // default sectors to read
if (embeddedCliGetTokenCount(args) >= 1) {
count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
if (count == 0) {
count = 1024;
}
}
// find first mounted MSC device
uint8_t dev_addr = 0;
for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
if (tuh_msc_mounted(i)) {
dev_addr = i;
break;
}
}
if (dev_addr == 0) {
printf("no MSC device mounted\r\n");
return;
}
const uint8_t lun = 0;
const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
if (count > block_count) {
count = block_count;
}
const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
count, count * block_size, sectors_per_xfer);
const uint32_t start_ms = tusb_time_millis_api();
const uint8_t pdrv = dev_addr - 1;
bool submit_failed = false;
for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
_disk_busy[pdrv] = true;
if (!tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0)) {
_disk_busy[pdrv] = false;
printf("dd: failed to submit read at sector %" PRIu32 " (%u sectors)\r\n", i, n);
submit_failed = true;
break;
}
wait_for_disk_io(pdrv);
}
if (submit_failed) {
return;
}
const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
const uint32_t total_data = count * block_size;
// each SCSI transaction has 31-byte CBW + data + 13-byte CSW
const uint32_t total_bus = total_data + xfer_count * (31 + 13);
if (elapsed_ms > 0) {
const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
const uint32_t bus_kbs = total_bus / elapsed_ms;
printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
total_data, elapsed_ms, data_kbs, bus_kbs);
} else {
printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
}
}
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// need at least 1 argument
if (argc == 0) {
printf("invalid arguments\r\n");
return;
}
for (uint16_t i = 0; i < argc; i++) {
FIL *fi = &file1;
const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
if (FR_OK != f_open(fi, fpath, FA_READ)) {
printf("%s: No such file or directory\r\n", fpath);
} else {
UINT count = 0;
while ((FR_OK == f_read(fi, rw_buf, sizeof(rw_buf), &count)) && (count > 0)) {
for (UINT c = 0; c < count; c++) {
const uint8_t ch = rw_buf[c];
if (isprint(ch) || iscntrl(ch)) {
putchar(ch);
} else {
putchar('.');
}
}
}
}
f_close(fi);
}
}
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
const char *dpath = args;
if (FR_OK != f_chdir(dpath)) {
printf("%s: No such file or directory\r\n", dpath);
return;
}
}
void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
if (argc != 2) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
const char *src = embeddedCliGetToken(args, 1);
const char *dst = embeddedCliGetToken(args, 2);
FIL *f_src = &file1;
FIL *f_dst = &file2;
if (FR_OK != f_open(f_src, src, FA_READ)) {
printf("cannot stat '%s': No such file or directory\r\n", src);
return;
}
if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
printf("cannot create '%s'\r\n", dst);
f_close(f_src);
return;
} else {
UINT rd_count = 0;
while ((FR_OK == f_read(f_src, rw_buf, sizeof(rw_buf), &rd_count)) && (rd_count > 0)) {
UINT wr_count = 0;
if (FR_OK != f_write(f_dst, rw_buf, rd_count, &wr_count)) {
printf("cannot write to '%s'\r\n", dst);
break;
}
}
}
f_close(f_src);
f_close(f_dst);
}
void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
if (argc > 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
const char *dpath = ".";
if (argc) {
dpath = args;
}
DIR dir;
if (FR_OK != f_opendir(&dir, dpath)) {
printf("cannot access '%s': No such file or directory\r\n", dpath);
return;
}
FILINFO fno;
while ((f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0)) {
if (fno.fname[0] != '.') // ignore . and .. entry
{
if (fno.fattrib & AM_DIR) {
// directory
printf("/%s\r\n", fno.fname);
} else {
printf("%-40s", fno.fname);
if (fno.fsize < 1024) {
printf("%" PRIu32 " B\r\n", fno.fsize);
} else {
printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
}
}
}
}
f_closedir(&dir);
}
void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
if (argc != 0) {
printf("invalid arguments\r\n");
return;
}
char path[256];
if (FR_OK != f_getcwd(path, sizeof(path))) {
printf("cannot get current working directory\r\n");
return;
}
puts(path);
}
void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
const char *dpath = args;
if (FR_OK != f_mkdir(dpath)) {
printf("%s: cannot create this directory\r\n", dpath);
return;
}
}
void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
if (argc != 2) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
const char *src = embeddedCliGetToken(args, 1);
const char *dst = embeddedCliGetToken(args, 2);
if (FR_OK != f_rename(src, dst)) {
printf("cannot mv %s to %s\r\n", src, dst);
return;
}
}
void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context) {
(void)cli;
(void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// need at least 1 argument
if (argc == 0) {
printf("invalid arguments\r\n");
return;
}
for (uint16_t i = 0; i < argc; i++) {
const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
if (FR_OK != f_unlink(fpath)) {
printf("cannot remove '%s': No such file or directory\r\n", fpath);
}
}
}

View File

@ -1,34 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2025 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.
*/
#ifndef MSC_APP_H
#define MSC_APP_H
#include <stdbool.h>
#include <stdio.h>
bool msc_app_init(void);
#endif

View File

@ -1,126 +0,0 @@
/*
* 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
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_FREERTOS
#endif
// Espressif IDF requires "freertos/" prefix in include path
#ifdef ESP_PLATFORM
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
/* 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_TUH_MEM_SECTION
#define CFG_TUH_MEM_SECTION
#endif
#ifndef CFG_TUH_MEM_ALIGN
#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// Host Configuration
//--------------------------------------------------------------------
// Enable Host stack
#define CFG_TUH_ENABLED 1
// #define CFG_TUH_MAX3421 1 // use max3421 as host controller
#if CFG_TUSB_MCU == OPT_MCU_RP2040
// #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
// host roothub port is 1 if using either pio-usb or max3421
#if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
#define BOARD_TUH_RHPORT 1
#endif
#endif
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
//------------------------- Board Specific --------------------------
// RHPort number used for host can be defined by board.mk, default to port 0
#ifndef BOARD_TUH_RHPORT
#define BOARD_TUH_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUH_MAX_SPEED
#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 256
#define CFG_TUH_HUB 1 // number of supported hubs
#define CFG_TUH_MSC 1
#define CFG_TUH_CDC 0
#define CFG_TUH_HID 0 // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_VENDOR 0
// max device support (excluding hub device): 1 hub typically has 4 ports
#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
//------------- MSC -------------//
#define CFG_TUH_MSC_MAXLUN 4 // typical for most card reader
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */