本文主要介绍了如何移植TinyUSB并实现CDC+MSC(虚拟串口和大容量存储设)例程

1. TinyUSB库导入

tinyusb库的导入有两种方式,可以下载源码后只复制相关核心源码到自己得工程中,也可以使用git submodule得方式直接添加到子模块。本例使用第二种方式。

我们得工程是使用STM32CubeMX生成得工程,直接在工程目录下增加子模块:

1
git submodule add https://gitee.com/mirrors/TinyUSB.git TinyUSB

检出版本0.14.0,TinyUSB的目录结构中如下:

  • TinyUSB/src 为源码目录以及公共源码
  • TinyUSB/example 为例程目录
  • TinyUSB/src/device 为设备相关源码
  • TinyUSB/src/common 为公共源码
  • TinyUSB/src/class 为设备类相关源码
  • TinyUSB/src/portable 为不同平台的底层驱动文件

其中列出了与本例相关的核心文件,将这些文件以库或者源码的形式添加到CmakeList.txt中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Add library tinyusb
add_library(tinyusb INTERFACE)

# lib tinyusb compile define
target_compile_definitions(tinyusb INTERFACE
CFG_TUSB_MCU=OPT_MCU_STM32G4
# USE_HAL_DRIVER
$<$<CONFIG:Debug>:DEBUG>
)

target_include_directories(tinyusb INTERFACE
./TinyUSB/src
)

target_sources(tinyusb INTERFACE
./TinyUSB/src/tusb.c
./TinyUSB/src/common/tusb_fifo.c
./TinyUSB/src/device/usbd.c
./TinyUSB/src/device/usbd_control.c
./TinyUSB/src/class/cdc/cdc_device.c
./TinyUSB/src/class/msc/msc_device.c
./TinyUSB/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
)

target_link_directories(tinyusb INTERFACE
)

唯一需要注意的是,如果使用到了cdc功能,STM32G系列的USB属于全速设备,port文件使用dcd_stm32_fsdev.c文件

2. 实现CDC和MSC设备关键回调函数

2.1 USB设备的回调函数

与Device设备的几个重要回调函数如下:

  • tud_mount_cb 设备挂载成功回调
  • tud_umount_cb 设备卸载回调
  • tud_suspend_cb 设备挂起回调
  • tud_resume_cb 设备恢复回调

以上我们在任意位置视情况实现功能,本例中默认回调函数为空

1
2
3
4
5
6
7
void tud_mount_cb(void) {}

void tud_umount_cb(void) {}

void tud_suspend_cb(bool remote_wakeup_en) {}

void tud_resume_cb(void) {}

2.2 CDC设备的回调函数

与CDC功能有关的回调函数如下:

  • tud_cdc_line_state_cb 状态变化回调
  • tud_cdc_rx_cb 接收到数据回调

以上我们在任意位置视情况实现功能,本例中默认回调函数为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
(void) itf;
(void) rts;

// TODO set some indicator
if ( dtr )
{
// Terminal connected
}else
{
// Terminal disconnected
}
}

void tud_cdc_rx_cb(uint8_t itf) {
(void) itf;
}

2.3 MSC设备的回调函数

与MSC相关的回调函数如下:

  • tud_msc_inquiry_cb 请求磁盘信息的回调,决定了PC端显示U盘名称等信息
  • tud_msc_test_unit_ready_cb 测试单元读的回调
  • tud_msc_capacity_cb 获取磁盘块大小和数量回调,决定了PC端显示U盘的存储空间
  • tud_msc_start_stop_cb PC端Star/Stop命令回调
  • tud_msc_read10_cb 读数回调,PC端读取文件的处理
  • tud_msc_is_writable_cb 可写属性回调,决定磁盘是否可写
  • tud_msc_write10_cb 写数据回调,PC端写入文件处理
  • tud_msc_scsi_cb 其他SCSI命令回调

关于这部分的实现,我们拷贝TinyUSB->example->device->cdc_msc->src目录下msc_disk.c、usb_descriptors.c、tusb_config.h三个文件到自己的工程文件下,并加载到工程CmakeList.txt

1
2
3
4
5
6
7
8
9
10
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
...
./Board/spi_flash.c
...
./UserApp/app_main.c
./UserApp/msc_disk.c
./UserApp/usb_descriptors.c
)

在tusb_config.h文件中,暂时不更改任何内容,注释掉msc_disk.c中的头文件

1
2
// #include "bsp/board.h"  
#include "tusb.h"

可以在头文件中指定MCU类型,也可以在CmakeList.txt中指定

1
2
3
4
5
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined symbols
CFG_TUSB_MCU=OPT_MCU_STM32G4
...
)

以上相关移植和配置工作完成,最后只需要在主程序中初始化并循环执行即可,如果在编译遇到报错,请检查上述内容是否有遗漏

1
2
3
4
5
6
7
void Main() {
tud_init(0);
for (;;) {
tud_task();
tud_cdc_write_flush();
}
}

3. 实现效果

烧录成功后,插入USB,PC端会自动弹出U盘,显示默认README.txt文件。