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 created an STM32 USB Device Mass Storage Class using the internal RAM memory. In this tutorial, we are going to implement STM32 USB Device MSC using Internal Flash memory – STM32 USB Device MSC Tutorial Part 2.
Table of Contents
Hardware Required
- STM32F767Zi Nucleo Board
- Micro USB Cable (which supports data transfer)
STM32 USB Device MSC using Internal Flash
In our previous article, we implemented the STM32 USB Device MSC using the RAM. In that, when we connect the User USB (CN13) to the PC, it detects as Mass Storage (200KB) in the PC and we can do read, write, and format the device.
In this article, we are going to see how to use the Internal Flash memory as Mass Storage. First, we will see the Flash memory which is available in the STM32F767Zi microcontroller.
STM32F767Zi Flash Memory
Note: If you are using another STM32 microcontroller, then please check its datasheet for the correct information.
STM32F767Zi has the capacity of up to 2 Mbytes, in single bank mode (read width 256 bits). We are going to use this Flash memory as a single bank mode. Check the below image.
In these sectors, we are going to use Sector 6 (0x08080000) for this STM32 USB Mass Storage Class which has 256Kb. The Flash memory erase operation can be performed at the sector level. So, If we want to write one byte, then we need to erase the complete sector and write it. So, writing operation is costly.
Problem with using Flash for USB Device MSC
Before we start, We want to tell you the problems with this method.
- The Write operation will be very very slow as the write operation is very slow in the FLASH memory and we are doing FLASH erase for every write.
- This process may damage the FLASH memory over the period as we are doing many write operations even for single-byte write. If we overwrite a 1KB file, you will need at least 4 write operations (probably more). Flash memory can withstand between 10,000 to 100,000 write/erase cycles, depending on the memory technology used. When the limit is reached, some portion of the memory may not function properly, leading to loss of data and corruption.
Since this method has many flaws, We won’t suggest anyone to implement this. This is just for education purposes only. In this example, we are going to use 256Kb of FLASH memory. If we reduce this size, then it will improve the speed a little bit. So, keep these things in your mind.
STM32 USB Device Example using Flash Memory
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 USB OTG
Click “Connectivity” –> USB_OTG_FS, then set the Mode as Device_Only. Refer to the below image.
Configure USB Device
Now, click Middleware and Software –> USB_DEVICE. Then set the Class forFS IP to Mass Storage Host Class. Then MSC_MEDIA_PACKET to 32KB (32768 Bytes). Please refer to the below image.
Configure Clock
We need to give the 48MHz to the USB. 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.
Then configure the clock like below.
Save the .ioc file and generate the code.
Source Code – STM32 USB Device MSC using Flash Example
In this example, we are going to use Sector 6 of the Flash memory for this STM32 USB Device example. The address of the Sector 6 is 0x08080000
. In that, we are going to use 256Kb bytes of data for this purpose. (You can reduce this size to speed up the operations).
[You can get the complete project source code on GitHub]
Open the USB_DEVICE–>App–>usbd_storage_if.c
file. In that file, we have to provide the allocated memory size. We decided to use 256KB bytes. In that code, block size (STORAGE_BLK_SIZ
) is defined as 0x200 (512 bytes). So in order to use 256Kb, we need to assign 512 blocks (512 x 0x200 = 256KB). Thus, set STORAGE_BLK_NBR
as 512. Refer to the code below.
#define STORAGE_LUN_NBR 1 #define STORAGE_BLK_NBR 512 #define STORAGE_BLK_SIZ 0x200
Then add the below code between USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION and USER CODE END PRIVATE_FUNCTIONS_DECLARATION.
#include <stdbool.h> #define USB_FLASH_START_ADDRESS ( 0x08080000 ) //USB Flash Address (Sector 6 in STM32F767Zi) #define TOTAL_USB_DEVICE_SIZE ( STORAGE_BLK_NBR * STORAGE_BLK_SIZ ) /** * @brief Writes data into the FLASH. * @param buf: data buffer. * @param blk_addr: Logical block address. * @param blk_len: Blocks number. * @retval HAL_StatusTypeDef */ static HAL_StatusTypeDef write_data_to_flash( uint8_t *buf, uint32_t blk_addr, uint16_t blk_len ) { HAL_StatusTypeDef ret = HAL_OK; uint8_t data[ TOTAL_USB_DEVICE_SIZE ]; do { /* First copy the data to the local buffer from Flash */ memcpy( data, (const void *)USB_FLASH_START_ADDRESS, TOTAL_USB_DEVICE_SIZE ); /* Make modifications in the local buffer */ memcpy((void*)&data[blk_addr*STORAGE_BLK_SIZ], buf, (blk_len*STORAGE_BLK_SIZ)); ret = HAL_FLASH_Unlock(); if( ret != HAL_OK ) { break; } /* Erase the Flash */ FLASH_EraseInitTypeDef EraseInitStruct; uint32_t SectorError; EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Sector = FLASH_SECTOR_6; EraseInitStruct.NbSectors = 1; //erase 1 sector(6) EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; ret = HAL_FLASHEx_Erase( &EraseInitStruct, &SectorError ); /* Write the data to the Flash */ for( uint32_t i = 0; i < TOTAL_USB_DEVICE_SIZE; i++) { ret = HAL_FLASH_Program( FLASH_TYPEPROGRAM_BYTE, ( USB_FLASH_START_ADDRESS + i ), data[i] ); if( ret != HAL_OK ) { break; } } HAL_FLASH_Lock(); } while( false ); return( ret ); }
In the above code, we are using write_data_to_flash()
function to write the data to the flash memory. In that function, we are creating a local buffer of 256Kb. If you don’t have enough space in your microcontroller, please reduce the size of the USB device MSC through this macro STORAGE_BLK_NBR
.
Then we copy the data from the Flash memory to the local buffer. Then we write the data in the local buffer. Finally, we need to write the data back to the Flash memory. If we want to write the data, we need to erase the flash sector. So, we are erasing the flash and writing the data. We can call this function from the STORAGE_Write_FS()
function. The STORAGE_Write_FS()
looks like below.
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 7 */ return write_data_to_flash( buf, blk_addr, blk_len ); /* USER CODE END 7 */ }
Add the read functionality to the STORAGE_Read_FS()
function. For writing only we need to unlock, erase, and lock the Flash memory. But we can read the data from Flash directly. So, memcpy()
itself is enough. The STORAGE_Read_FS()
function looks like below.
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 6 */ /* We can read directly from the Flash address */ memcpy( buf, (const void *)(USB_FLASH_START_ADDRESS + ( blk_addr * STORAGE_BLK_SIZ )), (blk_len * STORAGE_BLK_SIZ) ); return (USBD_OK); /* USER CODE END 6 */ }
That’s it. You can get the complete project from the GitHub.
Hardware Connection – STM32 USB Device
There are two USB ports available in this STM32F767Zi Nucleo Board. One is for ST-Link, and the other is for our USB communication. CN13 USB port is ours. So connect both USB Ports to the Computer (One is to flash the code, the other one is for USB data transfer). Refer to the below image.
Demo – STM32 USB Device Example
The code is ready. Build the code and flash it. You can see our USB drive. When you open it for the first time, it will ask you to format that USB drive. Format it and open that. You can create the file and do your file operations. Format, writing will take more time and you have to wait for a few seconds to a few minutes based on the data size.
Please read the other STM32 Tutorials.
In our next article, we will see STM32 USB Device MSC using an SD Card.
You can read the below tutorials.
Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!