本次介绍的两个软件包SFUD/FAL都与FLASH有关,并且都可以独立使用或者结合在一起使用,两个软件包都对操作系统无依赖,可以使用裸机移植,也很方便移植到各种系统。 SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险 项目地址:https://github.com/armink/SFUD 对于使用rtthread完整版来说,作者已经把SFUD制作成了rtthread的内置组件了,对于使用者只需要勾选就可以了: 如果使用的是QSPI通信方式,还需要实现快速读取数据的接口: 本次演示使用的是SPI,所以没有定义SFUD_USING_QSPI这个宏。 ③ 其他接口移植: 先说明下本库主要使用的一个结构体 sfud_flash 。SFUD中最重要的就是Flash设备对象,一切操作都是对这个Flash设备对象进行的,每个Flash设备对象独立,所以SFUD也支持系统中存在多个Flash设备对象。 Flash设备对象管理着Flash存储器的所有信息,原型在sfud_def.h中,定义如下: SFUD的核心功能配置文件在sfud_cfg.h,修改说明如下: ② 擦除 Flash 数据: ③ 往Flash写数据: FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API ,FAL 框架图如下: 项目地址:https://github.com/RT-Thread-packages/fal 同样在移植过程中一定要参考两个资料:项目的readme文档和samples的移植说明。 对于使用rtthread完整版来说,对于使用者只需要勾选就可以了: 在定义 Flash 设备表前,需要先定义 Flash 设备。可以是片内 flash, 也可以是片外基于 SFUD 的 spi flash: Flash 设备表定义在 设备表示例: Flash 设备表中,有两个 Flash 对象,一个为 STM32F2 的片内 Flash ,一个为片外的 Nor Flash。 分区表也定义在 分区表示例: 用户需要修改的分区参数包括:分区名称、关联的 Flash 设备名、偏移地址(相对 Flash 设备内部)、大小,需要注意以下几点: 至此,FAL移植完成,接下来介绍如何使用API接口! API 说明: FAL 初始化时会自动装载默认分区表。使用该设置将临时修改分区表,重启后会 丢失 该设置 该函数可以根据指定的分区名称,创建对应的块设备,以便于在指定的分区上挂载文件系统 该函数可以根据指定的分区名称,创建对应的 MTD Nor Flash 设备,以便于在指定的分区上挂载文件系统 该函数可以根据指定的分区名称,创建对应的字符设备,以便于通过 deivice 接口或 devfs 接口操作分区,开启了 POSIX 后,还可以通过 oepn/read/write 函数操作分区。 通过调用fal_init()对fal初始化后,可以使用rtthread的fal命令行进行性能测试:
这两个软件包的作者都是armink,armink的开源仓库地址:https://github.com/armink,更多好玩的软件,请到作者仓库查询。
以下将结合rtthread系统,分别对这两个软件包做下演示。1.SFUD
1.1 主要特点:
1.2 资源占用:
1.3 设计原理:
1.4 如何移植:
在移植过程中一定要参考两个资料:项目的readme文档和demo工程。
勾选后,就已经移植完成了,有点太简单了!
对于rtthread完整版的来说移植太简单了,不利于切换到其他平台,所以本次移植教程以rtthread nano为例,裸机移植可以参考作者的demo工程
① 实现底层SPI/QSPI读写接口:/** * SPI write data then read data */ static sfud_err spi_write_read(const sfud_spi *spi, const 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; /** * add your spi write and read code */ RT_ASSERT(spi); HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_RESET); if(write_size && read_size) { if(HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf, write_size, 1000)!=HAL_OK) { result = SFUD_ERR_WRITE; } /* For simplicity reasons, this example is just waiting till the end of the transfer, but application may perform other tasks while transfer operation is ongoing. */ while (HAL_SPI_GetState(spi_dev->spix) != HAL_SPI_STATE_READY); if(HAL_SPI_Receive(spi_dev->spix, (uint8_t *)read_buf, read_size, 1000)!=HAL_OK) { result = SFUD_ERR_READ; } }else if(write_size) { if(HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf, write_size, 1000)!=HAL_OK) { result = SFUD_ERR_WRITE; } }else { if(HAL_SPI_Receive(spi_dev->spix, (uint8_t *)read_buf, read_size, 1000)!=HAL_OK) { result = SFUD_ERR_READ; } } /* For simplicity reasons, this example is just waiting till the end of the transfer, but application may perform other tasks while transfer operation is ongoing. */ while (HAL_SPI_GetState(spi_dev->spix) != HAL_SPI_STATE_READY); HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_SET); return result; }
#ifdef SFUD_USING_QSPI /** * read flash data by QSPI */ static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format, uint8_t *read_buf, size_t read_size) { sfud_err result = SFUD_SUCCESS; /** * add your qspi read flash data code */ RT_ASSERT(spi); RT_ASSERT(sfud_dev); RT_ASSERT(rtt_dev); return result; } #endif /* SFUD_USING_QSPI */
② SPI设备对象初始化接口:static spi_user_data user_spi = { .spix = &hspi2, .cs_gpiox = BSP_DATAFALSH_CS_GPIOX, .cs_gpio_pin = BSP_DATAFALSH_CS_GPIO_PIN }; sfud_err sfud_spi_port_init(sfud_flash *flash) { sfud_err result = SFUD_SUCCESS; /** * add your port spi bus and device object initialize code like this: * 1. rcc initialize * 2. gpio initialize * 3. spi device initialize * 4. flash->spi and flash->retry item initialize * flash->spi.wr = spi_write_read; //Required * flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable * flash->spi.lock = spi_lock; * flash->spi.unlock = spi_unlock; * flash->spi.user_data = &spix; * flash->retry.delay = null; * flash->retry.times = 10000; //Required */ rt_mutex_init(&lock, "sfud_lock", RT_IPC_FLAG_FIFO); MX_SPI_Init(); #if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32G0) SET_BIT(hspi2.Instance->CR2, SPI_RXFIFO_THRESHOLD_HF); #endif switch (flash->index) { case SFUD_W25QXX_DEVICE_INDEX: { /* 同步 Flash 移植所需的接口及数据 */ flash->spi.wr = spi_write_read; flash->spi.lock = spi_lock; flash->spi.unlock = spi_unlock; flash->spi.user_data = &user_spi; /* about 100 microsecond delay */ flash->retry.delay = retry_delay_100us; /* adout 60 seconds timeout */ flash->retry.times = 60 * 10000; break; } } return result; }
static struct rt_mutex lock; static char log_buf[256]; static void spi_lock(const sfud_spi *spi) { sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data); struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data); RT_ASSERT(spi); RT_ASSERT(sfud_dev); RT_ASSERT(rtt_dev); rt_mutex_take(&lock, RT_WAITING_FOREVER); } static void spi_unlock(const sfud_spi *spi) { sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data); struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data); RT_ASSERT(spi); RT_ASSERT(sfud_dev); RT_ASSERT(rtt_dev); rt_mutex_release(&lock); } static void retry_delay_100us(void) { /* 100 microsecond delay */ rt_thread_delay((RT_TICK_PER_SECOND * 1 + 9999) / 10000); } void sfud_log_debug(const char *file, const long line, const char *format, ...) { va_list args; /* args point to the first variable parameter */ va_start(args, format); rt_kprintf("[SFUD](%s:%ld) ", file, line); /* must use vprintf to print */ rt_vsnprintf(log_buf, sizeof(log_buf), format, args); rt_kprintf("%sn", log_buf); va_end(args); } /** * This function is print routine info. * * @param format output format * @param ... args */ void sfud_log_info(const char *format, ...) { va_list args; /* args point to the first variable parameter */ va_start(args, format); rt_kprintf("[SFUD]"); /* must use vprintf to print */ rt_vsnprintf(log_buf, sizeof(log_buf), format, args); rt_kprintf("%sn", log_buf); va_end(args); }
1.5 如何使用:
typedef struct { char *name; /**< serial flash name */ size_t index; /**< index of flash device information table @see flash_table */ sfud_flash_chip chip; /**< flash chip information */ sfud_spi spi; /**< SPI device */ bool init_ok; /**< initialize OK flag */ bool addr_in_4_byte; /**< flash is in 4-Byte addressing */ struct { void (*delay)(void); /**< every retry's delay */ size_t times; /**< default times for error retry */ } retry; void *user_data; /**< some user data */ #ifdef SFUD_USING_QSPI sfud_qspi_read_cmd_format read_cmd_format; /**< fast read cmd format */ #endif #ifdef SFUD_USING_SFDP sfud_sfdp sfdp; /**< serial flash discoverable parameters by JEDEC standard */ #endif } sfud_flash, *sfud_flash_t;
1.5.1 配置SFUD:
修改完了之后,还需要去修改刚刚复制替换的sfud_port.c文件,与刚刚填写的配置信息相对应:
至此,SFUD移植、配置完成,接下来介绍如何使用API接口!1.5.2 API 说明:
将会调用 sfud_device_init ,初始化 Flash 设备表中的全部设备。如果只有一个 Flash 也可以只使用 sfud_device_init 进行单一初始化。sfud_err sfud_init(void)
sfud_err sfud_device_init(sfud_flash *flash)
参数
描述
flash
待初始化的 Flash 设备
在 SFUD 配置文件中会定义 Flash 设备表,负责存放所有将要使用的 Flash 设备对象,所以 SFUD 支持多个 Flash 设备同时驱动。本方法通过 Flash 设备位于设备表中索引值来返回 Flash 设备对象,超出设备表范围返回 NULL 。sfud_flash *sfud_get_device(size_t index)
参数
描述
index
Flash 设备位于 FLash 设备表中的索引值
① 读取Flash数据:sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data)
sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size);
sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
1.6 应用示例:
通过调用sfud_init()对sfud初始化后,可以使用rtthread的sfud命令行对flash进行性能测试:
2.FAL
从上图可以看出FAL抽象层位于SFUD框架的上层,可以将多个Flash硬件(包括片内Flash和片外Flash)统一进行管理,并向上层比如OTA层提供对底层多个Flash硬件的统一访问接口,方便上层应用对底层硬件的访问操作。我上篇文章介绍的FOTA就是在FAL层之上做的,理解了这篇文章,FOTA的底层就理解了。参考文章:STM32通用Bootloader——FOTA2.1 主要特点:
2.2 如何移植:
对于rtthread完整版的来说移植很简单,所以本次移植教程还是以rtthread nano为例,在上个移植完SFUD工程的基础上,继续移植FAL。2.2.1 下载FAL项目源码,并添加到工程目录中;
2.2.2 定义 flash 设备
fal_flash_stm32f2_port.c
。fal_flash_sfud_port.c
。
拷贝FAL项目samplesporting的fal_flash_sfud_port.c到工程中。
因为这个工程是在SFUD的基础上移植的,所以可以直接使用sfud的API接口:
STM32片内Flash驱动,RT-Thread已经在librariesHAL_Drivers drv_flash目录下提供了,可以根据芯片自行拷贝到工程:
本次演示项目使用的是STM32L431单片机,所以拷贝drv_flash_l4.c到工程中:
RT-Thread提供的内部flash驱动通过宏#define PKG_USING_FAL
向FAL提供的fal_flash_dev设备对象onchip_flash,包含了STM32L431片内Flash的参数及其访问接口函数:
在board.h中或者rtconfig.h中定义flash的参数:#define STM32_FLASH_START_ADRESS ((uint32_t)0x08000000) #define STM32_FLASH_SIZE (256 * 1024) #define STM32_FLASH_END_ADDRESS ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE)) #define STM32_SRAM1_START (0x20000000) #define STM32_SRAM1_END (STM32_SRAM1_START + 64 * 1024) // 结束地址 = 0x20000000(基址) + 64K(RAM大小)
2.2.3 定义 flash 设备表
fal_cfg.h
头文件中,定义分区表前需 新建 fal_cfg.h
文件 ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下,并将该头文件路径加入到工程。fal_cfg.h 可以参考 示例文件 fal/samples/porting/fal_cfg.h 完成。extern struct fal_flash_dev nor_flash0; extern const struct fal_flash_dev stm32_onchip_flash; /* flash device table */ #define FAL_FLASH_DEV_TABLE { &stm32_onchip_flash, &nor_flash0, }
2.2.4 定义 flash 分区表
fal_cfg.h
头文件中。Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区,这些分区的集合就是分区表。在配置分区表前,务必保证已定义好 Flash 设备 及 设备表。fal_cfg.h 可以参考 示例文件 fal/samples/porting/fal_cfg.h 完成。#define FAL_PART_TABLE { {FAL_PART_MAGIC_WROD, "app", "onchip_flash", 64*1024, 192*1024, 0}, {FAL_PART_MAGIC_WROD, "ef", FAL_USING_NOR_FLASH_DEV_NAME, 0 , 1024 * 1024, 0}, {FAL_PART_MAGIC_WROD, "download", FAL_USING_NOR_FLASH_DEV_NAME, 1024 * 1024 , 512 * 1024, 0}, {FAL_PART_MAGIC_WROD, "factory", FAL_USING_NOR_FLASH_DEV_NAME, (1024 + 512) * 1024 , 512 * 1024, 0}, }
2.3 如何使用:
查找 Flash 设备
const struct fal_flash_dev *fal_flash_device_find(const char *name)
参数
描述
name
Flash 设备名称
return
如果查找成功,将返回 Flash 设备对象,查找失败返回 NULL
查找 Flash 分区
const struct fal_partition *fal_partition_find(const char *name)
参数
描述
name
Flash 分区名称
return
如果查找成功,将返回 Flash 分区对象,查找失败返回 NULL
获取分区表
const struct fal_partition *fal_get_partition_table(size_t *len)
参数
描述
len
分区表的长度
return
分区表
临时设置分区表
void fal_set_partition_table_temp(struct fal_partition *table, size_t len)
参数
描述
table
分区表
len
分区表的长度
从分区读取数据
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
参数
描述
part
分区对象
addr
相对分区的偏移地址
buf
存放待读取数据的缓冲区
size
待读取数据的大小
return
返回实际读取的数据大小
往分区写入数据
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size)
参数
描述
part
分区对象
addr
相对分区的偏移地址
buf
存放待写入数据的缓冲区
size
待写入数据的大小
return
返回实际写入的数据大小
擦除分区数据
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)
参数
描述
part
分区对象
addr
相对分区的偏移地址
size
擦除区域的大小
return
返回实际擦除的区域大小
擦除整个分区数据
int fal_partition_erase_all(const struct fal_partition *part)
参数
描述
part
分区对象
return
返回实际擦除的区域大小
打印分区表
void fal_show_part_table(void)
创建块设备
struct rt_device *fal_blk_device_create(const char *parition_name)
参数
描述
parition_name
分区名称
return
创建成功,则返回对应的块设备,失败返回空
创建 MTD Nor Flash 设备
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
参数
描述
parition_name
分区名称
return
创建成功,则返回对应的 MTD Nor Flash 设备,失败返回空
创建字符设备
struct rt_device *fal_char_device_create(const char *parition_name)
参数
描述
parition_name
分区名称
return
创建成功,则返回对应的字符设备,失败返回空
2.4 应用示例:
FAL对分区进行读写测试:
联系作者:
欢迎关注本人公众号:
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算