TM32H7-SD卡DMA方式读写的可靠性修正

文末附主要代码,完整测试程序到参考资料[7]下载。

1- SD卡的初始化

回调函数初始化

IO和时钟初始化

使用400KHz单线进行初始化(用户无需自行设置)

SDMMC单元上电并适当延时

通过命令通信,确定SD卡合适的初始化方式(需要通过命令知道使用的SD卡是什么类型,需要使用怎样的)。并设置块大小。

通过用户设置的初始化结构体SD_InitTypeDef对SD卡正常读写时的速率、时钟、线宽等进行初始化,若用户设置的参数与SD的类型不匹配,则程序自动进行更正。。

读状态确定初始化成功

2- 基本的可靠性修正

1、由于SD卡的读写速率较高,当电路板老化或SD卡线太长时,都会大大增加SD的读写错误的发生。

原子哥虽然提供了不错的SD卡代码,但是为了降低错误,把读写速率讲得很低,这样并不是很合理,速率低了依然可能发生错误。因此处理方法是在读写错误发生后,重新读写该区域的数据,知道正确读取数据后才跳出循环。

2、另外,STM32H7 HAL库中的DMA方式写SD卡的程序中出现了笔误,导致不能正常些SD卡,故做了如下修正。当然,最好的解决办法是使用最新的1.90版本的库(目前,OpenedvSD卡HAL库版本还是1.30版本)。

3、即使上面两步已经做到位了,但依然可能收到错误数据,因此使用FATFS时,数据尽量有个校验和,当检验和出错时,通过fseek调整文件指针,实现数据重读。这也就要求校验和设计要严谨,不然会出现数据错了,但校验和凑巧还是正确的问题。

4、数据层面的再次检验,如一个变量A>100,如何A读出小于100的值,那也可以考虑数据回溯或丢弃本次数据了。

 

3- 参考资料

[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

4- 主要代码

/* Includes ------------------------------------------------------------------*/
#include "sdmmc_sdcard.h"
#include "string.h"
#include "delay.h"
//#include "debug.h"
#include <stdbool.h>
#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****/

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值