2. STM32F4 USB协议研究 - SD卡模拟U盘

有用的函数

串口打印

#include "stdarg.h"

int USART1Printf(const char* format, ...)
{
    
    
	static char sendBuff[100] = {
    
     0 };//发送缓冲区
	int bytes = 0;
	va_list list;

	va_start(list, format);
	bytes = vsprintf(sendBuff, format, list);//格式化输入
	va_end(list);
	/* 发送之前清除标志位 */
	CLEAR_BIT(huart1.Instance->SR, USART_SR_TC_Msk);//往TC位写入0来清除TC位
	HAL_UART_Transmit(&huart1, (void*)sendBuff, bytes, HAL_MAX_DELAY);//阻塞式发送数据,发送等待时间为最大等待时间

	return bytes;
}

USB SD卡 U盘核心流程

USB设备首先分为Host和Device.

作为Device, 可以再分为:

  • Audio Device Class 音频
  • Communication Device Class(Virtual Port Com)
  • Download Firmware Update Class(DFU) 固件升级
  • Human Interface Device Class(HID) 键盘鼠标游戏手柄画板
  • Custom Human interface Device Class(HID) 大多是HID混合设备
  • Mass Storage Class(U盘一类存储设备)

USB SD卡

  1. RCC, HSE Crystal/Ceramic Resonator
  2. Clock, source=8M, HSE, HCLK=168MHz
  3. SDIO, Mode=SD 4 Bits Wide bus, others default.(SD卡驱动,默认512 block size)
  4. USB_OTG_FS, Mode=Device_Only, Speed=12M, others disabled. PA11,PA12, USB1. (USB速度和种类)
  5. USB_DEVICE, Class For FS IP=Mass Storage Class, others default. 1,1,Size=512. (USB注册为存储设备)
  6. USART1, Mode=Asynchronous, 115200,8,None,1, 16 Samples. (用来进行调试输出)

时钟问题(重点,核心)

SDIO的时钟是有讲究的,默认使用48MHz专用频率,但是,如果不使用DMA方式,MCU是无法跟上读写速度,导致模拟出来的U盘不能格式化。
这个问题有好几个地方需要确认:

  1. 检查USB FS和Class设定
  2. 使用内存数据作为U盘存储(而不是SD卡),检查功能是否正常。
  3. 参考SD Clock计算,检查读写HAL_SD_WriteBlocks/HAL_SD_ReadBlocks错误代码,如果是同一类错误,说明时钟仍然有问题。建议控制在1M左右,然后加上读写状态判断等待,再继续下一次读写。(大多文章都是互相抄,不会讲这块问题)

出问题参考文章:

  • https://blog.csdn.net/ZLK1214/article/details/121388735 <= 推荐
  • https://blog.csdn.net/c_1969/article/details/123349427

总结:

  • HAL_SD_WriteBlocks/HAL_SD_ReadBlocks 读写超时,或者错误,一般都是时钟/分频问题。
  • 读写都是以块为单位,默认是512.

核心代码

sdio.c

// 省略其他常规代码
void MX_SDIO_SD_Init(void)
{
    
    

  /* USER CODE BEGIN SDIO_Init 0 */

  /* USER CODE END SDIO_Init 0 */

  /* USER CODE BEGIN SDIO_Init 1 */

  /* USER CODE END SDIO_Init 1 */
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 46;
  if (HAL_SD_Init(&hsd) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /* USER CODE BEGIN SDIO_Init 2 */

  /* USER CODE END SDIO_Init 2 */

}

usbd_storage_if.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : usbd_storage_if.c
  * @version        : v1.0_Cube
  * @brief          : Memory management layer.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"

/* USER CODE BEGIN INCLUDE */
#include "sdio.h"
/* USER CODE END INCLUDE */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @brief Usb device.
  * @{
  */

/** @defgroup USBD_STORAGE
  * @brief Usb mass storage device module
  * @{
  */

/** @defgroup USBD_STORAGE_Private_TypesDefinitions
  * @brief Private types.
  * @{
  */

/* USER CODE BEGIN PRIVATE_TYPES */

/* USER CODE END PRIVATE_TYPES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Defines
  * @brief Private defines.
  * @{
  */

#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  0x10000
#define STORAGE_BLK_SIZ                  0x200

/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Macros
  * @brief Private macros.
  * @{
  */

/* USER CODE BEGIN PRIVATE_MACRO */

/* USER CODE END PRIVATE_MACRO */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Variables
  * @brief Private variables.
  * @{
  */

/* USER CODE BEGIN INQUIRY_DATA_FS */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {
    
    /* 36 */

  /* LUN 0 */
  0x00,
  0x80,
  0x02,
  0x02,
  (STANDARD_INQUIRY_DATA_LEN - 5),
  0x00,
  0x00,
  0x00,
  'S', 'T', 'M', '3', '2', 'F', '4', ' ', /* Manufacturer : 8 bytes */
  'A', 'n', 'd', 'y', ' ', ' ', ' ', ' ', /* Product      : 16 Bytes */
  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  '1', '.', '0' ,'0'                      /* Version      : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */

/* USER CODE BEGIN PRIVATE_VARIABLES */

/* USER CODE END PRIVATE_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Exported_Variables
  * @brief Public variables.
  * @{
  */

extern USBD_HandleTypeDef hUsbDeviceFS;

/* USER CODE BEGIN EXPORTED_VARIABLES */

/* USER CODE END EXPORTED_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
  * @brief Private functions declaration.
  * @{
  */

static int8_t STORAGE_Init_FS(uint8_t lun);
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
static int8_t STORAGE_IsReady_FS(uint8_t lun);
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS(void);

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
extern int USART1Printf(const char* format, ...);
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */

/**
  * @}
  */

USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
    
    
  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
};

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  Initializes the storage unit (medium) over USB FS IP
  * @param  lun: Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Init_FS(uint8_t lun)
{
    
    
  /* USER CODE BEGIN 2 */
	HAL_SD_CardInfoTypeDef info;
	HAL_SD_GetCardInfo(&hsd, &info);

	USART1Printf("SD Type: %d, Version: %d, Class: %d\r\n", info.CardType, info.CardVersion, info.Class);
	USART1Printf("SD Card: Nbr %d M, Blk sz: %d\r\n", info.BlockNbr >>11, info.BlockSize);
	USART1Printf("SD Card Capacity: %d M, Block: %d\r\n", info.LogBlockNbr>>11, info.LogBlockSize);

	// test read write
	// blck content
	char buff[512];
	for(int i=0;i<512;i++){
    
    
		buff[i] = '0'+i%10;
	}
	uint8_t state = 0;
	for(int i=0;i<100;i++){
    
    
		state = HAL_SD_WriteBlocks(&hsd, (uint8_t*)buff, i, 1, 5000);
		USART1Printf("%d => write %d, code: %d\r\n", i, state, hsd.ErrorCode);
		int n = 5000;
		while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){
    
     if(n-- == 0) break; } ;
	}
	for(int i=0;i<100;i++){
    
    
		state = HAL_SD_ReadBlocks(&hsd, (uint8_t*)buff, i, 1, 5000);
		USART1Printf("%d => read %d, code: %d\r\n", i, state,hsd.ErrorCode);
		int n = 5000;
		while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){
    
     if(n-- == 0) break; } ;
	}
  return (USBD_OK);
  /* USER CODE END 2 */
}

/**
  * @brief  Returns the medium capacity.
  * @param  lun: Logical unit number.
  * @param  block_num: Number of total block number.
  * @param  block_size: Block size.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
    
    
  /* USER CODE BEGIN 3 */
	HAL_SD_CardInfoTypeDef info;
	//if(HAL_SD_GetCardState(&hsd) ==  HAL_SD_CARD_TRANSFER)
	{
    
    
		HAL_SD_GetCardInfo(&hsd, &info);
		*block_num =  2*1024*1;// 10M  info.BlockNbr;
		*block_size = info.BlockSize;
		return  USBD_OK;
	}
//	return  USBD_FAIL;
  /* USER CODE END 3 */
}

/**
  * @brief   Checks whether the medium is ready.
  * @param  lun:  Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
    
    
  /* USER CODE BEGIN 4 */
  uint8_t state = 0;
  state = HAL_SD_GetState(&hsd) ;
  //USART1Printf("STORAGE_IsReady_FS %d\r\n", state);
  if(HAL_SD_STATE_READY != state)
  {
    
    
	  return USBD_FAIL ;
  }
  return (USBD_OK);
  /* USER CODE END 4 */
}

/**
  * @brief  Checks whether the medium is write protected.
  * @param  lun: Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
    
    
  /* USER CODE BEGIN 5 */
  return (USBD_OK);
  /* USER CODE END 5 */
}

/**
  * @brief  Reads data from the medium.
  * @param  lun: Logical unit number.
  * @param  buf: data buffer.
  * @param  blk_addr: Logical block address.
  * @param  blk_len: Blocks number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    
    
  /* USER CODE BEGIN 6 */

    int8_t ret = USBD_OK;
//    if( HAL_SD_STATE_READY == HAL_SD_GetState(&hsd) ){
    
    
//    	USART1Printf("STORAGE_Read_FS addr: %d, len: %d\r\n", blk_addr, blk_len);
		HAL_SD_ReadBlocks(&hsd, buf, blk_addr,  blk_len, 5000);  // HAL_MAX_DELAY
		int n = 5000;
		while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){
    
     if(n-- == 0) break; } ;

//    }
    return ret;
  /* USER CODE END 6 */
}

/**
  * @brief  Writes data into the medium.
  * @param  lun: Logical unit number.
  * @param  buf: data buffer.
  * @param  blk_addr: Logical block address.
  * @param  blk_len: Blocks number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
    
    
  /* USER CODE BEGIN 7 */

    int8_t ret = USBD_OK;
//    if( HAL_SD_STATE_READY == HAL_SD_GetState(&hsd) )
    {
    
    
    	int8_t state = HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, 1000);
    	//while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    	int n = 5000;
    	while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){
    
     if(n-- == 0) break; } ;
        USART1Printf("STORAGE_Write_FS addr: %d, len: %d,  state: %d\r\n", blk_addr, blk_len, state);
    }
    return ret;
  /* USER CODE END 7 */
}

/**
  * @brief  Returns the Max Supported LUNs.
  * @param  None
  * @retval Lun(s) number.
  */
int8_t STORAGE_GetMaxLun_FS(void)
{
    
    
  /* USER CODE BEGIN 8 */
  return (STORAGE_LUN_NBR - 1);
  /* USER CODE END 8 */
}

/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */

/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */

/**
  * @}
  */

/**
  * @}
  */

DMA

DMA版本暂时不考虑,具体细节参考时钟问题(重点,核心)中的参考文章。

猜你喜欢

转载自blog.csdn.net/bbdxf/article/details/125817408