Starting to have functionnal host and device using tu.

This commit is contained in:
Ulysse Cura 2026-10-13 23:34:57 +02:00
parent 8c387da066
commit 1ea377684b
13 changed files with 506 additions and 609 deletions

View File

@ -3,27 +3,13 @@
{
"name": "Linux",
"intelliSenseMode": "linux-gcc-arm",
"includePath": [
"${env:PICO_SDK_PATH}/src/**/include/",
"${env:PICO_SDK_PATH}/lib/**/include/",
"${env:PICO_SDK_PATH}/lib/**/src/",
"${env:PICO_SDK_PATH}/lib/tinyusb/hw",
"${workspaceFolder}/build/generated/pico_base/",
"${workspaceFolder}/src/",
"${workspaceFolder}/lib/**/"
],
"compilerPath": "/usr/bin/arm-none-eabi-gcc",
"cStandard": "c17",
"browse": {
"path": [
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
"limitSymbolsToIncludedHeaders": true
},
"defines": [
"CFG_TUSB_MCU=OPT_MCU_RP2040"
]
"compileCommands": "build/compile_commands.json"
}
],
"version": 4

3
.vscode/tasks.json vendored
View File

@ -8,7 +8,8 @@
"command": "cmake",
"args": [
"-H.",
"-Bbuild"
"-Bbuild",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=1"
],
"group": "build",
"presentation": {

View File

@ -2,9 +2,6 @@ cmake_minimum_required(VERSION 3.13)
# Explicitely define board
set(PICO_BOARD pico)
if(NOT DEFINED PICO_BOARD)
add_definitions(-DPICO_BOARD=${PICO_BOARD})
endif()
# Include pico-sdk cmake support
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
@ -19,21 +16,37 @@ pico_sdk_init()
add_executable(Pico_Host)
add_executable(Pico_Device)
# Sources
target_sources(Pico_Host PUBLIC
src/main_host.c
src/cdc_app.c
src/hid_app.c
src/msc_app.c
src_host/main.c
src_host/cdc_app.c
)
target_sources(Pico_Device PUBLIC
src/main_device.c
src_device/main.c
src_device/usb_descriptor.c
)
# Flags
# Enable the reset interface
target_compile_definitions(Pico_Device PUBLIC
PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE=1
# use SDK MS_OS_20 descriptor, as this doesn't have a custom one
PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR=1
# pass the interface number to the SDK MS_OS_20 descriptor
PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF=4
)
# Include paths
target_include_directories(Pico_Host PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/
${CMAKE_CURRENT_LIST_DIR}/src_host/
)
target_include_directories(Pico_Device PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src_device/
)
# Libs
target_link_libraries(Pico_Host
pico_stdlib
tinyusb_board
@ -42,13 +55,17 @@ target_link_libraries(Pico_Host
target_link_libraries(Pico_Device
pico_stdlib
tinyusb_board
tinyusb_device
)
# Output
pico_enable_stdio_usb(Pico_Device 1)
# Outputs
pico_add_extra_outputs(Pico_Host)
pico_add_extra_outputs(Pico_Device)
# Flash target
# Flash targets
add_custom_target(Flash_Host
DEPENDS Pico_Host
COMMAND picotool load -f ${PROJECT_BINARY_DIR}/Pico_Host.uf2

View File

@ -1,298 +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"
//--------------------------------------------------------------------+
// 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_task(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
//--------------------------------------------------------------------+
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;
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,31 +0,0 @@
#include <pico/stdlib.h>
#include <stdio.h>
int main(void)
{
stdio_init_all();
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
while(true)
{
static uint64_t last_time = 0;
uint64_t time = time_us_64();
if(time - last_time > 1000 * 1000)
{
last_time = time;
static bool led_state = true;
led_state = !led_state;
gpio_put(PICO_DEFAULT_LED_PIN, led_state);
printf("led_state : %d\n", led_state);
}
tight_loop_contents();
}
return PICO_OK;
}

View File

@ -1,111 +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 "tusb_config.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
extern void cdc_app_task(void);
extern void hid_app_task(void);
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
// API to read/rite MAX3421's register. Implemented by TinyUSB
extern uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
extern bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
#endif
/*------------- MAIN -------------*/
int main(void) {
board_init();
printf("TinyUSB Host CDC MSC HID Example\r\n");
// init host stack on configured roothub port
const tusb_rhport_init_t rh_init = {
.role = TUSB_ROLE_HOST,
.speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL,
};
TU_ASSERT(tuh_rhport_init(BOARD_TUH_RHPORT, &rh_init));
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
while (1) {
// tinyusb host task
tuh_task();
led_blinking_task();
cdc_app_task();
hid_app_task();
}
}
//--------------------------------------------------------------------+
// 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);
}
#if TUSB_VERSION_NUMBER > 1800
// board_millis has been removed from tinyusb. Use tusb_time_millis_api instead
#define board_millis tusb_time_millis_api
#endif
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
void led_blinking_task(void) {
const uint32_t interval_ms = 1000;
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
if (board_millis() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
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 "tusb.h"
#include <inttypes.h>
#include <stdio.h>
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
static scsi_inquiry_resp_t inquiry_resp;
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 rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.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 " MB\r\n", block_count / ((1024*1024)/block_size));
printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, 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, &inquiry_resp, inquiry_complete_cb, 0);
}
void tuh_msc_umount_cb(uint8_t dev_addr)
{
(void) dev_addr;
printf("A MassStorage device is unmounted\r\n");
}

77
src_device/main.c Normal file
View File

@ -0,0 +1,77 @@
/**
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <tusb.h>
#include <bsp/board.h>
#include <pico/stdio.h>
#include <pico/stdlib.h>
#define LED_BLINK_DELAY_MS 1000
int main(void)
{
// Initialize TinyUSB stack
board_init();
tusb_init();
// let pico sdk use the first cdc interface for std io
stdio_init_all();
uint32_t start_ms = 0;
bool led_state = false;
// main run loop
while(true)
{
// TinyUSB device task | must be called regurlarly
tud_task();
if(board_millis() - start_ms > LED_BLINK_DELAY_MS)
{
board_led_write(led_state);
led_state = ! led_state;
start_ms += LED_BLINK_DELAY_MS;
if(tud_cdc_n_connected(0))
{
printf("Connected to CDC 0\n");
}
if(tud_cdc_n_connected(1))
{
printf("Connected to CDC 1\n");
}
}
}
}
// callback when data is received on a CDC interface
void tud_cdc_rx_cb(uint8_t itf)
{
// allocate buffer for the data in the stack
uint8_t buf[CFG_TUD_CDC_RX_BUFSIZE];
printf("RX CDC %d\n", itf);
// read the available data
// | IMPORTANT: also do this for CDC0 because otherwise
// | you won't be able to print anymore to CDC0
// | next time this function is called
uint32_t count = tud_cdc_n_read(itf, buf, sizeof(buf));
// check if the data was received on the second cdc interface
if (itf == 1) {
// process the received data
buf[count] = 0; // null-terminate the string
// now echo data back to the console on CDC 0
printf("Received on CDC 1: %s\n", buf);
// and echo back OK on CDC 1
tud_cdc_n_write(itf, (uint8_t const *) "OK\r\n", 4);
tud_cdc_n_write_flush(itf);
}
}

42
src_device/tusb_config.h Normal file
View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
#define CFG_TUD_ENABLED (1)
// Legacy RHPORT configuration
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT (0)
#endif
// end legacy RHPORT
//------------------------
// DEVICE CONFIGURATION //
//------------------------
// Enable 2 CDC classes
#define CFG_TUD_CDC (2)
// Set CDC FIFO buffer sizes
#define CFG_TUD_CDC_RX_BUFSIZE (64)
#define CFG_TUD_CDC_TX_BUFSIZE (64)
#define CFG_TUD_CDC_EP_BUFSIZE (64)
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE (64)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

211
src_device/usb_descriptor.c Normal file
View File

@ -0,0 +1,211 @@
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <tusb.h>
#include <bsp/board_api.h>
#include <assert.h>
#include "pico/usb_reset.h"
// set some example Vendor and Product ID
// the board will use to identify at the host
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define CDC_EXAMPLE_VID 0xCafe
// use _PID_MAP to generate unique PID for each interface
#define CDC_EXAMPLE_PID (0x4100 | _PID_MAP(CDC, 0))
// set to 2.10 so the MS_OS_20_DESCRIPTOR works
#define CDC_EXAMPLE_BCD 0x0210
// String descriptors referenced with .i... in the descriptor tables
enum {
STRID_LANGID = 0, // 0: supported language ID
STRID_MANUFACTURER, // 1: Manufacturer
STRID_PRODUCT, // 2: Product
STRID_SERIAL, // 3: Serials
STRID_CDC_0, // 4: CDC Interface 0
STRID_CDC_1, // 5: CDC Interface 1
STRID_RPI_RESET, // 6: Reset Interface
};
// array of pointer to string descriptors
char const *string_desc_arr[] = {
// switched because board is little endian
(const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer
"Pico (2)", // 2: Product
NULL, // 3: Serials (null so it uses unique ID if available)
"Pico SDK stdio", // 4: CDC Interface 0
"Custom CDC", // 5: CDC Interface 1,
"Reset" // 6: Reset Interface
};
// defines a descriptor that will be communicated to the host
tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = CDC_EXAMPLE_BCD,
.bDeviceClass = TUSB_CLASS_MISC, // CDC is a subclass of misc
.bDeviceSubClass = MISC_SUBCLASS_COMMON, // CDC uses common subclass
.bDeviceProtocol = MISC_PROTOCOL_IAD, // CDC uses IAD
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, // 64 bytes
.idVendor = CDC_EXAMPLE_VID,
.idProduct = CDC_EXAMPLE_PID,
.bcdDevice = 0x0100, // Device release number
.iManufacturer = STRID_MANUFACTURER, // Index of manufacturer string
.iProduct = STRID_PRODUCT, // Index of product string
.iSerialNumber = STRID_SERIAL, // Index of serial number string
.bNumConfigurations = 0x01 // 1 configuration
};
// called when host requests to get device descriptor
uint8_t const *tud_descriptor_device_cb(void);
enum {
ITF_NUM_CDC_0 = 0,
ITF_NUM_CDC_0_DATA,
ITF_NUM_CDC_1,
ITF_NUM_CDC_1_DATA,
ITF_NUM_RPI_RESET,
ITF_NUM_TOTAL
};
static_assert(ITF_NUM_RPI_RESET == PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF, "ITF_NUM_RPI_RESET must be equal to the PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF set in CMakeLists.txt");
// total length of configuration descriptor
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN)
// define endpoint numbers
#define EPNUM_CDC_0_NOTIF 0x81 // notification endpoint for CDC 0
#define EPNUM_CDC_0_OUT 0x02 // out endpoint for CDC 0
#define EPNUM_CDC_0_IN 0x82 // in endpoint for CDC 0
#define EPNUM_CDC_1_NOTIF 0x84 // notification endpoint for CDC 1
#define EPNUM_CDC_1_OUT 0x05 // out endpoint for CDC 1
#define EPNUM_CDC_1_IN 0x85 // in endpoint for CDC 1
// configure descriptor (for 2 CDC interfaces)
uint8_t const desc_configuration[] = {
// config descriptor | how much power in mA, count of interfaces, ...
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100),
// CDC 0: Communication Interface - TODO: get 64 from tusb_config.h
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, STRID_CDC_0, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64),
// CDC 0: Data Interface
//TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0_DATA, 4, 0x01, 0x02),
// CDC 1: Communication Interface - TODO: get 64 from tusb_config.h
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, STRID_CDC_1, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64),
// CDC 1: Data Interface
//TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1_DATA, 4, 0x03, 0x04),
// RPi Reset Interface
TUD_RPI_RESET_DESCRIPTOR(ITF_NUM_RPI_RESET, STRID_RPI_RESET),
};
// called when host requests to get configuration descriptor
uint8_t const * tud_descriptor_configuration_cb(uint8_t index);
// more device descriptor this time the qualifier
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = CDC_EXAMPLE_BCD,
.bDeviceClass = TUSB_CLASS_CDC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00
};
// called when host requests to get device qualifier descriptor
uint8_t const* tud_descriptor_device_qualifier_cb(void);
// buffer to hold the string descriptor during the request | plus 1 for the null terminator
static uint16_t _desc_str[32 + 1];
// called when host request to get string descriptor
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid);
// --------------------------------------------------------------------+
// IMPLEMENTATION
// --------------------------------------------------------------------+
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *)&desc_device;
}
uint8_t const* tud_descriptor_device_qualifier_cb(void)
{
return (uint8_t const *)&desc_device_qualifier;
}
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
// avoid unused parameter warning and keep function signature consistent
(void)index;
return desc_configuration;
}
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
// TODO: check lang id
(void) langid;
size_t char_count;
// Determine which string descriptor to return
switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2);
char_count = 1;
break;
case STRID_SERIAL:
// try to read the serial from the board
char_count = board_usb_get_serial(_desc_str + 1, 32);
break;
default:
// COPYRIGHT NOTE: Based on TinyUSB example
// Windows wants utf16le
// Determine which string descriptor to return
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) {
return NULL;
}
// Copy string descriptor into _desc_str
const char *str = string_desc_arr[index];
char_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
// Cap at max char
if (char_count > max_count) {
char_count = max_count;
}
// Convert ASCII string into UTF-16
for (size_t i = 0; i < char_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
}
// First byte is the length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (char_count * 2 + 2));
return _desc_str;
}

View File

@ -24,44 +24,48 @@
* This file is part of the TinyUSB stack.
*/
#include "tusb.h"
#include "bsp/board_api.h"
#include <tusb.h>
#include <bsp/board_api.h>
size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
if (ch <= 0) break;
size_t get_console_inputs(uint8_t* buf, size_t bufsize)
{
size_t count = 0;
buf[count] = (uint8_t) ch;
count++;
}
while (count < bufsize) {
int ch = board_getchar();
if (ch <= 0) break;
return count;
}
void cdc_app_task(void) {
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
uint32_t count = get_console_inputs(buf, bufsize);
buf[count] = 0;
// loop over all mounted interfaces
for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
if (tuh_cdc_mounted(idx)) {
// console --> cdc interfaces
if (count) {
tuh_cdc_write(idx, buf, count);
tuh_cdc_write_flush(idx);
}
buf[count] = (uint8_t) ch;
count++;
}
return count;
}
void cdc_app_task(void)
{
uint8_t buf[64 + 1]; // +1 for extra null character
uint32_t const bufsize = sizeof(buf) - 1;
uint32_t count = get_console_inputs(buf, bufsize);
buf[count] = 0;
// loop over all mounted interfaces
for(uint8_t idx = 0; idx < CFG_TUH_CDC; idx++)
{
if(tuh_cdc_mounted(idx))
{
// console --> cdc interfaces
if(count)
{
tuh_cdc_write(idx, buf, count);
tuh_cdc_write_flush(idx);
}
}
}
}
}
//--------------------------------------------------------------------+
// TinyUSB callbacks
//--------------------------------------------------------------------+
// Invoked when received new data
void tuh_cdc_rx_cb(uint8_t idx) {

86
src_host/main.c Normal file
View File

@ -0,0 +1,86 @@
/*
* 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 <pico/stdlib.h>
#define LED_BLINK_DELAY_MS 1000
extern void cdc_app_task(void);
/*------------- MAIN -------------*/
int main(void) {
board_init();
gpio_set_function(12, UART_FUNCSEL_NUM(uart0, 12));
gpio_set_function(13, UART_FUNCSEL_NUM(uart0, 13));
printf("TinyUSB Host CDC Example\n");
// init host stack on configured roothub port
const tusb_rhport_init_t rh_init = {
.role = TUSB_ROLE_HOST,
.speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL,
};
if(!tuh_rhport_init(BOARD_TUH_RHPORT, &rh_init))
{
printf("Failed to init rhport.\n");
return PICO_ERROR_GENERIC;
}
uint32_t start_ms = 0;
bool led_state = false;
while(true)
{
// tinyusb host task
tuh_task();
if(board_millis() - start_ms > LED_BLINK_DELAY_MS)
{
board_led_write(led_state);
led_state = ! led_state;
start_ms += LED_BLINK_DELAY_MS;
}
cdc_app_task();
}
}
// 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);
}

View File

@ -27,7 +27,7 @@
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------
@ -40,11 +40,11 @@
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#define CFG_TUSB_DEBUG 0
#endif
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
@ -62,63 +62,49 @@
#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// Host Configuration
//--------------------------------------------------------------------
// Enable Host stack
#define CFG_TUH_ENABLED 1
// Enable host stack
#define CFG_TUH_ENABLED 1
#if CFG_TUSB_MCU == OPT_MCU_RP2040
// #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
// #define CFG_TUH_MAX3421 1 // use max3421 as host controller
// #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
// #define CFG_TUH_MAX3421 1 // use max3421 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
// 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 --------------------------
#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
// RHPort number used for host can be defined by board.mk, default to port 0
#ifndef BOARD_TUH_RHPORT
#define BOARD_TUH_RHPORT 0
#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
#define BOARD_TUH_MAX_SPEED OPT_MODE_FULL_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 // 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_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
#define CFG_TUH_HUB 1 // number of supported hubs
#define CFG_TUH_CDC 1 // 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_HID 0
#define CFG_TUH_MSC 0
#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 0x03
@ -129,7 +115,7 @@
#ifdef __cplusplus
}
}
#endif
#endif /* _TUSB_CONFIG_H_ */