H734 USB_DEVICE with DMA as the SDCard reader
-
使用開發版:原子阿波羅 + H743 核心板
-
問題參考文章How to correctly setup your application to use USB DMA controller with STM32H7 devices
-主要問題: 如果使用CubeMX搭建RTOS,牽涉到SD卡的部分,經會強制使用DMA,所以必須搭建一個使用DMA的SD程序。其中只有一個坑,見參考文章(在網路上撈了好久終於撈到這篇),其大意是,因為在H743中,CPU與SD溝通使用的是MDMA,而MDMA牽涉到CPU DCache。所以,必須將 CPU DCache Disable。
-
SDMMC的設置:我們選擇SDMMC1,可以看到選項裡沒有DMA Setting ,這部分已經改到MDMA去了!使用原子的板子的話,就不用改GPIO了(可能要改上拉電阻,請自行檢查一下)
-
設定MDMA:除了add channel 選擇 SDMMC1 data end 外,其他都默認選項
-
設置USB OTG FS:這裡使用USB_OTG_FS,選擇Device Only,並且將DMA enable。
-
設置USB DEVICE:這裡選擇Mass Storage Class
-
時鐘設置:因為H743的高性能,這裡將時鐘頻率設置到最大容許值,usb使用 RC48,實測能跑!
-
中斷設置,SD>MDMA>USB(其實有次我忘了打開MDMA的中斷他也能跑?!但是我最後還是開了它)
-
CODE:需要更改三個檔案
1.CubeMX會引入 bsp_driver_sd.c 它會產生重複定義的錯誤,將錯誤的地方註解掉就行。
2.會用到原子的sdmmc_sdcard.c與sdmmc_sdcard.h
在sdmmc_sdcard.h 開啟 DMA#define SD_DMA_MODE 1
這個設定將會在sdmmc_sdcard.c使用DMA的方式讀寫SD卡,例如
u8 SD_ReadDisk(u8* buf,u32 sector,u32 cnt)
{
u8 sta=HAL_ERROR;
SDCardReadStatus=0;
if(HAL_SD_ReadBlocks_DMA(&hsd1,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt)==HAL_OK)
{
while(SDCardReadStatus==0){}; //等待讀完成
SDCardReadStatus=0;
while(SD_GetCardState()){}; //等待SD卡空閒
sta=HAL_OK;
}
return sta;
}
因為 SD 已經被 CubeMX 產生的程式碼做部分的初始化,所以將sdmmc_sdcard.c內u8 SD_Init(void)
重複的部分註釋掉:
u8 SD_Init(void)
{
u8 SD_Error;
//初始化時的時鐘不能大於400KHZ
// SDCARD_Handler.Instance=SDMMC1;
// SDCARD_Handler.Init.ClockEdge=SDMMC_CLOCK_EDGE_RISING; //上升沿
// SDCARD_Handler.Init.ClockPowerSave=SDMMC_CLOCK_POWER_SAVE_DISABLE; //空閒時不關閉時鐘電源
// SDCARD_Handler.Init.BusWide=SDMMC_BUS_WIDE_4B; //4位數據線
// SDCARD_Handler.Init.HardwareFlowControl=SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;//關閉硬件流控
// SDCARD_Handler.Init.ClockDiv=SDMMC_NSpeed_CLK_DIV; //SD傳輸時鐘頻率最大25MHZ
SD_Error=HAL_SD_Init(&hsd1);
if(SD_Error!=HAL_OK) return 1;
//獲取SD卡信息
HAL_SD_GetCardInfo(&hsd1,&SDCardInfo);
return 0;
}
//SDMMC底層驅動,時鐘使能,引腳配置,DMA配置
//此函數會被HAL_SD_Init()調用
//hsd:SD卡句柄
//void HAL_SD_MspInit(SD_HandleTypeDef *hsd)
//{
// GPIO_InitTypeDef GPIO_Initure;
// __HAL_RCC_SDMMC1_CLK_ENABLE(); //使能SDMMC1時鐘
// __HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOC時鐘
// __HAL_RCC_GPIOD_CLK_ENABLE(); //使能GPIOD時鐘
//
// //PC8,9,10,11,12
// GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
// GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽復用
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
// GPIO_Initure.Alternate=GPIO_AF12_SDIO1; //復用為SDIO
// HAL_GPIO_Init(GPIOC,&GPIO_Initure); //初始化
//
// //PD2
// GPIO_Initure.Pin=GPIO_PIN_2;
// HAL_GPIO_Init(GPIOD,&GPIO_Initure); //初始化
//#if (SD_DMA_MODE==1) //使用DMA模式
// HAL_NVIC_SetPriority(SDMMC1_IRQn,2,0); //配置SDMMC1中斷,搶佔優先級2,子優先級0
// HAL_NVIC_EnableIRQ(SDMMC1_IRQn); //使能SDMMC1中斷
//#endif
//}
還有要將所有的 SD_HandleTypeDef SDCARD_Handler;
改成 CubeMX 的定義 extern SD_HandleTypeDef hsd1;
3.改寫 usbd_storage_if.c,使用原子提供的讀寫函數做讀寫。(其實整個結構很像STM32給的example,參考他們的寫法自己寫一個也行),懶得剪下貼上,這裡給出所有的code
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : usbd_storage_if.c
* @version : v1.0_Cube
* @brief : Memory management layer.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/* USER CODE BEGIN INCLUDE */
#include "sys.h"
#include "sdmmc_sdcard.h"
#include "sdmmc.h"
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
vu8 USB_STATUS_REG=0;
/* 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', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0' ,'1' /* 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 */
/* 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 over USB FS IP
* @param lun:
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Init_FS(uint8_t lun)
{
/* USER CODE BEGIN 2 */
u8 res=0;
MX_SDMMC1_SD_Init();
res=SD_Init();
return res;
//return (USBD_OK);
/* USER CODE END 2 */
}
/**
* @brief .
* @param lun: .
* @param block_num: .
* @param 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;
HAL_SD_GetCardInfo(&hsd1,&info);
*block_num = info.LogBlockNbr - 1;
*block_size = info.LogBlockSize;
printf("blocj_num = %d, block size=%d \r\n",*block_num ,*block_size );
return (USBD_OK);
/* USER CODE END 3 */
}
/**
* @brief .
* @param lun: .
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
/* USER CODE BEGIN 4 */
USB_STATUS_REG|=0X10;//標还輪詿
return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief .
* @param lun: .
* @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 .
* @param lun: .
* @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 res=0;
printf("reading SD ..... ");
USB_STATUS_REG|=0X02;//標还正?ܨ访?ո???
res=SD_ReadDisk(buf,blk_addr,blk_len);
if (res==HAL_OK) printf("read SD complete!\r\n");
else printf("read SD err, err code = %d \r\n", res);
return (res);
/* USER CODE END 6 */
}
/**
* @brief .
* @param lun: .
* @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 res=0;
printf("writting SD ..... ");
USB_STATUS_REG|=0X01;//標还正?ܨ寫數???
res=SD_WriteDisk(buf,blk_addr,blk_len);
if (res==HAL_OK) printf("read SD complete!\r\n");
else printf("write SD err, err code = %d \r\n", res);
if(res)
{
USB_STATUS_REG|=0X04;//寫錯調!
}
return (USBD_OK);
/* USER CODE END 7 */
}
/**
* @brief .
* @param None
* @retval .
*/
int8_t STORAGE_GetMaxLun_FS(void)
{
/* USER CODE BEGIN 8 */
HAL_SD_CardInfoTypeDef info;
HAL_SD_GetCardInfo(&hsd1,&info);
if(info.LogBlockNbr)return STORAGE_LUN_NBR-1;
else return STORAGE_LUN_NBR-2;
//return (STORAGE_LUN_NBR - 1);
/* USER CODE END 8 */
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
最後,我的測試方法為從PC複製一個TXT文件檔到H743上的SD卡,關閉PC上的檔案總管,在PC再次打開H743上的SD卡,讀取檔案,修改檔案,處存檔案,關閉檔案總管,再次在PC上打開H743上的SD卡,讀取被修改過的文件,正確無誤。
以上應該很詳細描述過程,相關的code已經很詳細。所以不再上傳資源了。有需要再跟我說!
因為我在台灣,很少上來,很少積分,更沒金錢,如果此文對您有幫助,所以期待大家能互助,也幫助我解決問題。謝謝大家!