CW32L012 FLASH环形页存储介绍
最近做了一个智能电源保护器的项目,用到了掉电对数据要进行保存,掉电要保存数据就需要每次将数据修改过以后就要存储到flash中,使用到的芯片是CW32L012C8T这款芯片,这款芯片的参数是m0+内核的
arm
架构,是武汉芯源
半导体
力推的一款主打低功耗的芯片,
拥有96Mhz的主频,8K的S
RAM
以及64K的FLASH,FLASH的擦写寿命是10万次
。
MCU
自带Flash不是EEP
ROM
。
写之前必须擦,擦是按页/扇区。擦除把bit拉成1,写操作是把1变0。你想改一个字节?对不起,先把整个扇区擦掉,再把整块数据写回去。
这个过程短则几ms,长则几十ms甚至上百ms,中间断电就完蛋。
手册上写Flash擦写寿命10万次,看着多。你算一笔账:
每次上电校准一次或者修改一次数据,每天上电5次,可以修改55年,如果换成擦写100万次,那么可以使用550年
如果换成车每公里存一次里程,10万公里就磨穿了,擦写100万次,那么就要100万公里了
而且10万次是常温典型值。105°C、125°C下,耐久和数据保持时间会明显下降。别拿常温手册拍脑袋。
这是我见的很经典的flash的写入:
void Int_Flash_Write(uint16_t* data, uint32_t address, uint16_t length)
{
FLASH_UnlockPages(0xfe00, 0xfe00);
FLASH_ErasePages(0xfe00, 0xfe00);
uint16_t value[length + 1];
value[0] = 0x0430;
for (uint16_t i = 1; i < length + 1; i++)
{
value[i] = data[i - 1];
}
FLASH_WriteHalfWords(address, value, length + 1);
FLASH_LockPages(0xfe00, 0xfe00);
}
抛开别的不谈,就只论flash的写入,固定的页写入固定的数据,没有数据的校验,没有数据的备份。
写入的数据没有校验,那么我们没有成功写入数据,flash中存储的数据就是1,我们以两个个字节举例子,我们读两个字节就是0xFFFF,那读到的数据都是错误的,这样写flash数据的可靠性在哪里。
写入的数据没有备份,那如果遇到在flash写入的过程中掉电了,我们上电flash中存的数据不又全是0xFFFF了吗,写入的数据全部丢失了,安全性又在哪里。
那如果写入flash的程序是这样呐:
void Flash_Write(uint16_t page)
{
if (page < 1 || page >10)
{
return;
}
FLASH_UnlockPage(117 + page);
FLASH_ErasePage(117 + page);
uint32_t page_address;
page_address = 0xfe00U - (10U - page) * 512U;
flash_write_data.crc_value = CRC16_Calc_8bit(CRC16_CCITTFALSE, (uint8_t*)&flash_write_data, 12);
FLASH_WriteHalfWords(page_address, (uint16_t*)&flash_write_data, 7);
FLASH_LockPage(117 + page);
}
根据不同的页进行写入,
开辟10页的flash进行写入,CW32L012C8T6每页是512字节,64kfalsh一共就有128页,开辟最后10页来存储数据,不影响主程序的存储,最后10页循环写,写完第10页就返回去写第1页,CW32L012C8T6的10万次falsh寿命就变成了100万次了(在手册理论情况下)。
uint8_t Flash_FindLatestPage(void)
{
uint32_t page_address;
Flash_WriteTypedef* p_head;
uint8_t latest_page = 0;
uint16_t latest_seq = 0xffff;
for (uint8_t i = 1; i <= 10; i++)
{
page_address = 0xfe00U - (10U - i) * 512U;
p_head = (Flash_WriteTypedef*)page_address;
if (p_head- >flag != 0x0430)
{
continue;
}
uint16_t calc_crc = CRC16_Calc_8bit(CRC16_CCITTFALSE, (uint8_t*)page_address, 12);
uint16_t store_crc = p_head->crc_value;
if (calc_crc != store_crc)
{
continue;
}
uint16_t current_serquence = p_head->sequence;
if ((latest_seq == 0xffff) || ((current_serquence > latest_seq) && !(latest_seq == 0 && current_serquence == 0xffff)) || (current_serquence == 0 && latest_seq == 0xffff))
{
latest_seq = current_serquence;
latest_page = i;
}
}
return latest_page;
}
void Flash_Configuration(uint8_t(*callback_function)(void))
{
Flash_WriteTypedef* page_address;
FLASH_Init(96000000);
flash_latest_page = callback_function();
if (flash_latest_page == 0)
{
Flash_Write(1);
flash_latest_page = 1;
}
else
{
page_address = (Flash_WriteTypedef*)(0xfe00U - (10U - flash_latest_page) * 512U);
flash_write_data.calibration_v = page_address->calibration_v;
flash_write_data.crc_value = page_address->crc_value;
flash_write_data.flag = page_address->flag;
flash_write_data.protect_i.fl32 = page_address->protect_i.fl32;
flash_write_data.sequence = page_address->sequence;
}
}
每写入一次就将sequence++一次,写入的page也++一次,做好10次的循环。
每次上电就通过sequence遍历10页,找到10页中sequence最大的数就表示了写入的数据就是最新的一次,第二大的sequence计数就是最新数据的上一次,也就是可以用来作为备份的一页,这样数据安全就得到了保证。同样,每次在写入数据时,通过CRC校验将校验值写在了存储数据的最后,每次读取数据时将读取到的数据做一次CRC校验,和最后存储的校验值做一次比较,校验值相等才表示读取到了正确的数据,存储的校验值基本存储在数据尾或者数据头,校验值是不参与CRC校验过程的。
存储
存储
+关注
关注
13
文章
5175
浏览量
91583
