Back

MSD using tinyUSB stack in STM32

Getting Started with TinyUSB in STM32

TinyUSB is an open-source cross-platform USB Host/Device stack for embedded systems, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events being deferred and then handled in the non-ISR task function.

Introduction

The ST’s USB stack that is included in STM32CubeIDE, has lots of problems, including:

  1. Large footprint.
  2. All of the USB services are running on ISR
  3. Can’t correctly use it in RTOS

In this article, I’m going to show you how to use TinyUSB stack to create a USB mass storage device (MSD) using STM32.

My Environment

Syntax Description
OS Windows 10 (64bit)
Language C
IDE/Compiler STM32CubeIDE/GCC
Target STM32F429ZITx
STM32Cube Firmware FW_F4 V1.26.2
TinyUSB Version 0.13.0

Hardware

I’m using a custom board with STM32F429ZI, but process isn’t diffrent for other chips and other supported ST MCUs. My board uses Full Speed USB connected to USB_OTG_FS of MCU, based on your hardware, you may also use High Speed or Full Speed USB connected to USB_OTG_HS of MCU.

Project Configuration in CubeMX

USB Peripheral

As i mentioned before, i’m using USB_OTG_FS for usb communication, so first step is to enable this peripheral in cube mx. to do so, must set the Mode of USB_OTG_FS to Device Only and enable global interrupt of USB in NVIC Settings tab of Configuration window.

USB configuration in CubeMX

Clock Configuration

After enabling USB hardware, we must configure the clock of the MCU.

Note

USB peripheral in STM32 need external clock source to work!

Clock configuration in CubeMX

Importing TinyUSB library into project

Clone or download the TinyUSB reposetory in a folder. The library itself is in src folder in repository and example codes are in examples.

Create a new folder in Core folder of project structure and name it TinyUSB. Then right click on it and select Import. In the open window select File System (in General category), then click on Browse and navigate to the downloaded folder from last step and select src folder. In the Import window, select src folder, the rest of options in this window must be the same as following image and click on Finish.

Importing TinyUSB libraries in CubeIDE

The next step is to add the folder to include paths. To do so, right click on project (or Project menu) and select Properties. Then go to C/C++ General -> Paths and Symbols -> Includes and add “Core/TinyUSB”, then click on Apply and Close.

Adding TinyUSB libraries to Include Path in CubeIDE

The Code

Adding example files for MSD to project

TinyUSB MSD example is in TinyUSB repository -> examples -> device -> msc_dual_lun. from src folder, import or copy msc_disk_dual.c and usb_descriptors.c files into Src folder of the project structure and tusb_config.h into Inc folder of the project structure.

Project Structure

Edit example codes

In msc_disk_dual.c, remove #include “bsp/board.h” to prevent causing error when compling the code. The default way to configure TinyUSB is to pass configuration symbols in compile time (in board.mk file). to make things easier i directly define the needed symbols in tusb_config.h file in project.

//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUSB_MCU                 OPT_MCU_STM32F4
#define BOARD_DEVICE_RHPORT_SPEED    OPT_MODE_FULL_SPEED
#define BOARD_DEVICE_RHPORT_NUM      0
#define CFG_TUSB_RHPORT_MODE         (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)

Redirecting STM32 USB interrupt to TinyUSB stack

Add below code to stm32f4xx_it.c (or it’s equivalent if you are using another STM32 family).

/* USER CODE BEGIN Includes */
#include "tusb.h"
/* USER CODE END Includes */

...

/**
 * @brief This function handles USB On The Go FS global interrupt.
 */
void OTG_FS_IRQHandler(void)
{
	/* USER CODE BEGIN OTG_FS_IRQn 0 */
	tud_int_handler(BOARD_DEVICE_RHPORT_NUM);
	return;
	/* USER CODE END OTG_FS_IRQn 0 */
	HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
	/* USER CODE BEGIN OTG_FS_IRQn 1 */

	/* USER CODE END OTG_FS_IRQn 1 */
}

After doing above steps, we must first initialize TinyUSB stack by calling tusb_init() and then continuously calling tud_task() in the main loop of program (or in a task if using RTOS) to service the USB requests.

/* USER CODE BEGIN Includes */
#include "tusb.h"
/* USER CODE END Includes */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
tusb_init();
while (1)
{
	tud_task();
	/* USER CODE END WHILE */

	/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

Now compile and flash the code.

Info

The TinyUSB version that i used (0.13.0), has a code guard bug for synopsis port, that cause the multiple multiple definition of … error when compling the code. for simplicity i deleted all ports in portable folder of imported TinyUSB library but the ST port from the project.

Deleting unused ports

After deleting the mentioned files, performe a clean build of project!

Now when connecting the board to PC (via USB!), you should see to mass storage devices (This is a two LUN example):

Mass Storage Devices


Refrences:

  1. TinyUSB Offital website
  2. TinyUSB GitHUB repository
  3. A CDC (CDC+MSD) example using TinyUSB and STM32
Built with Hugo
Theme Stack designed by Jimmy