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擦除,有的STM32 chip 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 */
}
}
/**
* @Brief SPI MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hspi: 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读写.
读写没问题,再测试覆盖写.
原作者:上发条
|