【前言】
驱动MCU的SD卡,在日常的开发中,最为通用,用于数据的保存,固件升级等等都有很大的作用。这次我花了三天的时间对STM32F769-DISC1进行了驱动的学习,由于我是新手,官方的开发板提供了基于BSP的驱动示例,但是没有STM32CubeMX的工程,所以需要自行移植他的BSP里的驱动,或者使用STM32CubeMX进行配置驱动。STM32CubeMX配置出来,还是需要手工进行调整一些参数才行正常驱动,遇到了很多的坑,特此在这里记录并分享。
【软件环境】
1、操作系统 win11
2、配置工具:STM32CubeMX 6.13.0
3、代码编辑vscode
【配置过程】
1、创建基于STM32F769NIHx的基本工程。
2、打开USART1,选择PA9、PA10为串口用的IO。其他使用默认配置。
3、打开RCC的外部晶振
4、打开SDMMC2,选择SD 4 bits Wide Bus。时钟分频为4分频。

5、确认GPIO与原理图一样。
6、添加DMA设置,添加RX、TX两个传输通道,添加后不用修改这里有参数,保持黙认即可。

7、使能DMA中断,三个中断都需要使能。

8、调整时钟配置,选择HSE为输入源,配置如下图:

这里一定要注间,SDMMC2为48MHz以下的时钟,我就是在这里没有配置好,在坑里爬了2天才找出原因。具体过程可以看我的另一篇帖子:
【STM32F769】调试SD驱动,由于其时钟配置不对引起的错误以及排查记录 https://forum.eepw.com.cn/thread/391294/1
9、打开工程选项,输入项目名称,选择Cmake工程,根据以后自己的应用调节栈大小。

10、生成工程,并使用vscode打开工程。
11、修改sdmmc.c的MX_SDMMC2_SD_Init函数,将最先的初始化需要修数据宽度为1b,要不会选成错误:

12、在usart.c的最后添加printf重定向代码:

14、在stm32f7xx_it.c的最后,添加dma回调函数,并声明全局变量传输标志:
/* USER CODE BEGIN 1 */
extern __IO uint32_t SDWriteStatus;
extern __IO uint32_t SDReadStatus ;
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hsd);
SDWriteStatus = 1;
}
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
UNUSED(hsd);
SDReadStatus = 1;
}
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd)
{
UNUSED(hsd);
}
void HAL_SD_AbortCallback(SD_HandleTypeDef *hsd)
{
UNUSED(hsd);
}
/* USER CODE END 1 */13、添加测试函数,首先打印卡的信息,然后进行读写测试,代码如下:
#include <stdio.h>
#include "sd_test1.h"
#include "main.h"
extern SD_HandleTypeDef hsd2;
#define BLOCK_START_ADDR 0 /* Block start address */
/* BLOCKSIZE = 512 Bytes, defined in stm32f7xx_hal_sd.h */
#define NUM_OF_BLOCKS 50 /* Total number of blocks */
#define BUFFER_WORDS_SIZE ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t aTxBuffer[BUFFER_WORDS_SIZE];
uint32_t aRxBuffer[BUFFER_WORDS_SIZE];
__IO uint32_t SDWriteStatus = 0, SDReadStatus = 0;
__IO uint32_t SdmmcTest = 0;
/* Private function prototypes -----------------------------------------------*/
static void Fill_Buffer(uint32_t *pBuffer, uint32_t uwBufferLenght, uint32_t uwOffset);
static uint8_t Buffercmp(uint32_t *pBuffer1, uint32_t *pBuffer2, uint16_t BufferLength);
/* Private functions ---------------------------------------------------------*/
uint8_t BSP_SD_GetCardState(void)
{
return ((HAL_SD_GetCardState(&hsd2) == HAL_SD_CARD_TRANSFER) ? SD_TRANSFER_OK : SD_TRANSFER_BUSY);
}
void sd_test1(void)
{
uint8_t SD_state = MSD_OK;
HAL_SD_CardInfoTypeDef SDCardInfo;
// 获取SD卡信息
if (HAL_SD_GetCardInfo(&hsd2, &SDCardInfo) != HAL_OK)
{
// 获取信息失败处理
printf("Failed to get SD card information!\n");
while (1)
;
}
// 打印SD卡信息
printf("SD Card Information:\n");
printf("Card Type: %ld\n", SDCardInfo.CardType);
printf("Card Version: %ld\n", SDCardInfo.CardVersion);
printf("Card Capacity: %ld\n", SDCardInfo.BlockSize);
printf("Card Block Size: %ld\n", SDCardInfo.BlockNbr);
printf("Card Block Nbr: %ld\n", SDCardInfo.Class);
printf("Card LogBlockSize: %ld\n", SDCardInfo.LogBlockSize);
SD_state = HAL_SD_Erase(&hsd2, BLOCK_START_ADDR, (BLOCKSIZE * NUM_OF_BLOCKS)) ;
while (BSP_SD_GetCardState() != SD_TRANSFER_OK)
{
}
if (SD_state【实验效果】
下载到开发板后,通过串口打印,日志如下:

【经验总结】
1、配置时钟需要特别注意 PLLQ要配置48MHz以下。
2、修改初始化为1Bit。
3、需要添加dma的中断回调函数来获取传输完成后的标志位。
总之,厉时3天多,终于把坑填上了。

我要赚赏金
