书店网站模板,代理网店一件代发,国际新闻直播,阿里巴巴网站建设改图片个人上篇文章 搭建STM32F407的SPI-Flash#xff08;基于STM32CubeMX#xff09;_小刚学長的博客-CSDN博客
主要是解决STM32CubeMX这边的配置#xff0c;对code端侧是简单介绍了下
实际项目上一般都是拿片外flash存储一些东西#xff0c;比如一些比较多的配置、参数…个人上篇文章 搭建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}, \
/* 1MFLASH 地址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
#endifstatic 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_TABLEsfud_dev flash_table[0]; // sfud_dev 指向 table[0] 这个非常重要
#endifif (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_TABLEenum {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.htypedef 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}, \
} 这样应该可以了。我记得就这么多