STM系列的Flash操作方法及误操作笔记
1、flash操作方法
1.1 对stm8的flash操作
#define BaseCfgaddr (uint32*)0x4000
/* * 函数名称 : WriteAddrToFlash * 函数功能 : 数据写入Flash * 输入参数 : * 输出参数 : 无 * 返 回 值 : 0或1 * 其它说明 : 无 * */ uint8 SYS_WriteDataToFlash(uint8* Data,uint32* addr,uint16Len) { if(!Data) return 0; /*操作EEPROM,先进行解锁*/ FLASH_DUKR = 0xAE; /* 注意这里不能断点调试,否则会造成内部不同步,FLASH解锁失败*/ FLASH_DUKR = 0x56; FLASH_CR2 = 0x00; FLASH_NCR2 = 0xFF; if(!(FLASH_IAPSR & 0x08)) /* 检测对应的位是否解锁 */ return 0; memcpy(addr,Data,Len); FLASH_IAPSR = (ST_UINT8)(~0x08); /* 重新上锁 */ return 1; }
按字节操作即每次写入一位
SYS_WriteDataToFlash(Buf,BaseCfgaddr,WriteCfgSize);
1.2 stm32f的flash操作。
使用stm32f0k4
/* * 函数名称 : WriteAddrToFlash * 函数功能 : 数据写入Flash * 输入参数 : * 输出参数 : 无 * 返 回 值 : 0或1 * 其它说明 : 写入一个配置,每次写只用某一页开始写,不可一页中从起始地址写之后又从中间地址写入。 写第二个模块配置时,空间配置从下一页开始写。否则会擦除之前的页,写之前会先擦除该页。 * */ uint8 Sys_WriteDataToFlash(uint8* pData,uint32 Address,uint32 Len) { if(!pData) return 0; uint32_t SLen = 0; uint32_t Wdata; FLASH_Unlock();
FLASH_ErasePage(Address); // 写之前擦除要写入的整页 FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); /* Clear pending flags (if any) */ do { memcpy(&Wdata, pData, 4);// FLASH_ProgramWord(Address,Wdata); Address += 4; pData += 4; SLen += 4; }while(SLen<Len); FLASH_Lock(); return 1; }
/* * 函数名称 : Sys_ReadFlashToBuf * 函数功能 : 从flash中读出数据到buf查看 * 输入参数 : * 输出参数 : 无 * 返 回 值 : * 其它说明 : * */ void Sys_ReadFlashToBuf(uint8* buf,uint32 addr,uint16 len) { while(len--) { *buf = *(uint8*)addr; addr++; buf++; } }
32位机一个字为4个字节,库函数提供两种形式,一种是半字写入另一种是一个字写入,以上使用的是一个字写入的方式
实际操作过程中是将一个结构体内容写入flash,先将该结构体以字节对其,将该结构体指向buf存储,再将该buf按字节写入flash。
读取时将内容读到buf,在将该buf转为结构体,即可准确提取出结构体中定义的值
2、操作中所犯错误及注意点
1、内存地址
错误:从0x08000000基地址开始操作flash,一执行读写flash,程序就卡死。
要操作flash的地址,如果程序是存储在main flash memory中开始执行,进入用户程序,我们操作flash就不能从flash基地址开始操作,这部分已经被应用程序占用。
其操作flash地址值要大于本代码所占用FLASH的大小+0x08000000
用户程序代码占用flash大小可从编译时候看出来
8 394 bytes of readonly code memory
84 bytes of readonly data memory
2 828 bytes of readwrite data memory
#define FLASH_BASE_ADDR (uint32_t)(0x08000000u) #define FLASH_STARTADDR_PAGE11 (FLASH_BASE_ADDR + 1024 * 11)
计算可写在第几页,最好的方法是从最后一页开始写,根据手册了解flash大小,页数,勿超出flash大小,否则易出现HardFault_Handler错误
2、将字转换为按字节写入
错误: 写入的数据与实际的数据值不同
从cotex-m0的库函数来看,对flash的操作时以字或者半字来操作。实际使用操作多按1个字节操作,造成不对齐。
uint32_t Wdata;
memcpy(&Wdata, pData, 4);// FLASH_ProgramWord(Address,Wdata); Address += 4; pData += 4; 固按以上操作,将4个字节转换为按字写入,统一了使用的所有工程,再将地址按四个字节偏移。
3、宏定义地址
错误:flash读取时数据出错,内容指向错误。
SYS_BASE_CFG_T *pCfg = (SYS_BASE_CFG_T *) FLASH_STARTADDR_PAGE11;
操作是将flash页11的地址读取出来,指向转为结构体SYS_BASE_CFG_T 形式读取数据
但在读取就出错,程序运行到该句即出现内存操作错误中断产生
void HardFault_Handler(void) { while (1) { } }
从运行参数上看,当运行到该句,地址指向并不是预先设计好的11页地址
#define FLASH_STARTADDR_PAGE11 FLASH_BASE_ADDR + 1024 * 11
由于宏定义加操作没有加括号,导致实际偏移值出现错误。宏定义式子为防错误一定要加括号
#define FLASH_STARTADDR_PAGE11 (FLASH_BASE_ADDR + 1024 * 11)
4、HardFault_Handler
该中断产生的一般条件是内存非法操作,包括数组越界、内存溢出、指针地址错误、野指针存在
在stm8中当内存非法操作时mcu内部会产生复位,从复位源可查看为非法操作符复位
5、页大小
flash操作擦除时可以以页为单位擦除或者以整片flash擦除。
在使用flash时,如果需要写入两组不同的数据并且在一页以内,建议直接使用一页一组数据,由于内存够大而且防止误操作到其他组数据。
M3或者M0内核大容量与小容量的flash的页大小有所区别,小容量以1024字节 1k为一页,大容量以2048字节2k 为一页。所以操作具体内存时以
(FLASH_BASE_ADDR + 1024 * 11)形式,更直观看出数据位置,便于IDE调试查看。
对结构体的flash写入写出
uint16 ReadToFlash(uint8 *pCfg) { CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg; return sizeof(BASE_CFG_T); } void ReadAllCfg(void) { uint8 *pRead = (uint8*)FLASH_STARTADDR_PAGE11; // 转化为unsigned char 按字节操作 32位一次偏移4字节 uint32 CfgLen = 0; CfgLen = ReadToFlash(pRead ); pRead += CfgLen; CfgInfo.SysReadCfgSize += CfgLen; }
uint16 WriteToFlash(uint8 *pCfg) { CfgInfo.pBaseCfg = (BASE_CFG_T *)pCfg; CfgInfo.pBaseCfg->WriteFlashFlag = CFG_FLASH_FLAG; CfgInfo.pBaseCfg->FeedWatDogTime = 200; return sizeof(BASE_CFG_T); } static void WriteAllCfg(void) { uint8 *pWrite = CfgInfo.CfgBuf; uint32 CfgLen = 0; CfgLen = WriteToFlash(pWrite ); pWrite += CfgLen; CfgInfo.SysWriteCfgSize += CfgLen; Sys_WriteDataToFlash(CfgInfo.CfgBuf,FLASH_STARTADDR_PAGE11,CfgInfo.SysWriteCfgSize); Sys_ReadAllCfg(); }
void Sys_CfgInit(void) { BASE_CFG_T *pCfg = (BASE_CFG_T *) FLASH_STARTADDR_PAGE11;
/*在要写入的一个buf的第一位作为写入标志位,程序下次执行到这儿就不再重写,而只是读取这个flash的内容,实现配置的形式操作。*/ if(pCfg->WriteFlashFlag != CFG_FLASH_FLAG)
{ /* 写配置到FLASH */ WriteAllCfg(); } else { ReadAllCfg(); memcpy(CfgInfo.CfgBuf, (char *)FLASH_STARTADDR_PAGE11, CfgInfo.SysReadCfgSize); } }
typedef __packed struct { uint8 WriteFlashFlag; /* FLASH写标志 */ uint8 DebugFalg; /* 调试使用标志 */ uint32 FeedWatDogTime; }BASE_CFG_T; /* 系统基本配置 */ typedef __packed struct { BASE_CFG_T *pBaseCfg; /* 系统基本配置参数 */ uint8 SysWriteCfgSize; uint8 SysReadCfgSize; uint8 CfgBuf[SYS_CFG_SIZE]; /* 所有人的配置文件 */ }CFG_INFO_T; /* 配置配置参数的 */
CFG_INFO_T CfgInfo;
#define CFG_FLASH_FLAG (0x57)
以上内容为个人使用过程的方法笔记及个人认为的注意点、知识点.
内容不全面,如之后使用有所补充随时更新。
如您发现有所问题,希望给我意见。
