目录

STM32F4单片机SDIO驱动SD卡

1、SD卡相关的知识点介绍( 关于知识点这一块请看上传的文档,接下来只描述重要的部分)

STM32F4有一个SDIO接口,支持符合SD协议的各种设备,与 SD 存储卡规格版本 2.0 全兼容。此SDIO接口由SDIO 适配器和 AHB 接口两部分组成:SDIO 适配器提供 SDIO 主机功能,可以提供 SD 时钟、发送命令和进行数据传输;AHB 接口用于控制器访问 SDIO 适配器寄存器并且可以产生中断和 DMA 请求信号。功能框图如下:

SDIOCLK是SDIO适配器时钟,控制单元、命令通道和数据通道都使用这个时钟,SDIOCLK的时钟来源是PLL48CK,即SDIOCLK=PLL48CK=48M。

HCLK/2时钟主要是适配器寄存器和 FIFO 使用,一般情况下HCLK/2=90M(HCK就是单片机的系统主频)。

SDIO_CK 是 SDIO 接口与 SD 卡用于同步的时钟信号,它的时钟来源是SDIOCLK。 如果设置BYPASS模式,SDIO_CK = SDIOCLK48M。若禁止BYPASS 模式,可以通过配置时钟寄存器的 CLKDIV 位控制分频系数,即 SDIO_CK=SDIOCLK/(2+CLKDIV)= HCLK/(2+CLKDIV)。

注意:1、SD 卡普遍要求 SDIO_CK 时钟频率不能超过 25MHz;2、驱动SD卡时主从机只以 CLK 时钟线的上升沿为有效。

SDIO_D[7:0]是SDIO接口的7根数据线,在驱动SD卡时最多使用4根线。STM32F4的SDIO接口支持三种不同的数据总线模式:1 位(默认)、4 位 和 8 位。SD主机复位后默认使用SDIO_D0 用于数据传输。初始化后主机可以改变数据总线的宽度(通过 ACMD6 命令设置)。

2、CubeMX生成代码

时钟源选择外部高速时钟,使能串口1,波特率115200,SDIO配置如下图:

3、代码修改

cpp 复制代码
/* main.h添加头文件*/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"


/*usart.c添加串口重定向*/
int fputc(int ch, FILE *f)
{
	//具体哪个串口可以更改huart1为其它串口
	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);
	return ch;
}

/*sdio.c开头添加SD卡信息结构体定义*/
HAL_SD_CardInfoTypeDef sd_card_info_handle; /* SD卡信息结构体 */

/*sdio.c中MX_SDIO_SD_Init函数最后添加SD卡信息读取函数*/
HAL_SD_GetCardInfo(&hsd, &sd_card_info_handle);                  /* 获取SD卡信息 */


/*sdio.c中最后添加辅助函数*/
void Get_SD_Card_info(void)
{
	if( HAL_SD_GetCardInfo(&hsd, &sd_card_info_handle) != HAL_OK)                  /* 获取SD卡信息 */
	{
		printf("Get_SD_Card_info failed\n");
	}
}

uint8_t get_sd_card_state(void)
{
  return ((HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER) ? SD_TRANSFER_OK: SD_TRANSFER_BUSY);
}

/**
 * @brief       读SD卡(fatfs/usb调用)
 * @param       pbuf  : 数据缓存区
 * @param       saddr : 扇区地址
 * @param       cnt   : 扇区个数
 * @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);
 */
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{
	uint8_t sta = HAL_OK;
	uint32_t timeout = SD_TIMEOUT;
	long long lsector = saddr;

	__disable_irq();/* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */

	sta = HAL_SD_ReadBlocks(&hsd, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT);  /* 多个sector的读操作 */

	/* 等待SD卡读完 */
	while (get_sd_card_state() != SD_TRANSFER_OK)
	{
			if (timeout-- == 0)
			{
					sta = SD_TRANSFER_BUSY;
			}
	}
	
	__enable_irq();/* 开启总中断 */
	
	return sta;
}

/**
 * @brief       写SD卡(fatfs/usb调用)
 * @param       pbuf  : 数据缓存区
 * @param       saddr : 扇区地址
 * @param       cnt   : 扇区个数
 * @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);
 */
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{
	uint8_t sta = HAL_OK;
	uint32_t timeout = SD_TIMEOUT;
	long long lsector = saddr;

	__disable_irq();			/* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */
	sta = HAL_SD_WriteBlocks(&hsd, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT);  /* 多个sector的写操作 */

	/* 等待SD卡写完 */
	while (get_sd_card_state() != SD_TRANSFER_OK)
	{
			if (timeout-- == 0)
			{
					sta = SD_TRANSFER_BUSY;
			}
	}
	__enable_irq();
	
	return sta;
}


void show_sdcard_info(void)
{
	HAL_SD_CardCIDTypeDef sd_card_cid;

	HAL_SD_GetCardCID(&hsd, &sd_card_cid);        /* 获取CID */
	Get_SD_Card_info();                 /* 获取SD卡信息 */

	switch (sd_card_info_handle.CardType)
	{
			case CARD_SDSC:
			{
					if (sd_card_info_handle.CardVersion == CARD_V1_X)
					{
							printf("Card Type:SDSC V1\r\n");
					}
					else if (sd_card_info_handle.CardVersion == CARD_V2_X)
					{
							printf("Card Type:SDSC V2\r\n");
					}
			}
			break;

			case CARD_SDHC_SDXC:
					printf("Card Type:SDHC\r\n");
					break;
			default: break;
	}

	printf("Card ManufacturerID:%d\r\n", sd_card_cid.ManufacturerID);                   /* 制造商ID */
	printf("Card RCA:%d\r\n", sd_card_info_handle.RelCardAdd);                        /* 卡相对地址 */
	printf("Card Capacity:%d MB\r\n", (uint32_t)SD_TOTAL_SIZE_MB(&hsd));    /* 显示容量 */
	printf("Card BlockSize:%d\r\n\r\n", sd_card_info_handle.BlockSize);               /* 显示块大小 */
}


/*sdio.h添加宏定义和函数声明*/
#define SD_TIMEOUT             ((uint32_t)100000000)                                  /* 超时时间 */
#define SD_TRANSFER_OK         ((uint8_t)0x00)
#define SD_TRANSFER_BUSY       ((uint8_t)0x01)


/* 根据 SD_HandleTypeDef 定义的宏,用于快速计算容量 */
#define SD_TOTAL_SIZE_BYTE(__Handle__)  (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>0)
#define SD_TOTAL_SIZE_KB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>10)
#define SD_TOTAL_SIZE_MB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>20)
#define SD_TOTAL_SIZE_GB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>30)

extern HAL_SD_CardInfoTypeDef sd_card_info_handle; /* SD卡信息结构体 */

void Get_SD_Card_info(void);
uint8_t get_sd_card_state(void);
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);
void show_sdcard_info(void);

/*main.c开头添加测试函数*/
/**
 * @brief       测试SD卡的写入
 * @note        从secaddr地址开始,写入seccnt个扇区的数据
 *              慎用!! 最好写全是0XFF的扇区,否则可能损坏SD卡.
 * @param       secaddr : 扇区地址
 * @retval      无
 */
void sd_test_write(uint32_t secaddr)
{
	uint32_t i;
	uint8_t buf[512] = { NULL };
	uint8_t sta = 0;

	for (i = 0; i < 512; i++)          /* 初始化写入的数据,是3的倍数. */
	{
			buf[i] = i * 3;
	}

	sta = sd_write_disk(buf, secaddr, 1);  /* 从secaddr扇区开始写入seccnt个扇区内容 */

	if (sta == 0)
	{
			printf("Write over!\r\n");
	}
	else
	{
			printf("err:%d\r\n", sta);
	}
	memset(buf, 0, 512);//清空数组
}

/**
 * @brief       测试SD卡的写入
 * @note        从secaddr地址开始,写入seccnt个扇区的数据
 *              慎用!! 最好写全是0XFF的扇区,否则可能损坏SD卡.
 * @param       secaddr : 扇区地址
 * @retval      无
 */
void sd_test_writ1(uint32_t secaddr)
{
	uint32_t i;
	uint8_t buf[512] = { NULL };
	uint8_t sta = 0;

	for (i = 0; i <  512; i++)          /* 初始化写入的数据,是3的倍数. */
	{
			buf[i] = i ;
	}

	sta = sd_write_disk(buf, secaddr, 1);  /* 从secaddr扇区开始写入seccnt个扇区内容 */

	if (sta == 0)
	{
			printf("Write over!\r\n");
	}
	else
	{
			printf("err:%d\r\n", sta);
	}
	memset(buf, 0, 512);//清空数组
}

/**
 * @brief       测试SD卡的读取
 * @note        从secaddr地址开始,读取seccnt个扇区的数据
 * @param       secaddr : 扇区地址
 * @retval      无
 */
void sd_test_read(uint32_t secaddr)
{
	uint32_t i;
	uint8_t buf[512] = { NULL };
	uint8_t sta = 0;

	sta = sd_read_disk(buf, secaddr, 1);   /* 读取secaddr扇区开始的内容 */

	if (sta == 0)
	{
			printf("SECTOR %d DATA:\r\n", secaddr);

			for (i = 0; i < 512; i++)
			{
					printf("%x ", buf[i]);              /* 打印secaddr开始的扇区数据 */
			}

			printf("\r\nDATA ENDED\r\n");
	}
	else
	{
			printf("err:%d\r\n", sta);
	}
	memset(buf, 0, 512);//清空数组
}

/*main函数while1前添加以下内容*/
show_sdcard_info();//打印SD卡信息
HAL_Delay(100);
sd_test_write(0);//SD卡从0开始的block写入数据,数据都是3的倍数
HAL_Delay(100);
sd_test_read(0);//SD卡从0开始的block读出数据
HAL_Delay(100);
HAL_SD_Erase(&hsd,0,1);//从地址为0的block擦除1个block
HAL_Delay(100);
sd_test_writ1(0);//SD卡从0开始的block写入数据,数据都是1的倍数
HAL_Delay(100);
sd_test_read(0);	//SD卡从0开始的block读出数据

最后编译烧录打印的信息如下(最前面SD卡的信息插得不同的卡打印的不同信息,后面测试数据应该是一样的):

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
LS_learner5 分钟前
小智机器人关键函数解析,Application::OutputAudio()处理音频数据的输出的函数
人工智能·嵌入式硬件
西城微科方案开发37 分钟前
体重秤PCBA电路方案组成结构
单片机·嵌入式硬件
深圳市青牛科技实业有限公司43 分钟前
「青牛科技 」GC4931P/4938/4939 12-24V三相有感电机驱动芯片 对标Allegro A4931/瑞盟MS4931
科技·单片机·扫地机器人吸尘·筋膜枪电机·驱动轮电机·服务机器人驱动轮电机·工业机器人减速电机
集和诚JHCTECH1 小时前
集和诚携手Intel重磅发布BRAV-7820边缘计算新品,为车路云一体化场景提供强大算力支撑
人工智能·嵌入式硬件·边缘计算
阿超爱嵌入式1 小时前
STM32学习笔记之RCC模块(实操篇)
笔记·stm32·学习
Qingniu011 小时前
「青牛科技」GC5849 12V三相无感正弦波电机驱动芯片
科技·单片机·嵌入式硬件·cpu散热风扇·vga显卡散热风扇·rgb机箱散热风扇·水冷泵及散热风扇
charlie1145141912 小时前
STM32F103C8T6单片机硬核原理篇:讨论GPIO的基本原理篇章1——只讨论我们的GPIO简单输入和输出
c语言·stm32·单片机·嵌入式硬件·gpio·数据手册
阿让啊2 小时前
bootloader+APP中,有些APP引脚无法正常使用?
c语言·开发语言·stm32·单片机·嵌入式硬件
可待电子单片机设计定制(论文)3 小时前
【51单片机】基于单片机的智能门禁系统设计
单片机·mongodb·51单片机
蝎蟹居3 小时前
GB/T 4706.1-2024 家用和类似用途电器的安全 第1部分:通用要求 与2005版差异(1)
人工智能·单片机·嵌入式硬件·物联网·安全