个人网站可以做百度推广,免费表格模板网站,宁波网络推广,石家庄网红打卡地这个玩意吧#xff0c;说起来很简单#xff0c;就是几行代码的事#xff0c;但楞是折腾了我大半天时间才搞定。原因后面说#xff0c;先看代码吧#xff1a;
读操作
读操作很简单#xff0c;以32位方式读取的时候是这样的#xff1a;
data *(__IO uint32_t *)(0x080…这个玩意吧说起来很简单就是几行代码的事但楞是折腾了我大半天时间才搞定。原因后面说先看代码吧
读操作
读操作很简单以32位方式读取的时候是这样的
data *(__IO uint32_t *)(0x0800F000);需要注意的是当以32位方式读取时地址需要是4的整数倍即32位。 8位或16位方式类似操作即可
写操作
需要注意的是写操作时是以64位方式写入数据即以双字的方式写入以下代码是将一个u64的值0x12345678aabbccdd写入0x0800F000这个地址
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
HAL_FLASH_Program(0, 0x0800F000, 0x12345678aabbccdd);
HAL_FLASH_Lock();扇区擦除 EraseInitStruct.TypeErase FLASH_TYPEERASE_PAGES; //删除方式EraseInitStruct.Page start; //超始页号EraseInitStruct.NbPages len; //页的数量EraseInitStruct.Banks bank; //bank号HAL_FLASH_Unlock(); //解锁以准备进行FLASH操作HAL_FLASHEx_Erase(EraseInitStruct, err); //擦除HAL_FLASH_Lock(); //上锁以结束FLASH操作调试
写完烧录开始调试发现问题了有时能写入有时不能写入。
先找了正点原子的例程来做参考他的可以写入但似乎也是有问题没有仔细研究。而且原子的例程是操作寄存器进行读写的不直观移植性也不好个人还是喜欢用HAL库的方式来做东西。
然后又找了ST的例程来看刚好手上有一块G4的开发板于是编译报错可能是我的开发环境比较新与ST官方的编译环境不同又是一通折腾编译通过但一加载调试就卡死不动。
于是新建工程再把ST的例程移植到我的工程中编译通过可以调试还是有时能写有时不能写。又回到了起点。
不过在前面的折腾中总结了一个规律第1次写入几乎都会失败第2次有一半的机率成功但后续成功率很高几乎不会写入失败。于是改进一下增加对写入的判断如果发生错误会重复写入不超过10次如果超过10次仍然错误则写入失败。这样就可以保证写入成功了。
测试代码如下
未包含写入失败的相关代码需要的自行添加。
if(GET_KEY() 0)
{data32[0] *(__IO uint32_t *)(0x08010000i*8); //FLASH读数据以字的方式读取data32[1] *(__IO uint32_t *)(0x08010000i*84);if((data32[0] 0xffffffff) (data32[1] 0xffffffff)) //FLASH内容为空{LED1(1);HAL_FLASH_Unlock();__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);flash_count0;while(flash_count 10){if(HAL_FLASH_Program(0, 0x08010000i*8, 0xaabbccdd12340000i) HAL_OK)break;elseflash_count;}HAL_FLASH_Lock();}data32[0] *(__IO uint32_t *)(0x08010000i*8);data32[1] *(__IO uint32_t *)(0x08010000i*84);while(GET_KEY() 0);LED1(0);i;
}本段代码能正确运行的前提是待写入区域是空的即全都是0xFF才行。
封装为函数
将代码封装一下以便后续调用并增加了一些条件判断
头文件如下
//********************************************************************************************************
//*
//* 文件名LL_flash.h
//* 文件说明STM32G系列片上FLASH的相关操作
//* 作者李佳
//* 微信LAOLIDESENLIN
//* 说明本文档遵循GNU3.0开源许可证规范即代码可开源并免费使用引用、修改、衍生代码也需要开源、免费使用
//* 但不允许修改后和衍生的代码做为闭源的商业软件发布和销售
//*
//********************************************************************************************************#ifndef __LL_FLASH_H__
#define __LL_FLASH_H__#include LL_define.h/**************************************************************************************/
/* G431芯片的128KFLASH容量的页地址分布如下共有1个BANK64页每一页2kb大小 */
#define STM32_FLASH_BASE 0x08000000 /* STM32 FLASH 起始地址 */
#define STM32_FLASH_SIZE 0x20000 /* STM32 FLASH 总大小*/
#define STM32_FLASH_PAGE_SIZE 0x800 /* STM32 FLASH 页大小*/u8 LL_flash_erase_page(u16 start, u8 len, u8 bank); //删除FLASH扇区
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64); //读片上FLASH
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64); //向片上FLASH写入数据#endifC文件如下
//********************************************************************************************************
//*
//* 文件名LL_flash.c
//* 文件说明STM32G系列片上FLASH的相关操作
//* 作者李佳
//* 微信LAOLIDESENLIN
//* 说明本文档遵循GNU3.0开源许可证规范即代码可开源并免费使用引用、修改、衍生代码也需要开源、免费使用
//* 但不允许修改后和衍生的代码做为闭源的商业软件发布和销售
//*
//*
//*
//********************************************************************************************************#include LL_flash.h
#include LL_IO.h//按页删除片上FLASH数据
//start: 起始页号
//len: 待删除的页的数量
//bank: bank编号
//返回值错误类型0表示无错误
u8 LL_flash_erase_page(u16 start, u8 len, u8 bank)
{u32 err;u8 ret;FLASH_EraseInitTypeDef EraseInitStruct;u8 flash_count0;EraseInitStruct.TypeErase FLASH_TYPEERASE_PAGES; //删除方式EraseInitStruct.Page start; //超始页号EraseInitStruct.NbPages len; //页的数量EraseInitStruct.Banks bank; //bank号HAL_FLASH_Unlock(); //解锁以准备进行FLASH操作__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);for(u8 i0; i10; i) //最多重复10次如果仍然失败则返回{ret HAL_FLASHEx_Erase(EraseInitStruct, err); //擦除if (ret HAL_OK)break;}if(ret ! 0)led_err_flash(10, 100); //擦除失败后闪灯提示, 供调试阶段使用, 正式使用时可删除HAL_FLASH_Lock(); //上锁以结束FLASH操作return ret;
}//读片上FLASH
//addr: 32位的地址值,该值应当是8的整数倍因为是按照64位的方式读取数据的
//pdata: 返回的数据首地址
//len: 待读取数据的长度 长度是按u64的数量
//返回值: 错误类型0表示无错误
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64)
{u64 data;for(u32 i0; ilen_64; i){data *(__IO uint64_t *)(addri*8);pdata64[i] data;}return 0;
}//向片上FLASH写入数据每次写入8字节不够8字节的以0xFF补足
//addr: 32位的地址值,该值应当是8的整数倍因为是按照64位的方式读取数据的
//pdata: 待写入的数据首地址
//len: 待写入数据的长度长度是按u64的数量
//返回值: 错误类型0表示无错误
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64)
{u8 ret;u64 read;HAL_FLASH_Unlock(); //上锁以结束FLASH操作__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);for(u16 j0; jlen_64; j) //按u64计算的{for(u8 i0; i10; i) //不超过10次的重复操作以保证写入成功{ret HAL_FLASH_Program(0, addrj*8, *(pdata64j));if(ret HAL_OK){read *(__IO uint64_t *)(addrj*8); //在MCU认为写入正确以后再次读取一下数据并进行比对如果比对不成功也说明写入出错if(read ! *(pdata64j))ret 255;return ret;}}}if(ret ! 0)led_err_flash(10, 100); //擦除失败后闪灯提示, 供调试阶段使用, 正式使用时可删除HAL_FLASH_Lock(); //上锁以结束FLASH操作return ret;
}
封装后的测试函数
u64 data64[32]{0};
u8 g_text_buf[64] {LIJIA000LIJIA001LIJIA002LIJIA003LIJIA004LIJIA005LIJIA006LIJIA007};if(GET_KEY() 0){HAL_Delay(200);LL_flash_read(0x08010000, data64, 4);if(data64[0] 0xffffffffffffffff) //如果是空的则写入{LED1(1);LED2(1);if(LL_flash_write(0x08010000, (u64*)g_text_buf, 4) ! 0)continue;}else //如果为非空则擦除{LL_flash_erase_page(32, 1, 0); //第32页长度1页BANK 0}LL_flash_read(0x08010000, data64, 4);while(GET_KEY() 0);LED1(0);LED2(0);i;}测试结果如下图如果写入成功LED灯会在操作之后灭掉如果写入失败则LED灯会保持点亮状态
总结
绝对不能只写入一次就认为写入正确恰恰相反最开始的2次写入极有可能写入失败。 所以必须重复写入几次并且增加校验即写入完成后再读取数据并进行比较以确保正确。