FAL 简介 FAL (Flash Abstraction Layer) Flash 抽象层,是 RT-Thread 的一个软件包,是对 Flash 及基于 Flash的分区进行管理、操作的抽象层,对上层统一了 Flash 及分区操作的 API ,并具有以下特性: • 支持静态可配置的分区表,并可关联多个 Flash 设备; • 分区表支持 自动装载。避免在多固件项目,分区表被多次定义的问题; • 代码精简,对操作系统 无依赖,可运行于裸机平台,比如对资源有一定要求的 bootloader; • 统一的操作接口。保证了文件系统、OTA、NVM 等对 Flash 有一定依赖的组件,底层 Flash 驱动的 可重用性; • 自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦)Flash 或分区, 方便开发者进行调试、测试; 简单来说,FAL把flash分成了多个单独的分区,FAL就像虚拟内存一样,每个分区也都是从0地址开始的. 由于FLASH和E2PROM不一样,不能随意写入,只能把bit置0,通过擦除置1,但是擦除只支持块级以上擦除,即不能一个字节一个字节的擦除,一般spi flash都是4k擦除,有的STM32chip flash是2k擦除.由于这个特性,导致了我们如果想要对已经写入过数据的地址重新修改时,必须要将所在的块擦除,这样一来,其他的数据则会被一同擦除,也就是fal_partition_write实际上是block write,块写入.由于可能会遇到单字节或多字节写入情况,这就是本文的目的,能随意字节的可覆盖读写flash. 在RT-Thread上使用FAL 由于FAL 是对Flash的分区进行管理、操作的抽象层,所以还需要底层的FLASH驱动,目前的RTT已经支持了大部分FLASH,所以基本上不需要我们写驱动了.RTT也支持了SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库.作者文档介绍的也比较详细,这里就不再赘诉. 添加SPI 总线驱动
如果你的BSP下board里面的Kconfig文件没有配置SPI,从其他BSP下复制一下配置即可.然后使用RTT env工具,进入Hardware Drivers Config -->On-Chip Peripheral Drivers,按空格键选择使能Enable SPI Bus,再进入,使能SPI1即可.
由于选择RTT使用STM32的HAL库,所以我们还需要stm32f1xx_hal_msp.c中添加spi的初始化函数.可以使用STM32CubeMX来获取配置.这里就不介绍了(我也不太熟悉-_-||). void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hspi->Instance==SPI1) { /* USER CODE BEGIN SPI1_MspInit 0 */ /* USER CODE END SPI1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**SPI1 GPIO Configuration PA4 ------> SPI1_NSS PA5 ------> SPI1_SCK PA6 ------> SPI1_MISO PA7 ------> SPI1_MOSI */ GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN SPI1_MspInit 1 */ /* USER CODE END SPI1_MspInit 1 */ } } /** *@BriefSPI MSP De-Initialization * This function freeze the hardware resources used in this example *@paramhspi: SPI handle pointer * @retval None */ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) { if(hspi->Instance==SPI1) { /* USER CODE BEGIN SPI1_MspDeInit 0 */ /* USER CODE END SPI1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_SPI1_CLK_DISABLE(); /**SPI1 GPIO Configuration PA4 ------> SPI1_NSS PA5 ------> SPI1_SCK PA6 ------> SPI1_MISO PA7 ------> SPI1_MOSI */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7); /* USER CODE BEGIN SPI1_MspDeInit 1 */ /* USER CODE END SPI1_MspDeInit 1 */ } 在stm32f1xx_hal_conf.h中打开宏HAL_GPIO_MODULE_ENABLED. 添加SFUD驱动 在你的BSP下使用RTT ENV工具打开,使用menuconfig进行配置.
将Using SPI Bus打开,并选择Using Serial Flash Universal Driver. (如果不知道选项路径,输入 / 后,输入关键词,Enter进行查找),完毕后一直按ESC键,直到提示是否保持配置,选择Yes. 添加SPI DEVICE 上一步仅仅只是添加SPI BUS,添加SPI设备也很简单,只需要调用rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint16_t cs_gpio_pin)函数即可. rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4); 我的spi flash的片选IO为PA4. 使用SFUD添加SPI 块设备 调用 rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name, const char *spi_dev_name)即可添加. rt_sfud_flash_probe("norflash0", "spi10"); 相关spi flash型号和型号信息等都在sfud_flash_def.h中,如果没有你的spi flash,模仿其他flash添加在SFUD_MF_TABLE,SFUD_FLASH_CHIP_TABLE,SFUD_FLASH_EXT_INFO_TABLE这几个表里即可.
添加FAL 添加好SPI FLASH设备块,就可以添加FAL了,使用RTT env选择使用FAL(RT-Thread online packages —>system packages —> fal,并选择FAL uses SFUD drivers.保存退出.
使用pkgs --update指令下载fal包.
在这里复制fal_cfg.h到applications目录下.根据自己的情况配置分区.
接着使用scons构建项目
调用fal_init()初始化FAL.
添加fal_nbyte 貌似这个命名不太合适,fal的接口本来就是多字节读写的… 由于开篇提到的flash写入擦除问题,导致不能对已经写入过数据(0)的地址进行更改.所以我们需要在擦除的时候,把其他数据先读出来,再一同写入. 代码比较简单,就不注释了. static rt_mutex_t fal_nbyte_mtx; int fal_partition_write_nbyte(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size) { size_t i, remain, offset, _size; const struct fal_flash_dev * fal_dev = RT_NULL; uint8_t *fal_cache; fal_dev = fal_flash_device_find(part->flash_name); RT_ASSERT(fal_dev != RT_NULL); RT_ASSERT(FLASH_CACHE >= fal_dev->blk_size); RT_ASSERT(fal_dev != RT_NULL); rt_mutex_take(fal_nbyte_mtx, RT_WAITING_FOREVER); offset = addr%fal_dev->blk_size; remain = fal_dev->blk_size - offset; fal_cache = (uint8_t *)rt_malloc(fal_dev->blk_size); if (NULL == fal_cache) goto ERR; if (remain > size) remain = size; _size = size; while (1) { if (fal_partition_read(part, addr - offset, fal_cache, fal_dev->blk_size) != fal_dev->blk_size) { rt_kprintf("fal_partition_read err! "); goto ERR1; } for (i = 0; i < remain; i++) { if (fal_cache[offset+i] != 0xff) break; } if (i != remain) fal_partition_erase(part, addr - offset, remain); rt_memcpy(&fal_cache[offset], buf, remain); if (fal_partition_write(part, addr - offset, fal_cache, fal_dev->blk_size) != fal_dev->blk_size) { rt_kprintf("fal_partition_write err! "); goto ERR1; } if (remain == size) { break; } else { buf += remain; size -= remain; addr += remain; offset = 0; if (size > fal_dev->blk_size) remain = fal_dev->blk_size; else remain = size; } } rt_free(fal_cache); rt_mutex_release(fal_nbyte_mtx); return _size; ERR1: rt_free(fal_cache); ERR: rt_mutex_release(fal_nbyte_mtx); return -1; } int fal_partition_read_nbyte(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size) { size_t remain, offset, index, _size; const struct fal_flash_dev * fal_dev = RT_NULL; uint8_t *fal_cache; fal_dev = fal_flash_device_find(part->flash_name); RT_ASSERT(fal_dev != RT_NULL); RT_ASSERT(FLASH_CACHE >= fal_dev->blk_size); RT_ASSERT(fal_dev != RT_NULL); rt_mutex_take(fal_nbyte_mtx, RT_WAITING_FOREVER); offset = addr%fal_dev->blk_size; remain = fal_dev->blk_size - offset; fal_cache = (uint8_t *)rt_malloc(fal_dev->blk_size); if (NULL == fal_cache) goto ERR; if (remain > size) remain = size; _size = size; index = 0; while (1) { if (fal_partition_read(part, addr - offset, fal_cache, fal_dev->blk_size) != fal_dev->blk_size) { rt_kprintf("fal_partition_read err! "); goto ERR1; } rt_memcpy(buf, &fal_cache[offset], remain); if (remain == size) { break; } else { addr += remain; buf += remain; index += remain; size -= remain; offset = 0; if (size > fal_dev->blk_size) remain = fal_dev->blk_size; else remain = size; } } rt_free(fal_cache); rt_mutex_release(fal_nbyte_mtx); return _size; ERR1: rt_free(fal_cache); ERR: rt_mutex_release(fal_nbyte_mtx); return -1; } int fal_nbyte_init(void) { fal_nbyte_mtx = rt_mutex_create("fal_nbyte_mtx", RT_IPC_FLAG_FIFO); RT_ASSERT(fal_nbyte_mtx!=RT_NULL); return 0; } INIT_ENV_EXPORT(fal_nbyte_init); 验证
使用fal probe cfg匹配一下刚才cfg分区. fal read读取数据
可以看到,当数据为FF时,地址0写入1,读出来的数据正确,再次写入数据2,读出来的错了. 接着验证一下fal_nbyte读写.
读写没问题,再测试覆盖写.
原作者:上发条
|