RT-Thread Studio 使用STM32CubeMX联合开发——基于SPI通信,SFUD驱动,FAL组件使用W25Q32

引言

        本文为RT-Thread Studio 使用STM32CubeMX联合开发中——基于SPI通信,SFUD驱动,FAL组件使用W25Q32的读写。主要涉及:

  • SPI配置:最基本硬件接口;
  • SFUD配置:基于与W25Q32交互的驱动;
  • FAL配置:SFUD配置的抽象层;

        先看层级关系。

一、CubeMX配置SPI

        打开全输主SPI模式,我的芯片SPI最高支持18M,这里我设置15M。设置好后生成代码,然后关闭CubeMX。

二、RT-Thread Studio配置

        设备驱动里面打开SPI、打开SFUD、显示更多SFUD调试信息(可选)。

        打开FAL组件、打开FAL使用SFUD驱动、驱动名称修改为W25Q32(可选)。

        保存。编译,然后发现一大堆错误,这是因为还要去修改代码。

三、代码配置

(1)SPI配置

        去到CubeMX生成的 main.c 文件里面加入以下代码。这是为了让外部的RT-Thread去调用我们SPI的初始化。如果你的是SPI1就在里面放SPI1的初始化代码。

void RTT_STM32_HAL_SPI_Init(void)
{
    MX_SPI2_Init();
}

        打开 board.h 文件,打开里面SPI相关的宏。我用的是SPI2,所以打开SPI2。

        打开 board.c 文件,调用我们刚才的SPI初始化代码。

(2)FAL配置

        移动 fal_cfg.h 到 fal 目录下的 inc 文件夹中。

        移动 fal_flash_sfud_port.c 到 fal 目录下的 src 文件夹中。

         移动后。

        编译。发现有报错的,原因是我们没用用到芯片内部的Flash,只是用外部的,所以会报错。这里把芯片内部相关的删掉后好了。

        删掉内容。然后改一下这个宏 NOR_FLASH_DEV_NAME ,改为我们刚刚命名的设备名称,也就是 "W25Q32"。

         打开 fal_flash_stm32f2_port.c 文件。修改设备参数,按照自己的设备情况来。

        修改完,然后编译。已经没有错误了,接下来开始写应用代码。

四、基础代码

        application 文件夹下新建一个 w25q32.c 文件,我们的代码主要放在里面写。

        加入以下代码。

/*
 * @Author: Troubadour 2276791354@qq.com
 * @Date: 2024-04-28 17:51:03
 * @LastEditors: Troubadour 2276791354@qq.com
 * @LastEditTime: 2024-04-28 18:13:17
 * @Version:
 * @Description:
 */

#include "drv_spi.h"
#include "spi_flash_sfud.h"
#include "fal.h"


#define W25Q32_CS_GPIOx         GPIOB
#define W25Q32_CS_PIN           GPIO_PIN_12

rt_spi_flash_device_t rtt_spi_dev = RT_NULL;


int W25Q32_Init(void)
{
    /* Step1. 绑定片选引脚,也就是绑定设备。spi20 表示挂载在 spi2 总线上的 0 号设备, PB12是片选,这一步就可以将从设备挂在到总线中 */
    if (rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12) != RT_EOK)
    {
        LOG_E("SPI device attach failed! ");
        return -1;
    }

    /* 通过SFUD(串行闪存通用驱动程序)驱动程序库和SPI设备探测SPI FLASH设备。 */
    rtt_spi_dev = rt_sfud_flash_probe("W25Q32", "spi20");
    if (rtt_spi_dev == RT_NULL)
    {
        LOG_E("sfud flash probe failed! ");
        return -1;
    }

    /* ------------------------------------- */
    /* Step2. FAL initialization.            */
    /* ------------------------------------- */
    fal_init();
    LOG_D("W25Q32 ready! ");

    return 0;
}

INIT_COMPONENT_EXPORT(W25Q32_Init);

        运行...

五、应用。

(1)命令行测试

        注!RT-Thread是带命令行测试功能的,这还是单片机(我的单片机STM32F103ZET6)。接下来我们先来用命令行试一试看看能不能实际的跑。先来个 fal 查看支持的命令。

  • fal probe  -- 查看分区
  • fal read -- 读数据
  • fal write -- 写数据
  • fal erase -- 擦除数据
  • fal bench -- 性能测试

1.1 fal probe  -- 查看分区

        fal probe 查看所有分区。

        fal probe <name> 查看指定设备/分区。

1.2 fal read -- 读数据

        fal read <addr> <size> 从指定地址addr开始读取size个字节。

1.3 fal write -- 写数据

1.4 fal erase -- 擦除数据

        写数据就要先擦除!

        fal write <addr> <size> 从指定地址addr开始写入size个字节。

        fal erase <addr> <size> 从指定地址addr开始擦除size个字节。

1.5 fal bench -- 性能测试

        fal bench <sector> <yes> 使用指定的大小测试性能(一般选中扇区的大小,我的是4096),然后加一个yes,因为测试性能会导致数据丢失,所以需要加 yes 确定!

        测试前需要先使用 fal probe <device / partition> 选中分区或设备。(选中设备就是测试整个设备所有的分区)。

       示例:测试单个分区:fal probe easyflash。我的分区设置的是 1M 的大小,所用时间还行,大概几秒钟的时间。

       示例: 测试整个设备:fal probe W25Q32。我的整个设备是 4M 的大小,所有用的时间就比较长了。

(2)实际代码测试

        加入以下代码测试写入的字符串是否还能成功读取。

/*
 * @Author: Troubadour 2276791354@qq.com
 * @Date: 2024-04-28 17:51:03
 * @LastEditors: Troubadour 2276791354@qq.com
 * @LastEditTime: 2024-04-29 09:29:15
 * @Version:
 * @Description:
 */

#include "drv_spi.h"
#include "spi_flash_sfud.h"
#include "fal.h"


#define W25Q32_CS_GPIOx         GPIOB
#define W25Q32_CS_PIN           GPIO_PIN_12

rt_spi_flash_device_t rtt_spi_dev = RT_NULL;


int W25Q32_Init(void)
{
    /* Step1. 绑定片选引脚,也就是绑定设备。spi20 表示挂载在 spi2 总线上的 0 号设备, PB12是片选,这一步就可以将从设备挂在到总线中 */
    if (rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12) != RT_EOK)
    {
        LOG_E("SPI device attach failed! ");
        return -1;
    }

    /* 通过SFUD(串行闪存通用驱动程序)驱动程序库和SPI设备探测SPI FLASH设备。 */
    rtt_spi_dev = rt_sfud_flash_probe("W25Q32", "spi20");
    if (rtt_spi_dev == RT_NULL)
    {
        LOG_E("sfud flash probe failed! ");
        return -1;
    }

    /* ------------------------------------- */
    /* Step2. FAL initialization.            */
    /* ------------------------------------- */
    fal_init();
    LOG_D("W25Q32 ready! ");

    return 0;
}

INIT_COMPONENT_EXPORT(W25Q32_Init);


const rt_uint8_t TestWriteString[] = "Hello! This is test content.";
void W25Q32_WriteStr(int argc, char **argv)
{
    rt_uint32_t WriteCount = 0;

    /* Step1. Erase to Flash */
    LOG_I("Run W25Q32 Erase.");
    WriteCount = fal_partition_erase(fal_partition_find("easyflash"), 0, 4096);
    if (WriteCount <= 0)
    {
        LOG_E("fal_partition_write failed! ");
    }
    else
    {
        LOG_I("fal_partition_write succeess! Write size: %d", WriteCount);
    }

    /* Step2. Write to Flash */
    LOG_I("Run W25Q32 WriteStr.");
    WriteCount = fal_partition_write(fal_partition_find("easyflash"), 0, TestWriteString, sizeof(TestWriteString));
    if (WriteCount <= 0)
    {
        LOG_E("fal_partition_write failed! ");
    }
    else
    {
        LOG_I("fal_partition_write succeess! Write size: %d", WriteCount);
    }
}


void W25Q32_ReadStr(int argc, char **argv)
{
    rt_uint32_t ReadCount = 0;
    uint8_t ReadBuff[128] = {0};

    /* Step1. Read data from Flash */
    LOG_I("Read W25Q32 data.");
    ReadCount = fal_partition_read(fal_partition_find("easyflash"), 0, ReadBuff, sizeof(ReadBuff));
    if (ReadCount != sizeof(ReadBuff))
    {
        LOG_E("fal_partition_read failed! ");
    }
    else
    {
        LOG_I("fal_partition_read succeess! Read size: %d", ReadCount);
        LOG_I("Read data: %s", ReadBuff);
    }
}

MSH_CMD_EXPORT(W25Q32_WriteStr, Run W25Q32 WriteStr);
MSH_CMD_EXPORT(W25Q32_ReadStr, Run W25Q32 ReadStr);

        可以看到。成功读取! 

六、FAL的API讲解。

(1)fal_init

int fal_init(void)

        该函数初始化FAL组件。返回值是分区总数。代码中注释的描述是 >=0 有效,但正常情况下<=0 是无效的,也就是初始化失败。

(2)fal_partition_erase

int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)

        擦除操作函数。返回值小于0为失败。可以擦除指定分区,或者指定设备(所有分区)。

  • part:分区结构体指针。可以使用 fal_partition_find("easyflash") 获取。
  • addr:擦除的起始地址。
  • size:擦除大小,单位Byte。一般选择扇区大小的倍数。

(3)fal_partition_write

int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size)

        写数据操作。返回值为写入的数据量。如果扇区有数据,需要先擦除再写入!

  • part:分区结构体指针。可以使用 fal_partition_find("easyflash") 获取。
  • addr:写入的起始地址。
  • buf:数据。
  • size:数据量,单位Byte。

(4)fal_partition_read

int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)

        读取数据操作。返回值为读取的数据量。

  • part:分区结构体指针。可以使用 fal_partition_find("easyflash") 获取。
  • addr:读取的起始地址。
  • buf:存放读取数据的缓冲区。
  • size:数据量,单位Byte。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Troubadour~

觉得不错,打赏支持一下!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值