移植FlashDB、SFUD到STM32f407

news2024/11/27 4:00:44

个人上篇文章  搭建STM32F407的SPI-Flash(基于STM32CubeMX)_小刚学長的博客-CSDN博客

主要是解决STM32CubeMX这边的配置,对code端侧是简单介绍了下

实际项目上一般都是拿片外flash存储一些东西,比如一些比较多的配置、参数,偶尔修改下,还有一些程序文件,日志之类的。存在读写操作,而nor flash是每次擦除后才可以写入(也就是说你要更新内容,也是要先擦除再写入新的数据),往往这种操作比较麻烦(最近简单的方法,先把当簇内容读出来,然后对相应的位置进行修改,然后先擦除当前簇,再写入),如果更新内容存在跨簇,那更麻烦点,需要判断。另外,还有个特点,nor flash存在擦除次数上限,如果超过一定次数,此区域容易坏。

对于windows、linux,存在文件系统,文件系统就可以很好规避以上问题,使用时也不用去关心、去计算这些值。对于STM32显然文件系统开销很大,不太合适

那么STM32有没有轻量点库之类的,答案是有的:

FlashDB + SFUD + SPI Flash 

FlashDB就是个简单版本的DB,也是专门适合MCU环境,而且针对Flash特点,对相同簇写入次数也有优化(写入均衡)。FlashDB 支持kv类型数据库、也支持时间序列数据库,一般也足够用了。关键还是占资源少,写flash均衡,这样应用者可更关注逻辑部分。是个好东西,记得早些年,都是自己造轮子的,又无法证明自己造轮子是可靠的,因此经常被吊。

当然,FlashDB 不仅仅是 通过SFUD去访问SPI Flash,也可以通过自己封装的SPI FLASH驱动代码去访问,也支持文件系统(文件方式)去访问外部flash。但经典组合,还是 

由于芯片本身不同,这里芯片不仅仅是FLASH芯片,还包括MCU,因此要实现自己的代码,需要做相应的改进(调整),以下的方案是基于STM32F407芯片,具体硬件配置,参考上篇

1. 把相关代码复制到工程里面:(包括对应 h文件)

  

2. fal_cfg.h  fal 配置

#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#include "main.h"

#define FAL_DEBUG 1
#define FAL_PART_HAS_TABLE_CFG
#define FAL_USING_SFUD_PORT

/* ===================== Flash device Configuration ========================= */
extern struct fal_flash_dev nor_flash0;

/* flash device table */
#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &nor_flash0,                                                     \  //nor flash 基本信息以及读写擦除接口等。是fal_flash_sfud_port.c 有描述,也是需要初始化的对象
}

/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE                                                                 \
{                                                                                      \
    {FAL_PART_MAGIC_WROD,  "fdb_kvdb1",       "norflash0",           0, 1024*1024, 0}, \
/* 1M(FLASH 地址0~1M) 大小的table, 这里只是一张表,根据实际情况配置*/
}
#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */

3. fal_flash_sfud_port.c 修改,印象中只修改过 init 

#ifndef FAL_USING_NOR_FLASH_DEV_NAME
#define FAL_USING_NOR_FLASH_DEV_NAME             "norflash0"
#endif

static int init(void);
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
static int erase(long offset, size_t size);

static sfud_flash_t sfud_dev = NULL;
struct fal_flash_dev nor_flash0 =
{
    .name       = FAL_USING_NOR_FLASH_DEV_NAME,
    .addr       = 0,
    .len        = 1024 * 1024,
    .blk_size   = 4096,
    .ops        = {init, read, write, erase},
    .write_gran = 1
};

static int init(void)
{

#ifdef RT_USING_SFUD
    /* RT-Thread RTOS platform */
    sfud_dev = rt_sfud_flash_find_by_dev_name(FAL_USING_NOR_FLASH_DEV_NAME);
#else
    /* bare metal platform */
    extern sfud_flash flash_table[1];  // 这里table实际上是 指 SFUD_FLASH_DEVICE_TABLE
    sfud_dev = &flash_table[0];  // sfud_dev 指向 table[0] 这个非常重要
#endif

    if (NULL == sfud_dev)
    {
        return -1;
    }

    /* update the flash chip information */
    nor_flash0.blk_size = sfud_dev->chip.erase_gran;
    nor_flash0.len = sfud_dev->chip.capacity;

    return 0;
}

static int read(long offset, uint8_t *buf, size_t size)
{
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    sfud_read(sfud_dev, nor_flash0.addr + offset, size, buf);

    return size;
}

static int write(long offset, const uint8_t *buf, size_t size)
{
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    if (sfud_write(sfud_dev, nor_flash0.addr + offset, size, buf) != SFUD_SUCCESS)
    {
        return -1;
    }

    return size;
}

static int erase(long offset, size_t size)
{
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    if (sfud_erase(sfud_dev, nor_flash0.addr + offset, size) != SFUD_SUCCESS)
    {
        return -1;
    }

    return size;
}

4. sfud_cfg.h 配置 SFUD_FLASH_DEVICE_TABLE


#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_

#define SFUD_DEBUG_MODE

#define SFUD_USING_SFDP

#define SFUD_USING_FLASH_INFO_TABLE

enum {
    SFUD_W25Q128_DEVICE_INDEX = 0,   // 如果多个,往下加,名字自己取,但要跟其他对应好
};

// 上下要对应好
#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_W25Q128_DEVICE_INDEX] = {.name = "W25Q128B", .spi.name = "SPI1"},       \
}

#endif /* _SFUD_CFG_H_ */

5. sfud_port.c 修改,就是要实现 对接SPI接口

    要做:

     SPI相关配置:是通过HAL访问SPI,还是其他?片选信号对应GPIO信息

     SPI延时函数、锁、解锁等

     SPI 读写接口封装

#include "spi.h"


typedef struct {
	SPI_HandleTypeDef *spix;
	GPIO_TypeDef *cs_gpiox;
    uint32_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;

void sfud_log_debug(const char *file, const long line, const char *format, ...);

static void spi_configuration(spi_user_data_t spi) {
	
}

static void spi_lock(const sfud_spi *spi) {
    __disable_irq();
}

static void spi_unlock(const sfud_spi *spi) {
    __enable_irq();
}

/**
 * 这个函数要重新写
 */
static sfud_err spi_write_read(const sfud_spi *spi, uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
        size_t read_size) {
    sfud_err result = SFUD_SUCCESS;
    spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
	uint16_t blockSize = 0;
	uint32_t offset = 0;

    if (write_size) {
        SFUD_ASSERT(write_buf);
    }
    if (read_size) {
        SFUD_ASSERT(read_buf);
    }

// HAL - 片选拉低
	HAL_GPIO_WritePin(spi_dev->cs_gpiox,spi_dev->cs_gpio_pin,GPIO_PIN_RESET); 

	if (write_size)
	{
		if (HAL_SPI_Transmit(spi_dev->spix, write_buf, write_size, 100) != HAL_OK)
		{
			result = SFUD_ERR_WRITE;
			goto exit;
		}
	}

	while (read_size)
	{
		blockSize = read_size > 0xFFFF ? 0xFFFF : read_size;
		read_size -= blockSize;
		if (HAL_SPI_Receive(spi_dev->spix, read_buf + offset, blockSize, 100) != HAL_OK)
		{
			result = SFUD_ERR_READ;
			goto exit;
		}
		offset += blockSize;
	}

exit:
// HAL - 片选拉高
	HAL_GPIO_WritePin(spi_dev->cs_gpiox,spi_dev->cs_gpio_pin,GPIO_PIN_SET);

    return result;
}

/* about 100 microsecond delay */
static void retry_delay_100us(void) {
	DWT_Delay(100); // 这里要自己去实现一个,主要是100微秒,不是100ms
}

// spi 关键信息配置,对应spi多少,片选多少
static spi_user_data spi1 = { .spix = &hspi1, .cs_gpiox = SPI_FLASH_CS_GPIO_Port, .cs_gpio_pin = SPI_FLASH_CS_Pin };
sfud_err sfud_spi_port_init(sfud_flash *flash) {
    sfud_err result = SFUD_SUCCESS;

    switch (flash->index) {
    case SFUD_W25Q128_DEVICE_INDEX: {
        /* SPI 外设初始化 */
        spi_configuration(&spi1);
        /* 同步 Flash 移植所需的接口及数据 */
        flash->spi.wr = spi_write_read;
        flash->spi.lock = spi_lock;
        flash->spi.unlock = spi_unlock;
        flash->spi.user_data = &spi1;
        /* about 100 microsecond delay */
        flash->retry.delay = retry_delay_100us;
        /* about 60 seconds timeout */
        flash->retry.times = 60 * 10000;

        break;
    }
    }

    return result;
}

/**
 * This function is print debug info.
 *
 * @param file the file which has call this function
 * @param line the line number which has call this function
 * @param format output format
 * @param ... args
 */
void sfud_log_debug(const char *file, const long line, const char *format, ...) {
	va_list args;
	printf("[%s,%d] ", file, line);
	va_start(args, format);
	printf(format, args);
	va_end(args);
	printf("\n");
}

/**
 * This function is print routine info.
 *
 * @param format output format
 * @param ... args
 */
void sfud_log_info(const char *format, ...) {
	va_list args;
	va_start(args, format);
	printf(format, args);
	va_end(args);
	printf("\n");
}

以上信息,一般网上例子就最多那么多,实际还有两个关键:

1. printf 要重定向,还不能用Micro LIB的方式

在usart.c 添加如下代码,并不要勾选Micro LIB!!

/* USER CODE BEGIN 1 */
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */

#if 1
#if (__ARMCC_VERSION >= 6010050)                    /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");          /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");            /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}

/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* 重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);               /* 等待上一个字符发送完成 */
    
    USART1->DR = (uint8_t)ch;                       /* 将要发送的字符 ch 写入到DR寄存器 */	
    return ch;
}
#endif


/* USER CODE END 1 */

2. 增加芯片信息    SFUD_FLASH_CHIP_TABLE   最后一个就是我的芯片信息!!如果没有,需要手动添加,注意 "W25Q128B" 的name 关键字,这个也不是随便取的,要注意与前面的对应!!

#define SFUD_FLASH_CHIP_TABLE                                                                                    \
{                                                                                                                \
    {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81},      \
    {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                       \
    {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4*1024*1024, SFUD_WM_PAGE_256B, 64*1024, 0xD8},                    \
    {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},                    \
    {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
	{"W25Q128B", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
}

这样应该可以了。我记得就这么多!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1004965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

stu01-IDEA怎么创建一个HTML项目

1.打开idea,依次点击file→new→project 2.点击Java,选择你的jdk,没有下载的点击“Download JDK”/已经下载有JDK但在这里没显示的→点击“Add JDK”,选择你安装的JDK的路径,然后next 3.next 4.起好名字,我…

Python之OS模块

os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;即os模块提供了非常丰富的方法用来处理文件和目录。 使用的时候需要导入该模块:import os

【数据结构】树的基础知识及三种存储结构

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

定时任务执行脚本

1、编写bat脚本 将newman运行测试集的命令编写为bat脚本保存 2、设置定时任务 在计算机上右键->管理,在弹出菜单里的任务计划程序里创建定时任务配置执行

MySQL 8.0 驱动与阿里druid版本兼容操作

注意&#xff1a;这个异常表面druid数据源的版本与MySql 8.0的驱动版本不匹配&#xff0c;解决方法如下&#xff1a; 确保MySql 8.0的驱动如下网址&#xff1a; <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifact…

c语言练习题52:写一个函数判断当前机器是大端还是小端

代码&#xff1a; #include<stdio.h> int check_sys() {int a 1;return *(char*)&a;//小端retrun 1 大端return 0&#xff1b; } int main() {if (check_sys() 1) {printf("小端\n");}elseprintf("大端\n"); } 这里首先取a的地址&#xff0c…

腾讯云服务器怎么样?详细说下站长的看法

购买云服务器首选腾讯云&#xff0c;腾讯云服务器怎么样&#xff1f;作为国内头部原厂商的腾讯云&#xff0c;云服务器无论从安全性、可靠性和速度方面都有很好的保证&#xff0c;腾讯云服务器网来详细说下腾讯云服务器的优势以及腾讯云服务器购买流程&#xff1a; 目录 腾讯…

vscode-server

1know_host清除 2 删除服务器里的home/user/.vscode-server&#xff08;不是根root下的vscode-server&#xff09;&#xff0c;删除时用户名保持一致。 3 ssh配置文件 /etc/ssh/sshd_config[想改变,使用root&#xff0c;修改文件权限] 4 删除修改后&#xff0c;重启Windows下…

夯实思想根基:建行江门市分行持续加强党建工作

建行广东省江门市分行深化落实新时代党的建设总要求&#xff0c;坚持不懈用先进思想武装头脑和凝心铸魂&#xff0c;强化党建工作&#xff0c;夯实思想根基&#xff0c;护航高质量发展。 我是党员我先学 理论学习是党员的“永恒课题”。建行江门分行全体党员干部依托数字党建平…

【Linux学习笔记】基础命令3

1. find命令2. which命令3. alias命令4. whereis命令5. grep命令6. zip和unzip6.1. zip命令6.2. unzip命令 7. tar命令8. 命令行解释器 1. find命令 用法&#xff1a;find 路径名称 -name 文件名功能&#xff1a;可以在指定的路径下&#xff0c;找出所有与文件名相匹配的文…

常用百宝箱——日志处理

目录 前言 一、logging库 二、logging日志等级 三、logging四大组件 四、封装示例 总结 前言 日志是记录特定时间段或事件的详细信息的文件或记录。它们通过时间戳和关键词或描述符来标识事件或行动。日志可以用于许多目的&#xff0c;例如&#xff1a;故障排除、网络安全…

排产中的两种对立策略 - 负载均衡与成本最优

在APS系统设计中&#xff0c;根据不同的业务场景&#xff0c;会出现两种截然相反的策略需求&#xff0c;分别是(资源)负载均衡与成本最优(最低)&#xff0c;两种策略分别适用于不同的业务场景。本文我们将这两种策略的适用场景、方案原理作初步探讨&#xff0c;并就这些策略在O…

SpringBoot项目--电脑商城【确认订单】

1.持久层[Mapper] 1.1规划需要执行的SQL语句 用户在购物车列表页中通过随机勾选相关的商品,在点击"结算"按钮后跳转到"确认订单页",在这个页面中需要展示用户在上个页面所勾选的"购物车列表页"中对应的数据.说白了也就是列表展示,且展示的内容还…

solidworks底部状态栏显示不出来

如下图所示&#xff0c;solidworks主界面下面的状态栏突然不见了。 怎么调出来&#xff1f; 第一步&#xff1a;点击视图菜单&#xff0c;用户界面&#xff0c;把状态栏前的勾勾上。 第二步&#xff1a;把视图下面的触摸模式关掉&#xff0c;这一点很容易被大家忽略。

代码随想录--哈希--两个数组的交集

题意&#xff1a;给定两个数组&#xff0c;编写一个函数来计算它们的交集。 说明&#xff1a; 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序。 import java.util.ArrayList; import java.util.HashMap; import java.util.List;public class SSS {public …

织密安全防线——记建行江门市分行推进反洗钱工作

建行广东省江门市分行多层次织密反洗钱防线&#xff0c;持续护航高质量发展。 健全架构 建行江门分行成立以“一把手”为组长的反洗钱工作领导小组。通过在部门、支行、网点层面分别设置反洗钱合规官、合规专员、情报专员、合规员等岗位&#xff0c;层层织密反洗钱防线。持续加…

【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )

文章目录 一、构造函数意义1、类的构造函数2、构造函数显式调用与隐式调用3、构造函数替代方案 - 初始化函数4、初始化函数缺陷5、默认构造函数6、代码示例 - 初始化函数无法及时调用 一、构造函数意义 1、类的构造函数 C 提供的 构造函数 和 析构函数 作为 类实例对象的 初始化…

【性能测试】Jmeter插件之ServerAgent服务器性能监控工具的安装和使用

文章目录 安装插件安装ServerAgent 安装插件 1、在Jmeter官网&#xff1a;https://jmeter-plugins.org/wiki/PluginsManager/ 下载插件管理器Plugins-manager.jar 2、将JAR包放入到lib\ext目录下 3、重启Jmeter&#xff0c;可以在选项下看到Plugins Manager选项 4、安装…

解决golang无法下载依赖的奇葩问题

最近刚开始学习go&#xff0c;看了几个开源项目&#xff0c;遂下载好朋友的go开源项目&#xff0c;想着coding first&#xff0c;我得先跑起来&#xff0c;结果居然卡在依赖上。就像下图&#xff1a; 真的是头疼&#xff0c;反复执行 go mod tidy&#xff0c;发现本地库中也能下…

vue三个点…运算符时报错 Syntax Error: Unexpected token

出现以下问题报错&#xff1a; 解决&#xff1a; 在项目根目录新建一个名为.babelrc的文件 {"presets": ["stage-2"] }