文末附主要代码,完整测试程序到参考资料[7]下载。
回调函数初始化 |
IO和时钟初始化 |
使用400KHz单线进行初始化(用户无需自行设置) |
SDMMC单元上电并适当延时 |
通过命令通信,确定SD卡合适的初始化方式(需要通过命令知道使用的SD卡是什么类型,需要使用怎样的)。并设置块大小。 |
通过用户设置的初始化结构体SD_InitTypeDef对SD卡正常读写时的速率、时钟、线宽等进行初始化,若用户设置的参数与SD的类型不匹配,则程序自动进行更正。。 |
读状态确定初始化成功 |
1、由于SD卡的读写速率较高,当电路板老化或SD卡线太长时,都会大大增加SD的读写错误的发生。
原子哥虽然提供了不错的SD卡代码,但是为了降低错误,把读写速率讲得很低,这样并不是很合理,速率低了依然可能发生错误。因此处理方法是在读写错误发生后,重新读写该区域的数据,知道正确读取数据后才跳出循环。
2、另外,STM32H7 HAL库中的DMA方式写SD卡的程序中出现了笔误,导致不能正常些SD卡,故做了如下修正。当然,最好的解决办法是使用最新的1.90版本的库(目前,OpenedvSD卡HAL库版本还是1.30版本)。
[1] physical layer simplified specification Vision 7.10
[2]STM32之SD卡
[3]向SD卡某块写数据时,是否要先擦除该块,还是直接写啊?
[4] RM0433 STM32H742, STM32H743/753 and STM32H750 Value line advanced Arm®-based 32-bit MCUs
[5] AN5200 Getting started with STM32H7 Series SDMMC host controller
[6]STM32-HAL库(固件库)升级
[7] NoDistanceY-SD卡测试.zip
/* Includes ------------------------------------------------------------------*/
#include "sdmmc_sdcard.h"
#include "string.h"
#include "delay.h"
//#include "debug.h"
#include
#define Debug_Printf(format, ...) printf(format, ##__VA_ARGS__)
#define Debug_IT_Printf(format, ...) printf(format, ##__VA_ARGS__)
#define Debug_Fflush()
/* imported part--------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
SD_HandleTypeDef SDCARD_Handler; //SD卡句柄
HAL_SD_CardInfoTypeDef SDCardInfo; //SD卡信息结构体
//SD_ReadDisk/SD_WriteDisk函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候,
//需要用到该数组,确保数据缓存区地址是4字节对齐的.
__align(4) uint8_t SDIO_DATA_BUFFER[512];
static bool isInReadProcess = false;
static int SDerrCnt = 0;
/* SD initialization --------------------------------------------------------*/
//SD卡初始化,SDMMC频率为200MHz,SD卡最大频率25MHz
//返回值:0 初始化正确;其他值,初始化错误
uint8_t SD_Init(void)
{
uint8_t 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;//SDMMC_INIT_CLK_DIV;
SD_Error=HAL_SD_Init(&SDCARD_Handler);
if(SD_Error!=HAL_OK) return 1;
//获取SD卡信息
HAL_SD_GetCardInfo(&SDCARD_Handler,&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); //初始化
}
/* Interrupt service function and Handlers ----------------------------------*/
#if SD_DMA_MODE == 1 || SD_IT_MODE == 1
volatile uint8_t SDCardWriteStatus=0,SDCardReadStatus=0,SDCardErrorStatus=0;
void SdmmcSetPriority(uint8_t groupid, uint8_t subid)
{
HAL_NVIC_SetPriority(SDMMC1_IRQn,groupid,subid); //配置SDMMC1中断,抢占优先级2,子优先级0
HAL_NVIC_EnableIRQ(SDMMC1_IRQn); //使能SDMMC1中断
}
//SDMMC1中断服务函数 ,放置在 stm327xx_it.c
void SDMMC1_IRQHandler(void)
{
HAL_SD_IRQHandler(&SDCARD_Handler);
}
//SDMMC1写完成回调函数
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
SDCardWriteStatus=1; //标记写完成
}
//SDMMC1读完成回调函数
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
SDCardReadStatus=1; //标记读完成
}
//SDMMC1 错误回调函数
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd)
{
//ErrorCode 解析于:hal_sd.h line271,II_sdmmc.h line138
Debug_IT_Printf("#errorCode:%#x, isInRead?=%d\r\n",hsd->ErrorCode,(int)isInReadProcess);//test
SDCardErrorStatus = hsd->ErrorCode;
SDerrCnt++;
if(isInReadProcess)
SDCardReadStatus = 1;
else
SDCardWriteStatus = 1;
}
#endif
/* funtions -----------------------------------------------------------------*/
//得到卡信息
//cardinfo:卡信息存储区
//返回值:错误状态
uint8_t SD_GetCardInfo(HAL_SD_CardInfoTypeDef *cardinfo)
{
uint8_t sta;
sta=HAL_SD_GetCardInfo(&SDCARD_Handler,cardinfo);
return sta;
}
//判断SD卡是否可以传输(读写)数据
//返回值:SD_TRANSFER_OK 传输完成,可以继续下一次传输
// SD_TRANSFER_BUSY SD卡正忙,不可以进行下一次传输
uint8_t SD_GetCardState(void)
{
return((HAL_SD_GetCardState(&SDCARD_Handler)==HAL_SD_CARD_TRANSFER )?SD_TRANSFER_OK:SD_TRANSFER_BUSY);
}
#if SD_DMA_MODE == 1
//如果使用DMA的话下面两个变量用来标记SD卡读写是否完成
//读SD卡
//buf:读数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_ReadDisk(uint8_t* buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_ERROR;
SDCardReadStatus=0;
isInReadProcess = true;
while(1)
{
Debug_Fflush();
while(HAL_SD_ReadBlocks_DMA(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt) != HAL_OK)
{
SDerrCnt++;
if(SDerrCnt>10)
break;
Debug_Printf("##errorCode:%#x, isInRead?=%d\r\n",SDCARD_Handler.Instance->STA,(int)isInReadProcess);
Debug_Fflush();
delay_ms(10);//猜测的时间
}
if(SDerrCnt > 10)
{
Debug_Printf("###errorCode:%#x, isInRead?=%d\r\n",SDCardErrorStatus,(int)isInReadProcess);
break;
}
while(SDCardReadStatus==0){}; //等待读完成
SDCardReadStatus=0;
if(SDCardErrorStatus)
{
Debug_Fflush();
SDCardErrorStatus = 0;
continue;
}
while(SD_GetCardState()){}; //等待SD卡空闲
sta=HAL_OK;
break;
}
SDerrCnt = 0;
isInReadProcess = false;
return sta;
}
//写SD卡
//buf:写数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_WriteDisk(uint8_t *buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_ERROR;
SDCardWriteStatus=0;
while(1)
{
while(HAL_SD_WriteBlocks_DMA(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt) != HAL_OK)
{
SDerrCnt++;
if(SDerrCnt>10)
break;
Debug_Printf("##errorCode:%#x, isInRead?=%d\r\n",SDCARD_Handler.Instance->STA,(int)isInReadProcess);
delay_ms(10);//猜测的时间
}
if(SDerrCnt > 10)
{
Debug_Printf("###errorCode:%#x, isInRead?=%d\r\n",SDCardErrorStatus,isInReadProcess);
break;
}
while(SDCardWriteStatus==0){}; //等待读完成
SDCardWriteStatus=0;
if(SDCardErrorStatus)
{
Debug_Fflush();
SDCardErrorStatus = 0;
continue;
}
while(SD_GetCardState()){}; //等待SD卡空闲
sta=HAL_OK;
break;
}
SDerrCnt = 0;
return sta;
}
#endif
#if (SD_IT_MODE==1) //IT模式
//读SD卡
//buf:读数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_ReadDisk(uint8_t* buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_ERROR;
SDCardReadStatus=0;
if(HAL_SD_ReadBlocks_IT(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt)==HAL_OK)
{
while(SDCardReadStatus==0)
{
if(SDCardErrorStatus != 0)
{
SDCardErrorStatus = 0;
sta = HAL_SD_WriteBlocks_IT(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt);
}
}; //等待写完成
//SDCardReadStatus=0;//不能用 2019/5/24
while(SD_GetCardState()){}; //等待SD卡空闲
sta=HAL_OK;
}
return sta;
}
//写SD卡
//buf:写数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_WriteDisk(uint8_t *buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_ERROR;
SDCardWriteStatus=0;
if(HAL_SD_WriteBlocks_IT(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt)==HAL_OK)
{
while(SDCardWriteStatus==0)
{
if(SDCardErrorStatus != 0)
{
SDCardErrorStatus = 0;
HAL_SD_WriteBlocks_IT(&SDCARD_Handler,(uint8_t*)buf,(uint32_t)sector,(uint32_t)cnt);
}
}; //等待写完成
//SDCardWriteStatus=0; //不能用 2019/5/24
while(SD_GetCardState()){}; //等待SD卡空闲
sta=HAL_OK;
}
return sta;
}
#endif
#if SD_POLLING_MODE == 1 //轮询模式
//如果使用DMA的话下面两个变量用来标记SD卡读写是否完成
volatile uint8_t SDCardWriteStatus=0,SDCardReadStatus=0,SDCardErrorStatus=0;
//读SD卡
//buf:读数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_ReadDisk(uint8_t* buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_OK;
uint32_t timeout=SD_TIMEOUT;
long long lsector=sector;
//INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
sta=HAL_SD_ReadBlocks(&SDCARD_Handler, (uint8_t*)buf,lsector,cnt,SD_TIMEOUT);//多个sector的读操作
//等待SD卡读完
while(SD_GetCardState()!=SD_TRANSFER_OK)
{
if(timeout-- == 0)
{
sta=SD_TRANSFER_BUSY;
}
}
//INTX_ENABLE();//开启总中断
return sta;
}
//写SD卡
//buf:写数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
uint8_t SD_WriteDisk(uint8_t *buf,uint32_t sector,uint32_t cnt)
{
uint8_t sta=HAL_OK;
uint32_t timeout=SD_TIMEOUT;
long long lsector=sector;
//INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
sta=HAL_SD_WriteBlocks(&SDCARD_Handler,(uint8_t*)buf,lsector,cnt,SD_TIMEOUT);//多个sector的写操作
//等待SD卡写完
while(SD_GetCardState()!=SD_TRANSFER_OK)
{
if(timeout-- == 0)
{
sta=SD_TRANSFER_BUSY;
}
}
//INTX_ENABLE();//开启总中断
return sta;
}
#endif
/* Private variables ---------------------------------------------------------*/
static void show_sdcard_info(void)
{
uint64_t CardCap; //SD卡容量
HAL_SD_CardCIDTypedef SDCard_CID;
HAL_SD_GetCardCID(&SDCARD_Handler,&SDCard_CID); //获取CID
SD_GetCardInfo(&SDCardInfo); //获取SD卡信息
switch(SDCardInfo.CardType)
{
case CARD_SDSC:
{
if(SDCardInfo.CardVersion == CARD_V1_X)
Debug_Printf("Card Type:SDSC V1\r\n");
else if(SDCardInfo.CardVersion == CARD_V2_X)
Debug_Printf("Card Type:SDSC V2\r\n");
}
break;
case CARD_SDHC_SDXC:Debug_Printf("Card Type:SDHC\r\n");break;
}
CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize); //计算SD卡容量
Debug_Printf("Card ManufacturerID:%d\r\n",SDCard_CID.ManufacturerID); //制造商ID
Debug_Printf("Card RCA:%d\r\n",SDCardInfo.RelCardAdd); //卡相对地址
Debug_Printf("LogBlockNbr:%d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr)); //显示逻辑块数量
Debug_Printf("LogBlockSize:%d \r\n",(uint32_t)(SDCardInfo.LogBlockSize)); //显示逻辑块大小
Debug_Printf("Card Capacity:%d MB\r\n",(uint32_t)(CardCap>>20)); //显示容量
Debug_Printf("Card BlockSize:%d\r\n\r\n",SDCardInfo.BlockSize); //显示块大小
}
void TDD_SD_Test(void)
{
while(SD_Init())//检测不到SD卡
{
Debug_Printf("##SD Card Error!,is SD inserted?\r\n");
delay_ms(2000);
}
SDCARD_Handler.Instance->ICR = 0;
show_sdcard_info();
Debug_Printf("@SD Card OK!\r\n");
Debug_Fflush();
__align(4) uint8_t buf[512] ="hello SD card yyy\r\n\0";
SD_WriteDisk(buf,2,1);
while(1)
{
delay_ms(1000);
memset(buf,0,1024);
SD_ReadDisk(buf,2,1);
//Debug_TxFixedLen((char*)buf,sizeof("hello SD card xxx\r\n")-1);
Debug_Printf("%s",buf);
Debug_Fflush();
};
}
/************************ (C) COPYRIGHT NODISTANCEY****************END OF FILE****/