-
Notifications
You must be signed in to change notification settings - Fork 5.4k
[fal/sample] readme 添加 #10175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[fal/sample] readme 添加 #10175
Changes from 3 commits
a7ee837
f501117
88d2cc4
df12f66
616119c
30d3fc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -106,3 +106,222 @@ Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区, | |||||||||
| - 分区的起始地址和大小 **不能超过 Flash 设备的地址范围** ,否则会导致包初始化错误 | ||||||||||
|
|
||||||||||
| > 注意:每个分区定义时,除了填写上面介绍的参数属性外,需在前面增加 `FAL_PART_MAGIC_WORD` 属性,末尾增加 `0` (目前用于保留功能) | ||||||||||
| ## 3、如何实现读写擦除等操作 | ||||||||||
| 我们以fal_norflash_port.c为例,简单介绍一下。 | ||||||||||
| 首先 介绍一下这两个宏定义 | ||||||||||
| ```C | ||||||||||
| #define FAL_ALIGN_UP( size, align ) \ | ||||||||||
| ( ( ( size ) + ( align ) - 1 ) - ( ( ( size ) + ( align ) - 1 ) % ( align ) ) ) | ||||||||||
| #define FAL_ALIGN_DOWN( size, align ) ( ( ( size ) / ( align ) ) * ( align ) ) | ||||||||||
| ``` | ||||||||||
| ALIGN_UP(16,4)=16 ALIGN_UP(15,4)=16 ALIGN_UP(17,4)=20 | ||||||||||
| ALIGN_DOWN(16,4)=16 ALIGN_DOWN(15,4)=12 ALIGN_DOWN(17,4)=16 | ||||||||||
| 不难看出 ALIGN_UP是一个size向上取整到align的倍数,ALIGN_DOWN则是向下取整到align的倍数。 | ||||||||||
| 然后 介绍FLASH的特性 | ||||||||||
| FLASH都是按块擦除 norflash的块大小一般为4K 单片机内部FLASH的块大小为1K,2K,16K不等 | ||||||||||
| 同时有最少写入数据的限制 | ||||||||||
| norflash中 是按页写入 一次最少写256个字节数据 超过则覆盖起始数据 如第257个数据会覆盖第1个数据的位置 | ||||||||||
| 单片机内部flash中 一次最少写2个字节数据(STM32F105RC) 且只能将地址2字节对齐写入 只写一个字节时 给后面的字节补成FF | ||||||||||
| 实现擦除 | ||||||||||
| ```C | ||||||||||
| static int32_t get_sector( uint32_t address );//获取当前属于第一个扇区 | ||||||||||
| extern void norflash_erase_sector( uint32_t saddr );//负责擦除单个扇区的全部数据 | ||||||||||
| //FLASH都是按块擦除 我们假定在调用擦除函数时 用户知道自己将会擦除扇区内的全部数据 | ||||||||||
| static int erase( long offset, size_t size ) | ||||||||||
| { | ||||||||||
| int32_t cur_erase_sector; | ||||||||||
| uint32_t addr = FLASH_START_ADDR + offset; | ||||||||||
| uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE ); | ||||||||||
|
|
||||||||||
| uint32_t addr_end = addr + size; | ||||||||||
| uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE ); | ||||||||||
| uint32_t cur_addr = addr_down; | ||||||||||
|
|
||||||||||
| while ( cur_addr < addr_end_up ) { | ||||||||||
| cur_erase_sector = get_sector( cur_addr ); | ||||||||||
| if ( cur_erase_sector == -1 ) {//获取第几个扇区失败 说明地址超出范围 | ||||||||||
| return cur_addr - addr; | ||||||||||
| } | ||||||||||
| norflash_erase_sector( cur_erase_sector ); | ||||||||||
| cur_addr += FLASH_SECTOR_SIZE;//这里如果每个扇区的大小不同 需要实现从当前地址获取扇区实际大小的函数 | ||||||||||
| } | ||||||||||
| return size; | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
| 实现读取 | ||||||||||
| ```c | ||||||||||
| //这个比较简单 直接调用norflash_read即可 | ||||||||||
| static int read( long offset, uint8_t* buf, size_t size ) | ||||||||||
| { | ||||||||||
| norflash_read( buf, offset + FLASH_START_ADDR, size ); | ||||||||||
| return size; | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
| 最后 也是最关键的一步 实现写入 | ||||||||||
| ```c | ||||||||||
| /* 写入任意长数据到NOR Flash函数 */ | ||||||||||
| static int write( long offset, const uint8_t* buf, size_t size ) | ||||||||||
| { | ||||||||||
| // 计算实际物理地址(相对于Flash起始地址的偏移) | ||||||||||
| uint32_t addr = FLASH_START_ADDR + offset; | ||||||||||
| // 计算起始地址的扇区向上对齐地址(例如0x1007 -> 0x2000 当扇区大小4K) | ||||||||||
| uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_SECTOR_SIZE ); | ||||||||||
| // 计算起始地址的扇区向下对齐地址(例如0x1007 -> 0x1000) | ||||||||||
| uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE ); | ||||||||||
|
|
||||||||||
| // 计算写入结束地址 | ||||||||||
| uint32_t addr_end = addr + size; | ||||||||||
| // 结束地址的扇区向上对齐地址 | ||||||||||
| uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE ); | ||||||||||
| // 结束地址的扇区向下对齐地址 | ||||||||||
| uint32_t addr_end_down = FAL_ALIGN_DOWN( addr_end, FLASH_SECTOR_SIZE ); | ||||||||||
| uint32_t cur_addr = addr_down; // 当前处理的扇区起始地址 | ||||||||||
|
|
||||||||||
| uint32_t max_write_len = 0; // 单次最大可写入长度 | ||||||||||
| uint32_t write_len = 0; // 实际写入长度 | ||||||||||
|
|
||||||||||
| // 地址有效性检查:结束地址超过Flash范围 或 起始地址在Flash区域外 | ||||||||||
| if ( addr_end_up > FLASH_END_ADDR || ( int )addr_end_down < FLASH_START_ADDR ) return -1; | ||||||||||
|
|
||||||||||
| // 分配扇区大小的缓冲区(用于处理部分写入时需要保存原始数据的情况) | ||||||||||
| uint8_t* read_sector_buf = FAL_MALLOC( FLASH_SECTOR_SIZE ); | ||||||||||
| if ( read_sector_buf == RT_NULL ) { | ||||||||||
| return -2; // 内存分配失败 | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // 按扇区逐个处理(从起始扇区到结束扇区) | ||||||||||
| while ( cur_addr < addr_end_up ) { | ||||||||||
| /* 情况1:处理起始地址不在扇区边界的情况(首扇区部分写入) */ | ||||||||||
| if ( cur_addr < addr ) { | ||||||||||
| // 读取整个扇区原始数据到缓冲区 | ||||||||||
| read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE ); | ||||||||||
|
|
||||||||||
| // 计算首扇区可写入的最大长度(从起始地址到扇区末尾) | ||||||||||
| max_write_len = ( addr_up - addr ); | ||||||||||
| // 确定实际写入长度(不超过剩余数据大小) | ||||||||||
| write_len = size >= max_write_len ? max_write_len : size; | ||||||||||
|
|
||||||||||
| // 判断是否需要擦除(检查目标区域是否包含需要从0->1的位) | ||||||||||
| if ( judge_whether_erase( read_sector_buf + addr - cur_addr, write_len ) ){ | ||||||||||
| // 需要擦除时:执行擦除->修改缓冲区->写入整个扇区 | ||||||||||
| norflash_erase_sector( get_sector( cur_addr ) ); | ||||||||||
| // 将新数据合并到缓冲区对应位置 | ||||||||||
| FAL_MEMCPY( read_sector_buf + ( addr - cur_addr ), buf, write_len ); | ||||||||||
| // 写入整个扇区 | ||||||||||
| write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE ); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| // 无需擦除时直接写入数据(NOR Flash允许直接写入0位) | ||||||||||
| write_sector( addr, buf, write_len ); | ||||||||||
| } | ||||||||||
| buf += write_len; // 移动数据指针 | ||||||||||
| } | ||||||||||
| /* 情况2:处理结束地址不在扇区边界的情况(末扇区部分写入) */ | ||||||||||
| else if ( cur_addr == addr_end_down ) { | ||||||||||
| // 读取整个扇区原始数据 | ||||||||||
| read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE ); | ||||||||||
|
|
||||||||||
| // 计算最大可写入长度(整个扇区) | ||||||||||
| max_write_len = FLASH_SECTOR_SIZE; | ||||||||||
| // 计算实际需要写入的长度(从扇区起始到结束地址) | ||||||||||
| write_len = addr_end - cur_addr; | ||||||||||
| write_len = write_len >= max_write_len ? max_write_len : write_len; | ||||||||||
|
|
||||||||||
| // 判断是否需要擦除 | ||||||||||
| if ( judge_whether_erase( read_sector_buf, write_len ) ) { | ||||||||||
| // 需要擦除时:合并数据->擦除->写入整个扇区 | ||||||||||
| FAL_MEMCPY( read_sector_buf, buf, write_len ); | ||||||||||
| norflash_erase_sector( get_sector( cur_addr ) ); | ||||||||||
| write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE ); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| // 直接写入数据 | ||||||||||
| write_sector( cur_addr, buf, write_len ); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| /* 情况3:完整扇区写入(中间扇区) */ | ||||||||||
| else { | ||||||||||
| // 直接擦除整个扇区(完整覆盖不需要保留数据) | ||||||||||
| norflash_erase_sector( get_sector( cur_addr ) ); | ||||||||||
| // 写入整个扇区数据 | ||||||||||
| write_sector( cur_addr, buf, FLASH_SECTOR_SIZE ); | ||||||||||
| buf += FLASH_SECTOR_SIZE; // 移动数据指针 | ||||||||||
| } | ||||||||||
| cur_addr += FLASH_SECTOR_SIZE; // 移动到下一个扇区 | ||||||||||
| } | ||||||||||
| FAL_FREE( read_sector_buf ); // 释放缓冲区内存 | ||||||||||
| return size; // 返回成功写入的字节数 | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
| 关键逻辑说明: | ||||||||||
| 地址对齐处理:通过向上/向下对齐计算确定实际需要操作的扇区范围 | ||||||||||
| 三种写入场景: | ||||||||||
| 首扇区部分写入:需要读取原始数据,合并新数据后判断擦除必要性 | ||||||||||
| 中间完整扇区:直接擦除后全量写入,提高效率 | ||||||||||
| 末扇区部分写入:处理方式类似首扇区,但数据位置不同 | ||||||||||
| 擦除判断:通过judge_whether_erase函数检测是否需要执行擦除操作(基于NOR Flash的特性,只有需要将0变为1时才必须擦除) | ||||||||||
| 数据合并:使用临时缓冲区保存原始数据,仅修改需要写入的部分,最大限度减少擦除操作 | ||||||||||
| 内存管理:动态分配扇区大小的缓冲区,处理完成后立即释放 | ||||||||||
| 到这里 工作似乎做完了 但是 我们没有写入扇区的函数 只有页写入函数 norflash_write_page | ||||||||||
| 扇区写入逻辑和任意写入逻辑基本相同 | ||||||||||
| 下面实现扇区写入函数 | ||||||||||
| ```c | ||||||||||
| /* 扇区写入函数:处理按页对齐的NOR Flash写入操作 */ | ||||||||||
| static int write_sector( long offset, const uint8_t* buf, size_t size ) | ||||||||||
| { | ||||||||||
| // 计算实际物理地址(FLASH起始地址 + 偏移量) | ||||||||||
| uint32_t addr = FLASH_START_ADDR + offset; | ||||||||||
|
|
||||||||||
| // 计算地址的页对齐上边界和下边界(按FLASH_PAGE_SIZE对齐) | ||||||||||
| uint32_t addr_up = FAL_ALIGN_UP( addr, FALSH_PAGE_SIZE ); | ||||||||||
| uint32_t addr_down = FAL_ALIGN_DOWN( addr, FALSH_PAGE_SIZE ); | ||||||||||
|
|
||||||||||
|
||||||||||
| uint32_t addr_up = FAL_ALIGN_UP( addr, FALSH_PAGE_SIZE ); | |
| uint32_t addr_down = FAL_ALIGN_DOWN( addr, FALSH_PAGE_SIZE ); | |
| uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_PAGE_SIZE ); | |
| uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_PAGE_SIZE ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
此处宏定义是FLASH的单页大小
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FALSH_PAGE_SIZE
拼写是FALSH?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.0 我是笨蛋 这就去
Uh oh!
There was an error while loading. Please reload this page.