本帖最后由 Ai-Thinker小泽 于 2023-11-6 20:39 编辑
本帖最后由 Ai-Thinker小泽 于 2023-11-6 18:22 编辑
零基础开发小安派-Eyes-S1【外设篇】——FLASH
Flash简称闪存,它的特点是擦除和编程速度快,单片机的程序一般都是存储在一定空间内中进行运行。在C语言中,程序分为代码段,常量区,静态数据区(BSS段,数据段),栈,堆五个部分组成。内部空间就分为ROM和RAM,他们都是可以用来存储内容,区别在于,ROM的存储特性比较稳定,很久以前它不可擦除,所以只可读不可写,FLASH就是一种特殊的ROM,可擦除可读可写,它们在掉电后不丢失。RAM的存储特性较不稳定,但它的读写速度快,掉电后丢失。
他们的差异如下:
数据存储区域 |
类型 |
所在存储空间 |
代码段 |
宏定义、枚举、字面常量(整数、浮点类型) |
ROM(也可在RAM) |
常量区 |
只读变量、const修饰的全局变量、常量字符串(char *p=“hi”) |
ROM |
静态数据区BSS段 |
全局变量和static修饰后的静态变量(未初始化,如int a) |
RAM |
静态数据区数据段 |
全局变量和static修饰后的静态变量(已初始化,如int a=0) |
ROM,运行时拷贝到RAM中 |
栈 |
局部变量,如函数内临时定义的int b,在函数运行完自动释放 |
RAM |
堆 |
malloc申请的空间,需要用户手动申请/释放 |
RAM |
总结:FLASH也就是ROM,掉电后不丢失,读写速度较慢,空间大,可以理解未电脑上的硬盘。RAM速度快,掉电后丢失,可以理解未电脑上的内存。
一、了解小安派-Eyes-S1的存储
618FLASH物理上的地址起始地址是0XA0000000。M61-32S的FLASH大小是8M。在数据手册中可以看到所有的模块地址映射,包括外设的地址。
在这之前需要了解分区表的概念,分区表就是划分FLASH的清单,将FLASH划分为多个不同功能区域用于其它功能。在操作FLASH时得看一下分区表,查看哪些地址可以使用。在SDK中有4M的分区表,里面规划了一些分区的命名,起始地址和大小,这个目录下找到。AiPi-Open-Kits/aithinker_Ai-M6X_SDK/bsp/board/bl616dk/config/partition_cfg_4M.toml,在参与编译时会在工程文件夹下的build/build_out中生成partition.bin。我们在flash_prog_cfg.ini中可以看到,常见的有BOOT2固件,filedir:固件路径,address:地址,boot2必须使用0地址。而partition表示分区表固件,address是根据partition_cfg_4M.toml指定。而FW则表示需要烧录的应用固件,FW可以在partition_cfg_4M.toml中看到。
我们可以添加一样的“分区”,创建属于自己的名字,也可以在分区表中查看已有的分区使用。当然,建议还是使用原厂分配的分区表。
1.bflb_flash_init
说明: flash 初始化,自动识别支持的 flash 并重新配置 flash 的参数。如果返回错误,必须停止运行代码。
int bflb_flash_init(void);
parameter |
description |
return |
返回 0 表示成功,其他表示错误,必须停止运行代码 |
2.bflb_flash_get_jedec_id
说明: 获取 flash jedec id。
uint32_t bflb_flash_get_jedec_id(void);
parameter |
description |
return |
返回 flash jedec id |
3.bflb_flash_get_cfg
说明: 获取 flash 配置。
void bflb_flash_get_cfg(uint8_t **cfg_addr, uint32_t *len);
parameter |
description |
cfg_addr |
保存 flash 配置的地址 |
len |
flash 配置的长度 |
4.bflb_flash_set_iomode
说明: 设置 flash IO 工作模式。
void bflb_flash_set_iomode(uint8_t iomode);
parameter |
description |
iomode |
flash IO 工作模式 |
可填入以下几种模式:
#define FLASH_IOMODE_NIO 0
#define FLASH_IOMODE_DO 1
#define FLASH_IOMODE_QO 2
#define FLASH_IOMODE_DIO 3
#define FLASH_IOMODE_QIO 4
5.bflb_flash_get_image_offset
说明: 获取代码 xip 虚拟地址实际所在的 flash 物理地址。
uint32_t bflb_flash_get_image_offset(void);
parameter |
description |
return |
返回 flash xip 物理地址 |
6.bflb_flash_erase
说明: flash 扇区擦除。 len 为擦除的长度,需要为 4096 倍数,假设 addr 为0 , len 为 4096,则擦除范围为 0 ~ 4095。
int bflb_flash_erase(uint32_t addr, uint32_t len);
parameter |
description |
addr |
擦除的物理地址 |
len |
擦除长度,需要是 4096 的倍数 |
return |
返回 0 表示成功,其他表示错误 |
7.bflb_flash_write
说明:获取代码 xip 虚拟地址实际所在的 flash 物理地址。
int bflb_flash_write(uint32_t addr, uint8_t *data, uint32_t len);
parameter |
description |
addr |
写入的物理地址 |
data |
写入的数据缓冲区 |
len |
写入长度 |
return |
返回 0 表示成功,其他表示错误 |
8.bflb_flash_read
说明: 获取代码 xip 虚拟地址实际所在的 flash 物理地址。
int bflb_flash_read(uint32_t addr, uint8_t *data, uint32_t len);
parameter |
description |
addr |
读取的物理地址 |
data |
读取的数据缓冲区 |
len |
读取长度 |
return |
返回 0 表示成功,其他表示错误 |
9.bflb_flash_aes_init
说明: 配置某一段 flash 区域进行硬件 aes 解密,能够通过 xip 直接读取解密后的内容。
void bflb_flash_aes_init(struct bflb_flash_aes_config_s *config);
parameter |
description |
config |
flash aes配置 |
10.bflb_flash_aes_enable
说明: 使能 flash aes 解密。
void bflb_flash_aes_enable(void);
11.bflb_flash_aes_disable
说明: 关闭 flash aes 解密。
void bflb_flash_aes_disable(void);
二、简单示例——上电后读取flash内的值并打印
1.首次写入
#include "bflb_flash.h"
#include "board.h"
#include "string.h"
//头文件
#define FLASH_RW_START_ADDR 0x3F3000
//宏定义读写起始地址,查看分区表可以看出这是“DATA”所在分区
static uint8_t write_buf[16];
static uint8_t read_buf[16];
//定义读写缓存数组
int main(void)
{
uint32_t i;
board_init();
//板子初始化,包括了bflb_flash_init
memset(write_buf, 0, sizeof(write_buf));
memset(read_buf, 0, sizeof(read_buf));
//清空缓存数组
strcpy(write_buf,"HelloWorld!");
//填写write数组内容
/* erase flash */
bflb_flash_erase(FLASH_RW_START_ADDR,sizeof(write_buf));
//擦除缓存数组大小,从起始地址开始
/* read flash data */
bflb_flash_read(FLASH_RW_START_ADDR, read_buf, sizeof(read_buf));
//读取flash内的内容缓存到read数组里
for (i = 0; i < sizeof(read_buf); i++) {
if (read_buf[i] != write_buf[i]) {
printf("\r\nflash read fail at %d, expect:%d but get %d", i, write_buf[i], read_buf[i]);
}
}
//for循环校验,当write和read内容不一致时会打印
/* write flash data */
bflb_flash_write(FLASH_RW_START_ADDR, write_buf, sizeof(write_buf));
//将write内容写入flash中
memset(read_buf, 0, sizeof(read_buf));
/* read flash data */
bflb_flash_read(FLASH_RW_START_ADDR, read_buf, sizeof(read_buf));
printf("\r\n%s",read_buf);
while (1) {
}
}
第一次运行程序效果:在擦除了后读出来的ASCII码为255
2.二次读取,查看上电打印信息
#include "bflb_flash.h"
#include "board.h"
#include "string.h"
#define FLASH_RW_START_ADDR 0x3F3000
static uint8_t write_buf[16];
static uint8_t read_buf[16];
int main(void)
{
uint32_t i;
board_init();
memset(write_buf, 0, sizeof(write_buf));
memset(read_buf, 0, sizeof(read_buf));
strcpy(write_buf,"HelloWorld!");
/* read flash data */
bflb_flash_read(FLASH_RW_START_ADDR, read_buf, sizeof(read_buf));
for (i = 0; i < sizeof(read_buf); i++) {
if (read_buf[i] != write_buf[i]) {
printf("\r\nflash read fail at %d, expect:%d but get %d", i, write_buf[i], read_buf[i]);
}
}
printf("\r\n%s",read_buf);
while (1) {
}
}
二次读取后的运行效果:校验成功,只读取出hellowold
三、使用easyflash库,匹配字段
在AiPi-SCP_SmartCtrl工程中的components下,有easy_flash库,使用easy_flash更加方便快捷,只需要定义字段,第一个参数填入字段,第二个参数填入值,即可通过匹配字段的方式将值与字段匹配上。
添加库的方式如下:
1.将easy_flash的文件夹复制下来,放到模板工程中,这里使用Project_basic的模板工程,记得修改工程名。
2.在CMakeLists.txt下的添加easyflash的库文件。
3.在proj.conf下打开easyflash控件。
4.使用方法,只需要在调用flash_erase_set时填入两个参数,第一个参数为匹配的字段,一般由宏定义的字符串常量。第二个参数为值,也就是需要存储的值。这样在flash中就是由常驻的字段+值组成,在获取值时可以通过“字段‘来寻找相应的值。通过flash_get_data的方式,传入查找的字段和长度,得到存储的值。
5.easyflash使用的“PSM”分区,大小是32K,起始地址是0X3E9000。在这里面我们可以存放一些标志位,或者是类似wifi名称密码、AP名称密码等关键信息,使用起来也方便,通过匹配字段的方式将关键信息填入所需任务。如在AT指令中将SSID和PASS通过第三个参数0或1选择是否存入flash中。方便实现开机自动连接wifi且可通过指令修改wifi等操作。
[/i][/i][/i][/i][/i][/i][/i][/i]