(390条消息) STM32CubeMX系列09——SDIO(SD卡读写、SD卡移植FATFS文件系统)_stm32cubemx sdio_32Haozi的博客-CSDN博客
(389条消息) STM32CubeMX的SDIO模式下对SD卡读写测试(附源码)_sdio 速率测试代码_路伢辉的博客-CSDN博客
https://blog.csdn.net/zxh912516636/article/details/77679103
第25章 串行FLASH文件系统FatFs—零死角玩转STM32-F429系列 - 野火_firege - 博客园
https://www.cnblogs.com/firege/p/5805801.html
(390条消息) STM32CubeMx之SD卡驱动_stm32sd卡驱动_IT_阿水的博客-CSDN博客
![](https://i-blog.csdnimg.cn/blog_migrate/9874cd9defeeffba1cda7844558a2939.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6cd2d5ba5da6d73b9ad62fecc9f8f8af.png)
![](https://i-blog.csdnimg.cn/blog_migrate/345ec9e4745c1607555ca90c24e12702.png)
SDIO分为两个部分:AHB interface和SDIO adapter。
AHB interface采用的时钟是HCLK/2=36MHz, 是用来访问STM32 SDIO本身的寄存器的。
SDIO adapter采用的时钟是SDIOCLK=HCLK=72MHz,SDIO_CK时钟线(PC12脚,单片机给SD卡提供的时钟)输出的时钟就是从这个上面分频得到的,分频公式为SDIOCLK/(CLKDIV+2)。CLKDIV就是hsd.Init.ClockDiv的值。(就是我们上面设定的值)
当CLKDIV=70时,SDIO_CK输出的频率为72MHz/(70+2)=1MHz。在这个频率下可以不使用DMA收发数据。
当CLKDIV=1时,SDIO_CK输出的频率为72MHz/(1+2)=24MHz。在这个频率下必须使用DMA收发数据。
前面第一章说了 SDIO的时钟频率为0~25Mhz,此处设置分频系数为34,而我们开发板的时钟频率为72Mhz,这里分频系数为34,也就是72/(34+2) = 2Mhz。(其他数也行,反正分频完不要超过25,否则读写都会失败)
不过,实际上经过测试,设置为4,也就是分频完为 72 / 6 = 12,也仍然会写失败,又是踩坑的一天
问题:上面的函数中有一个 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;,但是我们配置的不是四线的吗。
答案:(其实也不太明白为什么,但是先这样吧):
SD卡可以采用1位数据线模式,也可以采用4位数据线模式。但是必须确保STM32单片机的SDIO设置的数据线位宽,和SD卡上设置的数据线位宽是一致的。
如果将 hsd.Init.BusWide 设为 SDIO_BUS_WIDE_4B,然后执行HAL_SD_Init 函数,只能把STM32单片机的SDIO设置为4位位宽,SD卡上还是用的1位位宽。
所以通常的做法是 hsd.Init.BusWide 设为 SDIO_BUS_WIDE_1B,HAL_SD_Init 执行完成后,再调用 HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B),这个函数可以将STM32和SD卡同时设为4位模式。
注意修改:将4B改为1B;同时将时钟分频加大(不然出现错误)
hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
hsd.Init.ClockDiv = 4;//SDIO块测试可以达到最大分频为0
hsd.Init.ClockDiv = 24;//FATFS文件系统测试要降低,否则打不开文件
添加初始化:
if (HAL_SD_Init(&hsd) != HAL_OK)
{
Error_Handler();
}
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
void MX_SDIO_SD_Init(void)
{
/* USER CODE BEGIN SDIO_Init 0 */
char SD_Error = 0;
/* USER CODE END SDIO_Init 0 */
/* USER CODE BEGIN SDIO_Init 1 */
/* USER CODE END SDIO_Init 1 */
hsd.Instance = SDIO;
hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
hsd.Init.ClockDiv = 24;
/* USER CODE BEGIN SDIO_Init 2 */
if (HAL_SD_Init(&hsd) != HAL_OK)
{
Error_Handler();
}
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END SDIO_Init 2 */
}
下面是DMA操作:
/**
* @brief BSP Tx Transfer completed callback
* @retval None
* @note empty (up to the user to fill it in or to remove it if useless)
*/
__weak void BSP_SD_WriteCpltCallback(void)
{
__HAL_DMA_DISABLE(hsd.hdmatx);
sd_tx_complete = 1;
}
/**
* @brief BSP Rx Transfer completed callback
* @retval None
* @note empty (up to the user to fill it in or to remove it if useless)
*/
__weak void BSP_SD_ReadCpltCallback(void)
{
__HAL_DMA_DISABLE(hsd.hdmarx);
sd_rx_complete = 1;
}
添加块测试程序
void HAL_SDIO_Block_Test(void)
{
HAL_StatusTypeDef status; // 状态
uint8_t read_buf[512]; // 读数据缓存
uint8_t write_buf[512]; // 写数据缓存
printf("\r\n --------------------- 修改频率 -------------------- \r\n");
/*
设置SDIO时钟频率 = 72 /(ClockDiv + 2) = 18 Mhz
1. 修改结构体 hsd 参数
2. 重新初始化
3. 判断初始化是否成功
*/
hsd.Init.ClockDiv = 2;
status = HAL_SD_Init(&hsd);
if (status == HAL_OK)
printf("频率更改为 %d Hz.\r\n", SystemCoreClock / (2 + hsd.Init.ClockDiv));
else
{
printf("更改频率失败! status = %d \r\n", status);
}
printf("\r\n --------------------- SD擦除 -------------------- \r\n");
// 注意查看参数
status = HAL_SD_Erase(&hsd, 0, 0);
if (status == HAL_OK)
{
printf("擦除1个SD卡块. state = %d \r\n", HAL_SD_GetCardState(&hsd));
while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);
printf("擦除完成! state = %d \r\n", HAL_SD_GetCardState(&hsd));
}
else
{
printf("DMA擦除1个SD卡块失败! status = %d \r\n", status);
}
printf("\r\n --------------------- DMA模式读SD卡 -------------------- \r\n");
// 从SD卡扇区0开始读, 读1个扇区到 read_buf 中
// note:注意DMA模式下每次最多只能读127个扇区,也就是最后一个参数最多只能是127
status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)read_buf, 0, 1);
if (status == HAL_OK)
{
while (!sd_rx_complete); // 等待传输完成(传输完成会有回调函数)
sd_rx_complete = 0; // 清除标志位
printf("使用DMA读取SD卡块1 \r\n");
for(int i = 0; i < 512; i++)
{
printf("0x%02x ", read_buf[i]);
if((i+1)%16 == 0)
{
printf("\r\n");
}
}
}
else
{
printf("DMA读取失败! status = %d \r\n", status);
}
printf("\r\n --------------------- DMA模式写SD卡 -------------------- \r\n");
for(int i = 0; i < 512; i++)
{
write_buf[i] = i;
}
status = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)write_buf, 0, 1);
if (status == HAL_OK)
{
while (!sd_tx_complete);
sd_tx_complete = 0;
printf("DMA写入1个SD卡块. state = %d \r\n", HAL_SD_GetCardState(&hsd));
// 注意DMA传输完成,不代表SD卡就写完成了。
while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);
printf("写入完成! state = %d \r\n", HAL_SD_GetCardState(&hsd));
}
else
{
printf("DMA写入1个SD卡块失败! status=%d \r\n", status);
}
printf("\r\n --------------------- 写完之后再读一次 -------------------- \r\n");
status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)read_buf, 0, 1);
if (status == HAL_OK)
{
while (!sd_rx_complete); // 等待传输完成(传输完成会有回调函数)
sd_rx_complete = 0; // 清除标志位
printf("使用DMA读取SD卡块1 \r\n");
for(int i = 0; i < 512; i++)
{
printf("0x%02x ", read_buf[i]);
if((i+1)%16 == 0)
{
printf("\r\n");
}
}
}
else
{
printf("DMA读取失败! status = %d \r\n", status);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/29ee5f1d824c97ac288fb8f4cd2e601c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e7b9b23d8bb3bc9835b715ec9424a3ed.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cf2396221a1db387fafa5c5164359f55.png)
可以选择不填
![](https://i-blog.csdnimg.cn/blog_migrate/6cdc09e7ecfbb0dfdd81f7f916c1cde5.png)
添加文件系统测试程序
void HAL_SDIO_FATFS_Test(void)
{
FATFS fs; /* FatFs 文件系统对象 */
FIL file; /* 文件对象 */
FRESULT f_res; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
BYTE ReadBuffer[1024] = {0}; /* 读缓冲区 */
BYTE WriteBuffer[] = "This is STM32 working with FatFs \r\n";/* 写缓冲区 */
printf("\r\r\n****** 这是一个SD卡 文件系统实验 ******\r\r\n");
/* 注册一个FatFS设备:SD卡 */
// if(FATFS_LinkDriver(&SD_Driver, SDPath) == 0)
{
printf("f_mount:%s \r\n",SDPath);
//在SD卡挂载文件系统,文件系统挂载时会对SD卡初始化
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
printf("f_mount:%s \r\n",SDPath);
printf_fatfs_error(f_res);
/*----------------------- 格式化测试 ---------------------------*/
/* 如果没有文件系统就格式化创建创建文件系统 */
if(f_res == FR_NO_FILESYSTEM)
{
printf("》SD卡还没有文件系统,即将进行格式化...\r\n");
/* 格式化 */
f_res=f_mkfs((TCHAR const*)SDPath,0,0);
if(f_res == FR_OK)
{
printf("》SD卡已成功格式化文件系统。\r\n");
/* 格式化后,先取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
/* 重新挂载 */
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
}
else
{
printf("《《格式化失败。》》\r\n");
while(1);
}
}
else if(f_res!=FR_OK)
{
printf("!!SD卡挂载文件系统失败。(%d)\r\n",f_res);
printf_fatfs_error(f_res);
while(1);
}
else
{
printf("》文件系统挂载成功,可以进行读写测试\r\n");
}
/*----------------------- 文件系统测试:写测试 -----------------------------*/
/* 打开文件,如果文件不存在则创建它 */
printf("****** 即将进行文件写入测试... ******\r\n");
f_res = f_open(&file, "FatFs.txt",FA_CREATE_ALWAYS | FA_WRITE );
if ( f_res == FR_OK )
{
printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
/* 将指定存储区内容写入到文件内 */
f_res=f_write(&file,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(f_res==FR_OK)
{
printf("》文件写入成功,写入字节数据:%d\r\n",fnum);
printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
}
else
{
printf("!!文件写入失败:(%d)\r\n",f_res);
}
/* 不再读写,关闭文件 */
f_close(&file);
}
else
{
printf("!!打开/创建文件失败。\r\n");
}
/*------------------- 文件系统测试:读测试 ------------------------------------*/
printf("****** 即将进行文件读取测试... ******\r\n");
f_res = f_open(&file, "FatFs.txt", FA_OPEN_EXISTING | FA_READ);
if(f_res == FR_OK)
{
printf("》打开文件成功。\r\n");
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(f_res==FR_OK)
{
printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);
}
else
{
printf("!!文件读取失败:(%d)\r\n",f_res);
}
}
else
{
printf("!!打开文件失败。\r\n");
}
/* 不再读写,关闭文件 */
f_close(&file);
/* 不再使用,取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
}
/* 注销一个FatFS设备:SD卡 */
FATFS_UnLinkDriver(SDPath);
}
(389条消息) STM32F407移植FATFS文件系统(版本 R0.09b)到SD卡(硬件SPI总线)_哪些单片机支持sdio总线_ba_wang_mao的博客-CSDN博客