打印
[应用相关]

如何使用CubeMx制作一个基于SD卡的文件系统工程(转载)

[复制链接]
1212|26
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
观海|  楼主 | 2019-6-17 12:03 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1 前言

本文将介绍使用CubeMx工具从零开始制作一个基于SD卡的文件系统,以便后续使用此功能者参考。


使用特权

评论回复
沙发
观海|  楼主 | 2019-6-17 12:04 | 只看该作者

本文工程得测试将以STM3240G-EVAL这个ST官方的评估板为测试平台,所有这块板子的一些外部组件连接方式有必要先了解一下。



LED1~LED4分别使用管脚PG6,PG8,PI9,PC7,输出高电平LED灯点亮,用户按键使用PG15管脚,按下时为低电平。


使用特权

评论回复
板凳
观海|  楼主 | 2019-6-17 12:04 | 只看该作者



如上图,MCU使用的HSE是25M晶振。SD卡使用STM32F407固定的6个管脚如上图所示D0(PC8),D1(PC9),D2(PC10),D3(PC11),CLK(PC12), CMD(PD2). SD卡插入检测脚为用户自定义的管脚,在STM3240G这块ST官方的评估板上是使用PH13管脚。


使用特权

评论回复
地板
观海|  楼主 | 2019-6-17 12:05 | 只看该作者
3 创建CubeMx工程与代码修改
3.1 创建CubeMx工程

打开CubeMx创建一个基于STM32F407IGHx的工程,使能RCC的HSE,外部晶振模式,SDIO使用4位总线宽度,如下图所示:




使用特权

评论回复
5
观海|  楼主 | 2019-6-17 12:05 | 只看该作者
并在pinout右边将PH13设置为INPUT模式:

使用特权

评论回复
6
观海|  楼主 | 2019-6-17 12:05 | 只看该作者
接来下配置时钟树,将主屏配置为168M:

使用特权

评论回复
7
观海|  楼主 | 2019-6-17 12:06 | 只看该作者
在配置页面下的配置如下图所示:

使用特权

评论回复
8
观海|  楼主 | 2019-6-17 12:06 | 只看该作者
在中间件添加SD Card类型的FATFS:

在配置中的文件系统相关参数配置都使用默认参数值。

使用特权

评论回复
9
观海|  楼主 | 2019-6-17 12:06 | 只看该作者
另外一点非常重要的是,在工程设置内配置栈大小为0x800,默认的0x400是不够的,运行时会出错。如下:

最后点击生成代码。

使用特权

评论回复
10
观海|  楼主 | 2019-6-17 12:07 | 只看该作者
3.2 SD卡和文件系统初始化部分

在main函数内有如下代码:

  MX_GPIO_Init();  MX_SDIO_SD_Init();  MX_FATFS_Init();

使用特权

评论回复
11
观海|  楼主 | 2019-6-17 12:07 | 只看该作者
SDIO初始化代码已经自动生成,不需要修改任何地方,进入到MX_FATFS_Init()函数内,在/* USER CODE BEGIN Init /与/ USER CODE END Init */内添加用户自定义代码,如下:

/* USER CODE BEGIN Variables */
FATFS SDFatFs;
/* USER CODE END Variables */

void MX_FATFS_Init(void)
{
  /*## FatFS: Link the SD driver ###########################*/
  retSD = FATFS_LinkDriver(&SD_Driver, SD_Path);

  /* USER CODE BEGIN Init */
  /* additional user code for init */
    if(f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0) != FR_OK)
    {
      /* FatFs Initialization Error */
      Error_Handler();
    }
    else
    {
//      if(f_mkfs((TCHAR const*)SD_Path, 0, 0) != FR_OK)        //格式化SD存储卡
//      {
//          /* FatFs Format Error */
//          Error_Handler();
//      }
    }
  /* USER CODE END Init */
}

如上代码,自己定义全局变量SDFatFs,以供后续代码需要用到。
上面代码中在挂载SD卡后的注释掉的f_mkfs行是格式化SD卡,可以根据情况来是否需要格式化SD卡,这里选择不格式化。


使用特权

评论回复
12
观海|  楼主 | 2019-6-17 12:07 | 只看该作者
3.3 文件系统与SDIO对接部分

文件系统与SDIO对接有些地方需要修改。
打开bsp_driver_sd.c文件,找到BSP_SD_IsDetected函数,由于SD卡插入检测是通过用户定义的管脚来实现检测的(本例是通过PH13来检测的),因此,这里需要匹配下,在此函数内的/* USER CODE BEGIN 1 /与/ USER CODE END 1 */之间添加用户自己的代码:

uint8_t BSP_SD_IsDetected(void)
{
  __IO uint8_t status = SD_PRESENT;

  /* USER CODE BEGIN 1 */
  /* user code can be inserted here */
  if(HAL_GPIO_ReadPin(SD_DETECT_GPIO_Port, SD_DETECT_Pin) != GPIO_PIN_RESET) //SD卡插入时为低电平
    {
        status =SD_NOT_PRESENT;
    }
  /* USER CODE END 1 */     

  return status;
}

就这样,就移植好了,接来下是写测试代码。


使用特权

评论回复
13
观海|  楼主 | 2019-6-17 12:08 | 只看该作者
3.4 测试文件系统
回到main函数,在/* USER CODE BEGIN 2 /与/ USER CODE END 2 */添加自己的文件系统测试代码,如下:

if(f_open(&MyFile, "STM32.TXT", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) //创建一个文件
    {
        /* 'STM32.TXT' file Open for write Error */
        Error_Handler();
    }
  else
  {
            res = f_write(&MyFile, wtext, sizeof(wtext), (void *)&byteswritten);//写入内容
            if((byteswritten == 0) || (res != FR_OK))
            {
                /* 'STM32.TXT' file Write or EOF Error */
                Error_Handler();
            }
            else
            {
                f_close(&MyFile); //关闭文件

                /*##-7- Open the text file object with read access ###############*/
                if(f_open(&MyFile, "STM32.TXT", FA_READ) != FR_OK) //再次打开文件
                {
                    /* 'STM32.TXT' file Open for read Error */
                    Error_Handler();
                }
                else
                {
                    res = f_read(&MyFile, rtext, sizeof(rtext), (UINT*)&bytesread);  //读出内容

                    if((bytesread == 0) || (res != FR_OK))
                    {
                        /* 'STM32.TXT' file Read or EOF Error */
                        Error_Handler();
                    }
                    else
                    {
                        f_close(&MyFile); //关闭文件

                /*##-10- Compare read data with the expected data ############*/
                if((bytesread != byteswritten))  //比较
                {
                  /* Read data is different from the expected data */
                  Error_Handler();
                }
                else
                {
                  /* Success of the demo: no error occurrence */
                  HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
                }
                    }
                }
            }
  }



上述代码流程为:创建一个文件,写入内容,再关闭此文件,然后再次打开这个文件,读出内容,关闭文件,最后比较读出的内容是否为写入的内容,根据比较结果分别进行处理。

最后编译整个工程烧录到STM3240G-EVAL这块评估板上进行测试,运行结果是OK的。



使用特权

评论回复
14
观海|  楼主 | 2019-6-17 12:08 | 只看该作者
4 结论

CubeMx还是相当强大的,将一个SD存储卡的文件系统通过几步设置就自动生成了工程,只需要掌握几处关键地方进行修改就能让这么一个看似很复杂的工程通过几步简单的步骤就完成了。CubeMx是一个非常强大的工具,此文最关键的参数就栈大小设置为0x800,没碰到过的人需要花很长时间才会发现这个问题,所以再次强调下。


使用特权

评论回复
15
观海|  楼主 | 2019-6-17 12:09 | 只看该作者
上文并没有考虑SD卡拔插问题,且SDIO没有使用DMA,以下文字作为补充,将示例如何改善这两方面的问题。

使用特权

评论回复
16
观海|  楼主 | 2019-6-17 12:09 | 只看该作者
1 SD卡拔插检测

FATFS文件系统初始化得修改下:

void MX_FATFS_Init(void)
{
  /*## FatFS: Link the SD driver ###########################*/
  retSD = FATFS_LinkDriver(&SD_Driver, SD_Path);

  /* USER CODE BEGIN Init */
  /* additional user code for init */

  /* USER CODE END Init */
}

即保持CubeMx自动生成的原样即可,并不需要增加任何其他内容。


使用特权

评论回复
17
观海|  楼主 | 2019-6-17 12:10 | 只看该作者
关键实在main中的while循环中检测SD卡的拔插:

while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        curSdCardStatus =BSP_SD_IsDetected();
        if(curSdCardStatus !=preSdCardStatus)
        {
            switch(curSdCardStatus)
            {
            case SD_PRESENT:
                BSP_SD_Init();
                MountFat32FileSystemImmeditely();
                break;
            case SD_NOT_PRESENT:
                UnmountFilesystem();
                //FATFS_UnLinkDriver("0:/");
                break;
            }
            preSdCardStatus =curSdCardStatus;
        }
//      HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
//      HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
//      HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
//      HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);
//      HAL_Delay(200);
  }

如上代码,循环检测SD卡的插入状态,一旦发现状态有变化,则进行相应操作,比如挂载文件系统,或者卸载文件系统。

使用特权

评论回复
18
观海|  楼主 | 2019-6-17 12:10 | 只看该作者
挂载文件系统
tatic int  MountFat32FileSystem(void)
{
        FRESULT ret ;

        ret =f_mount(&SDFatFs, "0:/", 0);
        if(ret!= FR_OK)
    {
      if(ret ==FR_DISK_ERR)
            {
//              if(f_mkfs((TCHAR const*)SD_Path, 0, 0) != FR_OK)        //format the SDCard with FAT32 filesystem
//              {
//                  /* FatFs Format Error */
//                  Error_Handler();
//              }
            }
            else
            {
        Error_Handler();
            }
    }

        return 0;
}



使用特权

评论回复
19
观海|  楼主 | 2019-6-17 12:10 | 只看该作者
函数f_mount为实际操作挂载文件系统的函数,有3个输入参数,最后那个参数为0,表示先不挂载,等到fopen时再挂载,为1时,则表示立即挂载,在这个函数内部会使用check_fs函数去检查是否为FAT32文件系统,如果是,在挂载成功,否则失败。

当然这里可以立即挂载,但是你若不断拔插SD卡,还是有一定的概率会出意外,比如后续fopen失败。因此,这里第3个参数使用0,不马上挂载,而是等到真正读取文件时才执行挂载文件系统。


使用特权

评论回复
20
观海|  楼主 | 2019-6-17 12:11 | 只看该作者
文件系统卸载

static int UnmountFilesystem(void) {
FRESULT ret;
ret =f_mount(NULL, "0:/", 0);
     return ret;
}

f_mout的第3个参数在这里没有意义,在这个f_mount函数内部会忽略它。


使用特权

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

本版积分规则

99

主题

3882

帖子

1

粉丝