Within series of articles devoted to STM32CubeMx we start discussing different USB modes. And today we'll realize USB Mass Storage Device class with SD-Card connected to the MCU. Thus, microcontroller STM32F10x acting as a card reader will be the result of this post 🙂
As I've mentioned at the beggining of the post, I'll use STM32F10x microcontroller. SD-card will be connected via SDIO interface. So, let's run STM32CubeMx and begin the configuring of MCU units! First of all, we should enable high-speed external oscillator, USB and SDIO units. Moreover, the proper USB mode should be selected. In this case it is Mass Storage Class mode:
Please note that when we enable any microcontroller unit, STM32CubeMx automatically marks all involved pins. In addition, I'll use PA10 as USB_DISCONNECT_PIN - it is responsible for software connection and disconnection of USB. So, I'll configure this GPIO as "GPIO_Output".
General configuration is quite clear, and now we proceed to the "Clock configuration" tab. All the frequencies can be set there. While working with USB, the most important point of clock configuration is 48 MHz at USB clock:
After that we can do some extra configuration. In order to do this let's open the "Configuration" tab and click on SDIO unit. As a result a new window will appear, where we would be able to set SDIO clock prescaler:
In principle, after that all units are configured properly, so we can start the project generation process!
When the generation is finished, the project is ready for programming the MCU, but we have to make some modifications in the source files. Firstly, I'll reset the output state of USB_DISCONNECT_PIN:
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_SDIO_SD_Init(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET); MX_USB_DEVICE_Init();
As you see, Cube has already initialised SDIO and USB, but it doesn't take care about relations between these units. Thus, we should edit usbd_storage_if.c file, where all the functions needed for USB interface are located:
STORAGE_Init_FS, STORAGE_GetCapacity_FS, STORAGE_IsReady_FS, STORAGE_IsWriteProtected_FS, STORAGE_Read_FS, STORAGE_Write_FS, STORAGE_GetMaxLun_FS, (int8_t *)STORAGE_Inquirydata_FS
STMCubeMx doesn't fill this functions, so they are empty. Let's edit some of them in order to make our device working! Firstly, we should correct the block size:
#define BLOCK_SIZE 512
Secondly, STORAGE_GetCapacity_FS() function has to be modified. To get SD-card full size we can use HAL_SD_Get_CardInfo(). This function takes two arguments which are defined in the main.c file. So, they should be defined in usbd_storage_if.c file too:
extern SD_HandleTypeDef hsd; extern HAL_SD_CardInfoTypedef SDCardInfo;
STORAGE_GetCapacity_FS() should return the block size and the number of blocks, and now we can calculate it. We should just divide the total size by the size of one block:
int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size) { /* USER CODE BEGIN 3 */ HAL_SD_Get_CardInfo(&hsd, &SDCardInfo); *block_num = SDCardInfo.CardCapacity / BLOCK_SIZE; *block_size = BLOCK_SIZE; return (USBD_OK); /* USER CODE END 3 */ }
Thirdly, the read and write functions should be implemented:
/**************************************************************************************** * Function Name : STORAGE_Read_FS * Description : * Input : None. * Output : None. * Return : None. ****************************************************************************************/ int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 6 */ HAL_SD_ReadBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len); return (USBD_OK); /* USER CODE END 6 */ } /**************************************************************************************** * Function Name : STORAGE_Write_FS * Description : * Input : None. * Output : None. * Return : None. ****************************************************************************************/ int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 7 */ HAL_SD_WriteBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len); return (USBD_OK); /* USER CODE END 7 */ }
We've just added the code which will read/write data from/to SD-card connected to SDIO interface.
Important note!
HAL_SD_ReadBlocks() and HAL_SD_WriteBlocks() functions' prototypes have been changed in the newest STM32CubeMx versions. So now we should rewrite USB callbacks in such a way:
/**************************************************************************************** * Function Name : STORAGE_Read_FS * Description : * Input : None. * Output : None. * Return : None. ****************************************************************************************/ int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 6 */ HAL_SD_ReadBlocks(&hsd, buf, blk_addr, (uint32_t) blk_len, 10); return (USBD_OK); /* USER CODE END 6 */ } /**************************************************************************************** * Function Name : STORAGE_Write_FS * Description : * Input : None. * Output : None. * Return : None. ****************************************************************************************/ int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 7 */ HAL_SD_WriteBlocks(&hsd, buf, blk_addr, (uint32_t) blk_len, 10); return (USBD_OK); /* USER CODE END 7 */ }
Where '10' is a timeout value for read/write operations.
Now the project is ready for compiling and programming the MCU! If we connect STM32 to the PC, we'll see a new USB Mass storage device in our system. Thus, we can create, edit and delete files on SD-card.
Well, I'll stop here, if you have any questions, you can add a comment on this post, I'll be very glad to help you!
If you have problems with USB you can try to increase Stack_size and Heap_size in startup_stm32xxxx.s/ Sometimes it helps 🙂