STM32内部Flash读写问题
STM32Flash读写之Flash调试技巧
文章目录
概述:
MCU-STM32H743,编程环境-Keil,Flash容量为2MB(2048K)。
1.先熟悉所用MCU的Flash存储大小以及扇区地址
2MB分成 2个块:Bank1和 Bank2,每个块有8个128K的用户扇区和1个128K的系统扇区,所以用户可用的Flash大小为28128=2048KB,也就是2MB。
闪存模块组织表:
2.Flsah写之前为什么要先擦除
要明白Flash的编程原理都是只能将1写为0,而不能将0写为1,所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。如下图所示将BANK1的扇区2(0x08040000)擦除的结果:
Keil中查看某个地址的数据
在Memory中输入地址查看改地址的内容。
3.Flash擦除长时间占用CPU
片内flash擦除及写入的时序由芯片内自动控制,当发出擦除或写入指令时,CPU暂时停止工作,外围设备(串行口、ADC、Timer等仍处于活动状态),外围设备产生的中断此时被挂起,中断在擦除或写入完成后按优先级顺序执行,所以片内Flash的擦除占用了CPU的时间,这段时间还不少呢,查看STM32H743芯片手册:
写只需要us级的时间,但是扇区擦除和块擦除都是s级的,这时间是真的不短呢,因为Flash机制而且H7的Flash有2M字节,擦除时间长也在所难免。
4.实测Flash擦写占用的时间
实测扇区擦除需要时间
首先要明白STM32H7只能以扇区(128K)或块(1M)为单位擦除,利用GPIO拉高拉低的方式测试Flash扇区擦除的时间,擦除之前拉高GPIO4擦除完毕后拉低,代码如下
GPIO4_L;
GPIO4_H;
STMFLASH_OnlyErase(FLASH_Erase_ADDR,1);//后面会给出该函数
GPIO4_L;
执行结果如下,一个扇区的擦除消耗了950ms
实测不擦除写入时间
不擦除写入是指:要写入的区域没有被写过,或者提前被擦除过。执行以下代码
GPIO4_L;
GPIO4_H;
STMFLASH_OnlyWrite(FLASH_Write_ADDR,(uint32_t*)Flash_WData,8);//flash测试(写) 写完一次就屏蔽(写只需要一次)
GPIO4_L;
执行结果如下,消耗约190us
5.Flash读写要注意几点
●<1>.STM32H7每次写入数据必须为8个字(32字节)
STM32H743内存的编程位数固定为256位,也就是每次写入数据必须为8个字(32字节),如果不够8个字,可以在后面进行补0写入,否则内容将不可预知。而且,写入首地址必须是32的整数倍,否则会影响前后的数据。
32字节写入至BANK1的扇区2(0x08040000):
执行代码:
uint32_t Flash_WData[8] ={0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA};//测试数组
STMFLASH_Write(0x08040000,(uint32_t*)Flash_WData,8);//Flash擦除后写入
写入结果
小于32字节写入至BANK1的扇区2(0x08040000):
执行代码:
uint32_t Flash_WData[1] ={0xAAAAAAAA};//测试数组
STMFLASH_Write(0x08040000,(uint32_t*)Flash_WData,1);//写入读取的安装位置放置在0x08020060(扇区2)flash地址中(转存上一次写入的ID值)
写入结果,出现了不可预知的内容:
●<2>Flash写入的首地址要大于代码占用的地址
stm32默认就是从flash中取指令执行的,所以我们用户操作Flash时要避开代码占用区,闪存的起始地址为 0x08000000。我们在Keil的工程下就可以查看代码的起始地址和结束地址。方式如下:在Keil工程目录下搜索.map文件,用notepad++打开,里面有各个变量存放的地址代码的起始地址等等,从中看到代码起始地址为0x08000000:
结束地址为:
所以我们所要操作的Flash起始地址要大于代码占用的结束地址,并且为32的整数倍。
6.keil的.map文件中包含了什么
编译后的.map文件中包含了很多有用的信息,关于涉及内存调试必看。
1.Section Cross References:模块、段(入口)交叉引用
2.Removing Unused input sections from the image:移除未调用模块
3.Image Symbol Table:映射符号表
4.Memory Map of the image:内存(映射)分布
5.Image component sizes:存储组成大小
比如在程序的编译后,我们会注意到以下信息:
其中
Code: 指代码的大小
RO-data :const变量或字符串常量
RW-data :指可读写(RW)、已初始化的变量数据
ZI-data:指未初始化(ZI)的变量数据
其实在工程目录下的.map文件中有更详细的描述,动手查看一下。
.map文件中:
Code:代码段
RO :const变量或字符串常量
RW :指可读写(RW)的数据
data:赋值了的全局变量或static变量、全局数组
bss:未赋值的全局变量或static变量
★注意:
Code、Ro-data:位于FLASH中;
RW-data、ZI-data:位于RAM中;
RW-data已初始化的数据会存储在Flash中,上电会从FLASH搬移至RAM。
观察下图.map文件中,RW数据事先保存在Flash中,但是.bss(未赋值的全局变量)就不会为其在Flash中分配内存。
所以程序占用的Flash大小为Code + RO Data + RW Data(中已初始化的数据)。
7.操作不当导致Flash损坏会怎样
调试一模一样的三块电路板,同样的程序两块好使,另一块一运行就进错误中断,最后查看了我操作的Flash部分:
成了这样…
换了扇区就好了。只能怀疑不小心损坏了它?
8.Flash上锁与解锁
在Flash的读、写、擦除、操作的时候都会对Flash进行上锁解锁操作。
解锁: 在FLASH_KEYR寄存器写入特定的序列(KEY1和KEY2)。
上锁: 在Flash操作完成后要对Flash上锁,将FLASH_CR寄存D0位置1。
9.Keil编译器如何查看MCU寄存器的值
执行以下步骤:
就可以查看当前某个寄存器的值,调试起来非常方便了。
10.Flash读、写、擦除、擦除写代码
●Flash只擦除不写
/*只擦除不写*/
void STMFLASH_OnlyErase(uint32_t EraseAddr,uint32_t NumToErase)
{
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus=HAL_OK;
uint32_t SectorError=0;
uint32_t addrx=0;
uint32_t endaddr=0;
if(EraseAddr<STM32_FLASH_BASE||EraseAddr%4)return; //非法地址
HAL_FLASH_Unlock(); //解锁
addrx=EraseAddr; //写入的起始地址
endaddr=EraseAddr+NumToErase*4; //写入的结束地址
if(addrx<0X1FF00000)
{
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除
FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区STMFLASH_GetFlashSector(addrx)
FlashEraseInit.Banks=FLASH_BANK_1; //操作BANK1
FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
{
break;//发生错误了
}
SCB_CleanInvalidateDCache(); //清除无效的D-Cache
}else addrx+=4;
FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK_1); //等待上次操作完成
}
}
FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK_1); //等待上次操作完成
if(FlashStatus==HAL_OK)
{
}
HAL_FLASH_Lock(); //上锁
}
●Flash只写不擦
/*只写不擦除*/
void STMFLASH_OnlyWrite(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)
{
HAL_StatusTypeDef FlashStatus=HAL_OK;
uint32_t endaddr=0;
HAL_FLASH_Unlock(); //解锁
endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
while(WriteAddr<endaddr)//写数据
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD,WriteAddr,(uint64_t)pBuffer)!=HAL_OK)//写入数据
{
break; //写入异常
}
WriteAddr+=32;
pBuffer+=8;
}
HAL_FLASH_Lock(); //上锁
}
●Flash先擦除后写
/*先擦出后写*/
void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)
{
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus=HAL_OK;
uint32_t SectorError=0;
uint32_t addrx=0;
uint32_t endaddr=0;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
HAL_FLASH_Unlock(); //解锁
addrx=WriteAddr; //写入的起始地址
endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
if(addrx<0X1FF00000)
{
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除
FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区STMFLASH_GetFlashSector(addrx)
FlashEraseInit.Banks=FLASH_BANK_1; //操作BANK1
FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
{
break;//发生错误了
}
SCB_CleanInvalidateDCache(); //清除无效的D-Cache
}else addrx+=4;
FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK_1); //等待上次操作完成
}
}
FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK_1); //等待上次操作完成
if(FlashStatus==HAL_OK)
{
while(WriteAddr<endaddr)//写数据
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD,WriteAddr,(uint64_t)pBuffer)!=HAL_OK)//写入数据
{
break; //写入异常
}
WriteAddr+=32;
pBuffer+=8;
}
}
HAL_FLASH_Lock(); //上锁
}
●Flash读
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(32位)数
void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)
{
uint32_t i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
ReadAddr+=4;//偏移4个字节.
}
}
下一篇:Flash擦除长时间占用CPU时间,影响代码正常运行解决方案。
待更新…
待更新…
待更新…
待更新…
待更新…
待更新…
待更新…
★★★如有错误,欢迎指导!!!
本文地址:https://blog.csdn.net/qq_40147893/article/details/107423621
上一篇: pi
下一篇: 历史上的钟无艳是什么人?她是什么结局