STM32 USB Host MSC Tutorial – Connect Pendrive to STM32

This is the Series of tutorials on the STM32 Microcontroller. The aim of this series is to provide easy and practical examples that anyone can understand. In our last article, we have seen STM32 UART DMA Example. In this tutorial, we are going to connect Pendrive to STM32 – STM32 USB Host MSC Tutorial.

Hardware Required

STM32 USB Host MSC – Connect Pendrive to STM32

STM32F767Zi has USB OTG Full Speed and USB OTG High-Speed capability (with the ULPI). The USB OTG is a dual-role device (DRD) controller that supports both device and host functions and is fully compliant with the On-The-Go Supplement to the USB 2.0 Specification. It can also be configured as a host-only or device-only controller, fully compliant with the USB 2.0 Specification.

In this article, we are going to see STM32 USB Host MSC (Host-Only mode).

We need to give the 48MHz clock to the USB OTG from RCC. The STM32 Nucleo-144 board supports USB OTG or device-full-speed communication via a USB Micro-AB connector (CN13) and USB power switch (U12) connected to VBUS.

STM32 USB Host MSC Example

Project Creation

Create the new STM32 project in STM32CubeIDE.

Note: This project was set up with STM32CubeMx V6.10 using STM32Cube FW_F7 V1.17.1.

Configure USART3 for Debug Prints

In the STM32F767Zi Nucleo board, USART3 is available as a virtual COM Port. So, we can use this USART3 for our debug prints. In the .ioc file, Click “Connectivity” –> “USART3“. Select Mode as Asynchronous. By default, PB10 and PB11 will be configured. But in our STM32F767Zi Nucleo board, PD8 and PD9 are used for Virtual Com Port. So, configure the PD9 and PD8 for USART3. Refer to the below image. The baud rate is 115200.

USART3 Configure

Configure USB OTG

Click USB_OTG_FS, then set the Mode as Host_Only. After that, Activate the VBUS. Refer to the below image.

USB_OTG_FS

Configure USB HOST

In the STM32F767Zi Nucleo board, PG6 GPIO is used to configure the VBUS. Refer to the below schematic diagram of the USB Connection.

STM32F7 VBUS Schematics

So configure the PG6 pin as Output like the below image. And set this GPIO to High by default.

Now, click Middleware and Software –> USB_HOST. Then set the Class forFS IP to Mass Storage Host Class. Then in the Platform Settings, configure the PG6 to VBUS. Please refer to the below image.

USB Host VBUS configure

Now, we will enable the USB Log messages which will help us to debug the USB if any error. Click Parameter Settings and set the USBH_DEBUG_LEVEL to 3 like the below image.

USB_HOST Configure

Configure FatFs

Now we will configure the FAT filesystem. Click FATFS and enable the USB Disk. Then set the MAX_SS to 4096 like the below image.

FATFS Configure

Configure Clock

We need to give the 48MHz to the USB. So configure the clock like the below image. We are going to use our HSE clock which gets 8MHz. Click RCC and Set the HSE to Crystal/Ceramic Resonator like the below image.

STM32 USB Clock

Then configure the clock like below.

STM32 USB Clock

Save the .ioc file and generate the code.

Source Code – STM32 USB Host MSC Example

[You can get the complete project source code on GitHub]

Note: If you are using the STM32F767Zi Nucleo board or STM32G0 board and STM23F7 Firmware Package V1.17.1, it has USB Host V3.4.1. This USB Host package has a bug. So, the USB won’t work as expected. To solve that issue, download the USB Host V3.5.0.

Delete the STM32_USB_Host_Library directory in the source code Extract this downloaded file instead of the deleted one. Check the below image.

STM32_USB_Host_Library

Note: Whenever you are re-generating the code, you need to replace that directory with the downloaded code once again.

Now add the below code to the USB_HOST--> App-->usb_host.c between USER CODE BEGIN 1 and USE CODE END 1.

/* USER CODE BEGIN 1 */
#include <stdio.h>
#include "fatfs.h"

extern UART_HandleTypeDef huart3;

/**
  * @brief Print the characters to UART (printf).
  * @retval int
  */
#ifdef __GNUC__
  /* With GCC, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
int __io_putchar(int ch)
#else
int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the UART3 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}

static FRESULT ETX_MSC_ProcessUsbDevice(void)
{
  FATFS     UsbDiskFatFs;                                 /* File system object for USB disk logical drive */
  char      UsbDiskPath[4] = {0};                         /* USB Host logical drive path */
  FIL       file;                                         /* File object */
  FRESULT   res;                                          /* FatFs function common result code */
  uint32_t  total_space, free_space;                      /* Total Space and Free Space */
  DWORD     fre_clust;                                    /* Freee Cluster */
  uint32_t  byteswritten, bytesread;                      /* File write/read counts */
  uint8_t   wr_data[] = "Welcome to EmbeTronicX!!!";      /* Data buffer */
  uint8_t   rd_data[100];                                 /* Read buffer */
  char      file_name[] = "temp.txt";                     /* File name */

  do
  {
    /* Register the file system object to the FatFs module */
    res = f_mount( &UsbDiskFatFs, (TCHAR const*)UsbDiskPath, 0 );
    if( res != FR_OK )
    {
      /* FatFs Init Error */
      break;
    }

    /* Check the Free Space */
    FATFS *fatFs = &UsbDiskFatFs;
    f_getfree("", &fre_clust, &fatFs);
    total_space = (uint32_t)((UsbDiskFatFs.n_fatent - 2) * UsbDiskFatFs.csize * 0.5);
    free_space = (uint32_t)(fre_clust * UsbDiskFatFs.csize * 0.5);
    printf("USB Device Total Space = %lu MB\n", total_space/1024);
    printf("USB Device Free Space  = %lu MB\n", free_space/1024);

    /* Create a new text file with write access */
    res = f_open( &file, file_name, ( FA_CREATE_ALWAYS | FA_WRITE ) );
    if( res != FR_OK )
    {
      /* File Open Error */
      break;
    }

    /* Write the data to the text file */
    res = f_write( &file, wr_data, sizeof(wr_data), (void *)&byteswritten );

    /* Close the opened file */
    f_close( &file );

    if( res != FR_OK )
    {
      /* File write Error */
      break;
    }

    printf("Data written to the USD Device\n");

    /* Open the text file object with read access */
    res = f_open( &file, file_name, FA_READ );
    if( res != FR_OK )
    {
      /* File Open Error */
      break;
    }

    /* Read data from the file */
    res = f_read( &file, rd_data, sizeof(wr_data), (void *)&bytesread);

    /* Close the file */
    f_close(&file);

    if(res != FR_OK)
    {
      /* File Read Error */
      break;
    }

    /* Print the data */
    printf("Read Data : %s\n", rd_data);

  } while ( 0 );

  /* Unmount the device */
  f_mount(NULL, UsbDiskPath, 0);

  /* Unlink the USB disk driver */
  FATFS_UnLinkDriver(UsbDiskPath);

  return res;
}
/* USER CODE END 1 */

In the above code, __io_putchar or fputc is used for printf function. When we use printf, the prints will be sent to the USART3. So, we can use this for debugging purposes.

ETX_MSC_ProcessUsbDevice() function mounts the USB device and prints the storage details. Then it creates the file named temp.txt and writes the string (“Welcome to EmbeTronicX!!!”) to that file. Then read back the content of the file and print it. Finally, it unmounts the USB device and unlinks the USB disc path.

Add the below code to the function USBH_UserProcess()‘s case HOST_USER_CLASS_ACTIVE.

  if( ETX_MSC_ProcessUsbDevice() != FR_OK )
  {
    printf("USB Device Process Error\n");
    /* Error : Hang Here */
    while(1);
  }

So the complete USBH_UserProcess() function looks like below.

static void USBH_UserProcess  (USBH_HandleTypeDef *phost, uint8_t id)
{
  /* USER CODE BEGIN CALL_BACK_1 */
  switch(id)
  {
  case HOST_USER_SELECT_CONFIGURATION:
  break;

  case HOST_USER_DISCONNECTION:
  Appli_state = APPLICATION_DISCONNECT;
  break;

  case HOST_USER_CLASS_ACTIVE:
  Appli_state = APPLICATION_READY;
  if( ETX_MSC_ProcessUsbDevice() != FR_OK )
  {
    printf("USB Device Process Error\n");
    /* Error : Hang Here */
    while(1);
  }
  break;

  case HOST_USER_CONNECTION:
  Appli_state = APPLICATION_START;
  break;

  default:
  break;
  }
  /* USER CODE END CALL_BACK_1 */
}

USBH_UserProcess is a state machine that checks the different states, for eg- when the USB Connection, Disconnection, or When it is Ready for Communication. We will write our program once the Application is READY (APPLICATION_READY). That means the communication is established. So, We can do our file operations. That’s why we have called our function once the state is set to APPLICATION_READY.

Note: If you are using STM32F407 Discovery Board, then you need to check one more thing. The MX_DriverVbusFS() function from USB_HOST_Target->usbh_platform.c file should be like below. If state ==0, then we need to do GPIO_PIN_SET, otherwise GPIO_PIN_RESET.

/**
  * @brief  Drive VBUS.
  * @param  state : VBUS state
  *          This parameter can be one of the these values:
  *           - 1 : VBUS Active
  *           - 0 : VBUS Inactive
  */
void MX_DriverVbusFS(uint8_t state)
{
  uint8_t data = state;
  /* USER CODE BEGIN PREPARE_GPIO_DATA_VBUS_FS */
  if(state == 0)
  {
    /* Drive high Charge pump */
    data = GPIO_PIN_SET;
  }
  else
  {
    /* Drive low Charge pump */
    data = GPIO_PIN_RESET;
  }
  /* USER CODE END PREPARE_GPIO_DATA_VBUS_FS */
  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,(GPIO_PinState)data);
}

Hardware Connection – STM32 USB Host

We have connected the Micro USB OTG Adapter Host Cable and pen drive. Then connect the OTG Cable to the CN13 User USB. Check the below image. And make sure the JP4 (USB_EN) is ON.

stm32 usb host msc example - Connection

Demo – STM32 USB Host FatFs Example

Connect the Pendrive to the PC and format it in the FAT32 filesystem.

The code is ready. Build the code and flash it. We have connected 16GB Sandisk Pendrive. You should get the prints like below.

USB Device Connected
USB Device Reset Completed
PID: 5567h
VID: 781h
Address (#1) assigned.
Manufacturer : SanDisk
Product : Cruzer Blade
Serial Number : 03024529092623093708
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : 8h
SubClass : 6h
Protocol : 50h
MSC class started.
Number of supported LUN: 1
LUN #0:
Inquiry Vendor  : SanDisk
Inquiry Product : Cruzer Blade
Inquiry Version : 1.00
MSC Device ready
MSC Device capacity : 3120561664 Bytes
Block number : 31260671
Block Size   : 512
USB Device Total Space = 15247 MB
USB Device Free Space  = 15247 MB
Data written to the USD Device
Read Data : Welcome to EmbeTronicX!!!

Connect the Pendrive to the PC and check. There should be a file called temp.txt and if you open it, it should contain a “Welcome to EmbeTronicX!!!” string.

Please read the other STM32 Tutorials.

In our next article, we will see STM32 USB Device MSC.

You can read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials – STM32Zephyr RTOS Tutorials – ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Table of Contents