STM32CubeMX SDIO SD卡 FATFS

(390条消息) STM32CubeMX系列09——SDIO(SD卡读写、SD卡移植FATFS文件系统)_stm32cubemx sdio_32Haozi的博客-CSDN博客

https://blog.csdn.net/weixin_46253745/article/details/127865071?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7-127865071-blog-77679103.235%5Ev27%5Epc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7-127865071-blog-77679103.235%5Ev27%5Epc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=14

(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://blog.csdn.net/weixin_44453694/article/details/123680102?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167973942016800222824899%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=167973942016800222824899&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-6-123680102-null-null.article_score_rank_blog&utm_term=2023%20%E6%96%B0%E7%89%88HAL%E5%BA%93%20SDIO%20%E9%A9%B1%E5%8A%A8%E5%88%9D%E5%A7%8B%E5%8C%96%E5%A4%B1%E8%B4%A5&spm=1018.2226.3001.4450

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);
    }
    
}

可以选择不填

添加文件系统测试程序

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博客

https://blog.csdn.net/ba_wang_mao/article/details/109487584

猜你喜欢

转载自blog.csdn.net/m0_37777700/article/details/129780016