打印
[STM32H7]

【STM32H7S78-DK测评】6、SD卡列出目录,读写文件

[复制链接]
513|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 sujingliang 于 2024-9-16 07:10 编辑

目的
通过FatFs (Generic FAT Filesystem module)访问SD卡,读写文件,列出文件目录,通过串口输出结果。
FatFs Module是一种完全免费开源的FAT文件系统模块,专为小型的嵌入式系统而设计。
FatFs Module提供了一系列的文件操作接口,如文件的打开、关闭、读写、移动指针、获取状态等。这些接口函数使得在嵌入式系统中操作文件变得像在PC上一样简单。以下是一些常用的接口函数:

  • f_mount:登记或注销一个工作区域。
  • f_open:打开或创建文件。
  • f_close:关闭一个文件。
  • f_read:从文件中读取数据。
  • f_write:向文件写入数据。
  • f_lseek:移动文件读/写指针。
  • f_stat:获取文件状态。
  • f_mkdir:创建一个目录。
  • f_unlink:删除文件或目录。
  • f_rename:重命名或移动文件/目录。


编译环境:keil5.40


一、硬件方面

DAT0:数据传输引脚,主要用于单线双向数据传输。在四线数据传输模式中,它也是四根数据线之一,用于高速数据传输。
DAT1、DAT2、DAT3:附加数据传输引脚,在支持四线数据传输的SD卡中,这些引脚与DAT0共同工作,实现高速数据传输。在某些规范中,这些引脚可能会被省略或用作其他目的。

CMD(命令传输引脚):用于主机和SD卡之间的命令传输。主机通过CMD引脚向SD卡发送命令,SD卡则通过CMD引脚或DAT引脚(取决于命令类型)返回响应。
CLK(时钟引脚):提供时钟信号以同步数据传输。CLK引脚控制数据传输的速率和同步性,确保主机和SD卡之间的数据交换能够准确无误地进行
SD卡的DETECT引脚(通常也标记为CD引脚)主要用于检测SD卡是否已正确插入到设备中。这一引脚在SD卡与设备之间的通信中起着至关重要的作用,它允许设备在SD卡插入或拔出时采取相应的操作,如初始化SD卡、读取数据或关闭数据传输等。


二、功能实现
1、为方便调试,利用STM32H7S78-DK的BSP库函数文件stm32h7s78_discovery支持板载LED、UART4:
  BSP_LED_Init(LED_GREEN);
  BSP_LED_Init(LED_RED);
  BSP_LED_Init(LED_ORANGE);
  BSP_LED_Init(LED_BLUE);

/* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
    BspCOMInit.BaudRate   = 115200;
    BspCOMInit.WordLength = COM_WORDLENGTH_8B;
    BspCOMInit.StopBits   = COM_STOPBITS_1;
    BspCOMInit.Parity     = COM_PARITY_NONE;
    BspCOMInit.HwFlowCtl  = COM_HWCONTROL_NONE;
    if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
    {
      Error_Handler();
    }
2、SD_DETECT引脚初始化
SD_DECTECT需要设置为外部中断引脚
  GPIO_InitStruct.Pin = SD_DETECT_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(SD_DETECT_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(SD_DETECT_EXTI_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(SD_DETECT_EXTI_IRQn);


3、SD卡的初始化

MX_SDMMC1_SD_Init()
/**
  * [url=home.php?mod=space&uid=247401]@brief[/url] SDMMC1 Initialization Function
  * @param None
  * @retval None
  */
void MX_SDMMC1_SD_Init(void)
{

  /* USER CODE BEGIN SDMMC1_Init 0 */

  /* USER CODE END SDMMC1_Init 0 */

  /* USER CODE BEGIN SDMMC1_Init 1 */

  /* USER CODE END SDMMC1_Init 1 */
  hsd1.Instance = SDMMC1;
  hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING;
  hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
  hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B;
  hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE;
  hsd1.Init.ClockDiv = 0x02;

  if(HAL_SD_Init(&hsd1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SDMMC1_Init 2 */

  /* USER CODE END SDMMC1_Init 2 */

}
4、FatFs 初始化
用到了FREERTOS建立了一个uSDThread_Entry和一个队列
void MX_FATFS_Init(void)
{
  /* USER CODE BEGIN Init */
  /* additional user code for init */
  /*## FatFS: Link the disk I/O driver(s)  ###########################*/
  if (FATFS_LinkDriver(&SD_DMA_Driver, SDPath) == 0)
  {
    /* creation of uSDThread */
    FSAppThreadHandle = osThreadNew(uSDThread_Entry, NULL, &uSDThread_attributes);

    /* Create Storage Message Queue */
    QueueHandle = osMessageQueueNew(1U, sizeof(uint16_t), NULL);
  }
  /* USER CODE END Init */
}
5、uSDThread_Entry
用来处理队列的各种消息,并做对应处理
static void uSDThread_Entry(void *argument)
{
  osStatus_t status;

    if(SD_IsDetected())
    {
      osMessageQueuePut (QueueHandle, &CARD_CONNECTED, 100, 0U);
    }

  /* Infinite Loop */
  for( ;; )
  {
    status = osMessageQueueGet(QueueHandle, &osQueueMsg, NULL, 100);

    if ((status == osOK) && (osQueueMsg== CARD_STATUS_CHANGED))
    {
        if (SD_IsDetected())
        {
          osMessageQueuePut (QueueHandle, &CARD_CONNECTED, 100, 0U);
        }
        else
        {
          osMessageQueuePut (QueueHandle, &CARD_DISCONNECTED, 100, 0U);
        }
     }

     if ((status == osOK) && (osQueueMsg== CARD_CONNECTED))
     {
        //sHAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
                                BSP_LED_On(LED_BLUE);
        //FS_FileOperations();
                                FS_listDirectory();
        statusChanged = 0;
     }

     if ((status == osOK) && (osQueueMsg== CARD_DISCONNECTED))
     {
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
        HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
        osDelay(200);

        f_mount(NULL, (TCHAR const*)"", 0);
        statusChanged = 0;
     }
  }
}
在收到CARD_CONNECTED消息后,会执行FS_listDirectory()

6、SD_DECTECT外部中断处理函数
判断SD_DETECT状态,如果发生改变,向队列中发送CARD_STATUS_CHANGED消息。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == SD_DETECT_Pin)
  {
     if (statusChanged == 0)
     {
       statusChanged = 1;
       osMessageQueuePut ( QueueHandle, &CARD_STATUS_CHANGED, 100, 0U);
     }
  }
}


7、FS_listDirectory
在这里利用FatFs提供的函数:
f_mount挂载SD。
f_opendir打开"/"目录
f_readdir读取"/"目录
通过循环打印出目录下所有文件名和文件大小

static void FS_listDirectory(void)
{
        FRESULT res;
        DIR dir;
        FILINFO fno;
        uint32_t bytesread;
        
        printf("【1】列出目录中文件内容:\r\n");
  if(f_mount(&SDFatFs, (TCHAR const*)SDPath, 0) == FR_OK)
  {
                printf("mount SD success\r\n");
                res = f_opendir(&dir, "/");
                if (res == FR_OK) {
                                printf("open directory success\r\n");
                                printf("list directory / files:\r\n");
        for (;;) {
            res = f_readdir(&dir, &fno);
            if (res != FR_OK || fno.fname[0] == 0) break; // 读取错误或结束标志
            if (fno.fname[0] == '.') continue; // 跳过隐藏文件
            printf("%s  %d\r\n", fno.fname,fno.fsize); // 打印文件名
        }
                        
        f_closedir(&dir);
                }
               
                if(f_open(&SDFile, "STM32_1.TXT", FA_READ) == FR_OK)
                {
                        /* Read data from the text file */
                        res = f_read(&SDFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);

                        if((bytesread > 0) && (res == FR_OK))
                        {
                                printf("\r\nSTM32_1.TXT file content:%s\r\n",rtext);
                                /* Close the open text file */
                                f_close(&SDFile);
                        }
                }
        }               
}
8、另外FS_FileOperations函数实现了对文件读写
f_mkfs:建立文件系统,相当于格式化。
static void FS_FileOperations(void)
{
  FRESULT res;                                          /* FatFs function common result code */
  uint32_t byteswritten, bytesread;                     /* File write/read counts */


  /* Register the file system object to the FatFs module */
  if(f_mount(&SDFatFs, (TCHAR const*)SDPath, 0) == FR_OK)
  {
        printf("mount SD success\r\n");
    /* check whether the FS has been already created */
    if (isFsCreated == 0)
    {
                        /*
      if(f_mkfs(SDPath, &OptParm, workBuffer, sizeof(workBuffer)) != FR_OK)
      {
            printf("make file system success\r\n");
        HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
        return;
      }
      isFsCreated = 1;
                        */
    }
    /* Create and Open a new text file object with write access */
    if(f_open(&SDFile, "STM32_1.TXT", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
    {
            printf("open STEM32.TXT success\r\n");
      /* Write data to the text file */
      res = f_write(&SDFile, (const void *)wtext, sizeof(wtext), (void *)&byteswritten);

      if((byteswritten > 0) && (res == FR_OK))
      {
            printf("write finished, close STEM32.TXT success\r\n");
        /* Close the open text file */
        f_close(&SDFile);

        /* Open the text file object with read access */
        if(f_open(&SDFile, "STM32_1.TXT", FA_READ) == FR_OK)
        {
          /* Read data from the text file */
          res = f_read(&SDFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);

          if((bytesread > 0) && (res == FR_OK))
          {
                  printf("open finished, close STEM32.TXT success\r\n");
            /* Close the open text file */
            f_close(&SDFile);

            /* Compare read data with the expected data */
            if(bytesread == byteswritten)
            {
                    printf("read and written bytes are same\r\n");
              /* Success of the demo: no error occurrence */
              //HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
                                                        BSP_LED_On(LED_RED);
              return;
            }
          }
        }
      }
    }
  }
  /* Error */
  //HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
        BSP_LED_On(LED_BLUE);
}


三、效果
通过串口获得结果:
列出了SD卡上的3个文件,其中SYSTEM~1是系统文件,其他2个是自己建的,并打印出了STM32_1.TXT文件内容。



将SD卡插入读卡器并连上电脑,我用的带SD卡的收音机,可以看到被识别了U盘,可以看到下面有2个文件。


使用特权

评论回复
沙发
呈兴| | 2024-9-20 22:54 | 只看该作者
专为小型的嵌入式系统而设计

使用特权

评论回复
板凳
Amazingxixixi| | 2024-10-31 16:07 | 只看该作者
跟进学习一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

60

帖子

0

粉丝