发新帖本帖赏金 100.00元(功能说明)我要提问
返回列表
打印
[开发工具]

如何使用 F4A0 RT-Thread bsp 驱动 sd/tf 卡文件系统?

[复制链接]
4283|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yang377156216 于 2023-8-29 16:19 编辑
#申请原创# @21小跑堂
   
    由于MCU本身的FLASH容量有限,而在本项目中需要存储大量的电能数据,此时则要使用外部的存储器,同时考虑到成本以及方便在其他设备上使用,microSD卡成为常用外部存储器,它具有体积小、易使用、容量大的优点,同时现有的配套软件和硬件非常齐全,能够达到开箱即用的效果。为了管理大容量的SD卡设备,还需要一个文件系统,好在RT-Thread已经为我们提供了一套完善的文件系统机制,称为Device Virtual File System(即设备虚拟文件系统),提供POSIX标准的文件操作功能(open,write,read等),完全可以使用与PC端基本一致的API调用进行文件的读写等操作。microSD卡和文件系统之间需要的驱动程序(elm-FatFs和MSD)也由RT-Thread提供了,我们几乎不需要编写代码就能够在MCU的嵌入式系统上,读写SD卡上的文件了,省去学习和调试代码的大量时间。本文将介绍在 F4A0 平台上,基于 RT-Thread bsp 框架实现使用 spi 接口对外部 SD/TF 卡的操作。
  • IDE :Keil MDK 5.2X
  • 硬件平台 :EV_HC32F4A0_LQFP176_Rev1.0 开发板和 16G 的 TF 卡
  • Env tool :env_released_x_x_x
  • 调试工具 :USB 串口线和逻辑分析仪


基础工程
    本项目工程之前是基于 RT-Thread v4.0.4 版本上开发的,所以得克隆下对应版本,链接为: https://gitee.com/rtthread/rt-thread/tree/v4.0.4 。至于如何搭建 env 环境和构建基础的 F4A0 rt-thread bsp 的 MDK 工程,在此忽略,可参考上篇介绍的流程进行处理: https://bbs.21ic.com/icview-3320018-1-1.html 。
配置工程
    得到基础工程后,需要在 menuconfig 环境下完成所需功能的配置。
  • RT-Thread Components -> Device virtual file system 开启文件系统,并启用 Enable elm-chan fatfs 开启 elm-fatfs 文件系统

  • Hardware Drivers Config -> On-chip Peripheral Drivers -> SPI Drivers  开启spi1

  • RT-Thread Components -> Device Drivers -> Using SPI Bus/Device device drivers开启SPI设备驱动,并启用Using SD/TF card driver with spi选项开启spi协议的sd卡驱动支持,由于文件系统中还会需要 RTC 的支持,暂时使用软件模拟 RTC 来适配该功能,启用 Using RTC device drivers 选项和启用 Using software simulation RTC device 选项来开启 RTC 功能

    到此,已经完成了所需驱动的所有配置。接下来就可以重新生成对应的 Keil 工程了,使用命令 : scons --target=mdk5 -s ,生成的工程文件为根目录下的 project.uvprojx 。

修改配置代码
    由于 bsp 中未使能 spi 对应引脚的配置功能,所以需要根据硬件原理图完成更改,开发板上的连线方式是 SDIO 接口的,实际上也可以直接换成 SPI 接口的,而且加上 F4A0 具备通信端口可自由映射的超级方便的功能,这样可以直接在原 demo 板上实现一个 TF 卡既可以用 SDIO 驱动又可以用 SPI 模式驱动。根据原理图可以得到对应的通信引脚功能配置如下:
  • PB05   -  SPI1_NSS       3线制软cs
  • PD02  -   SPI1_MOSI    FUNC41
  • PC12  -   SPI1_SCK       FUNC40
  • PB07  -  SPI1_MISO     FUNC42


    相应需要更改的 board_config.h 代码如下:
#if defined(BSP_USING_SPI1)
#define SPI1_NSS_PORT                   (GPIO_PORT_B)
#define SPI1_NSS_PIN                    (GPIO_PIN_05)
#define SPI1_NSS_GPIO_FUNC              (GPIO_FUNC_0_GPO)

#define SPI1_SCK_PORT                   (GPIO_PORT_C)
#define SPI1_SCK_PIN                    (GPIO_PIN_12)
#define SPI1_SCK_GPIO_FUNC              (GPIO_FUNC_40_SPI1_SCK)

#define SPI1_MOSI_PORT                  (GPIO_PORT_D)
#define SPI1_MOSI_PIN                   (GPIO_PIN_02)
#define SPI1_MOSI_GPIO_FUNC             (GPIO_FUNC_41_SPI1_MOSI)

#define SPI1_MISO_PORT                  (GPIO_PORT_B)
#define SPI1_MISO_PIN                   (GPIO_PIN_07)
#define SPI1_MISO_GPIO_FUNC             (GPIO_FUNC_42_SPI1_MISO)
#endif
    接着创建 spi_tfcard.c 应用代码层源文件,主要是在整个 component 初始化时触发调用 spi msd 驱动设备的初始化,详细见下面代码:
void sd_mount(void *parameter)
{
    while (1)
    {
        rt_thread_mdelay(500);
        if(rt_device_find("sd0") != RT_NULL)
        {
            if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
            {
                LOG_I("sd card mount to '/'");
                break;
            }
            else
            {
                LOG_W("sd card mount to '/' failed!");
            }
        }
    }
}

int hc32_sdcard_mount(void)
{
    rt_thread_t tid;

    tid = rt_thread_create("sd_mount", sd_mount, RT_NULL,
                           1024, RT_THREAD_PRIORITY_MAX - 2, 20);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    else
    {
        LOG_E("create sd_mount thread err!");
    }
    return RT_EOK;
}
INIT_APP_EXPORT(hc32_sdcard_mount);

static int rt_hw_spi1_tfcard(void)
{
    hc32_hw_spi_device_attach("spi1", "spi10", SPI1_NSS_PORT, SPI1_NSS_PIN);
    LOG_I("hw_spi_device_attach ok!\r\n");
    return msd_init("sd0", "spi10");
}
INIT_DEVICE_EXPORT(rt_hw_spi1_tfcard);
    似乎到这里基本就完成了所有代码的编辑了。好,现在是踩坑时刻,编译运行起来后串口输出没有正确 mount 的信息,得一步步 debug 了。
    首先一个坑,msd_init 函数调用后一直无法正确触发回调到底层配置 hc32_spi_configure 函数里面来,导致 spi1 初始化未能正确完成。经过一番挣扎,终于发现到问题点:之前用的是  INIT_COMPONENT_EXPORT(rt_hw_spi1_tfcard); 需要改成  INIT_DEVICE_EXPORT(rt_hw_spi1_tfcard);   至于为什么,那要好好研究 rt-thread 了,在此不作过多讨论。
    第二个坑,使用逻辑分析仪抓波形,发现 cs 引脚电平一直不变而其它引脚上有波形了。很明显这是 IO 配置的问题,一下子就定位到了问题点:需要在 board_config.c  的 hc32_board_spi_init 初始化函数中添加以下代码使能输出作用并且初始化拉高 cs:
GPIO_OE(SPI1_NSS_PORT, SPI1_NSS_PIN, Enable);
GPIO_SetPins(SPI1_NSS_PORT, SPI1_NSS_PIN);
    到这里填完坑后,才真正可以实现所需要的功能了。

测试验证
    编译代码并下载成功后,飞线将 PH15/PH13 连接到板载 CMSIS-Dap 端的 RX/TX 即可使用调试器枚举出的虚拟串口,开启串口工具以及抓取对应的 SPI1 波形,正常会显示如下:

    另外,不再多写一句代码都可以进行测试读写 TF 卡,那就是在 msh 中使用命令实现,因为文件系统中已经自带了该功能。大致命令流程梳理如下:
  • 输入list device命令并回车,可以查看设备,[size=1em]这里sd0为分区,后续会对他进行操作
  • [size=1em]使用 date 命令进行时间的设置和查询,让文件系统具备时钟功能
  • 初次使用的SD卡,可能没有文件系统,或者文件系统不是FAT格式的,需要使用 mkfs 命令将SD卡格式化为FAT文件系统,后续才能挂载成功
  • 使用 ls、echo、cat 命令,进行文件读写操作,使用 cd、mkdir、pwd 等命令,进行文件目录的操作


    当然也可以使用文件系统的 api 接口来实现文件的读写操作,这里也不做过多探讨了,因为底层都已经通了,后面可以随便造了。

hc32f4a0_rttv4.0.4_spi_msd.zip.zip (9.99 MB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2023-08-31
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-8-31 15:38 回复TA
基于 RT-Thread bsp 框架实现使用 spi 接口对外部 SD/TF 卡进行操作,解决大量数据存储问题。 
沙发
guoyt| | 2023-8-29 19:47 | 只看该作者
软件模拟 RTC 来适配

使用特权

评论回复
板凳
yang377156216|  楼主 | 2023-8-30 09:20 | 只看该作者
guoyt 发表于 2023-8-29 19:47
软件模拟 RTC 来适配

其实可以使用硬件 RTC ,bsp 也有的,只是图省事儿

使用特权

评论回复
地板
gangong| | 2024-10-27 09:05 | 只看该作者
不错了

使用特权

评论回复
5
gangong| | 2024-10-27 09:13 | 只看该作者
棒非常好

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

204

帖子

10

粉丝