当前位置:   article > 正文

STM32FLASH介绍和代码_flash_cr_per

flash_cr_per

FLASH结构

Flash 是一种非易失性存储器,其结构由主存储器块、系统存储器、OTP 区域和选项字节组成。 主存储器块被分为不同大小的扇区,包括 4 个 16 KB 的扇区、1 个 64 KB 的扇区和 7 个 128 KB 的扇区。这些扇区可被擦除和编程,用于存储应用程序和数据。 系统存储器可以在系统启动时自主加载,用于执行初始化和配置操作。这个存储器通常包含 bootloader、初始化代码和配置数据。 OTP 区域是一次性可编程的区域,可以用来存储用户数据。该区域还有 16 个额外字节,用于锁定对应的 OTP 数据块,以保护其不被意外擦除或编程。 选项字节包含配置数据,用于设置读写保护、BOR 级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。这些选项可以被编程,并在器件复位时加载。Flash 存储器是许多嵌入式系统中重要的组成部分,可以存储数据、程序和配置信息,并支持快速读写操作。

以STM32F4系列为例,FLASH分布如下图。

FLASH操作流程

  1. 对于FLASH_CR的LOCK位,需要先判断是否已经锁定。如果锁定的话,需要先解锁才能进行擦除操作。

  1. FLASH_SR寄存器的BSY位为‘0’时表示闪存空闲,可以进行操作。如果BSY位为‘1’,表示当前有其他操作正在进行,需要等待其完成后再进行擦除操作。

  1. 设置FLASH_CR寄存器中的PER位为‘1’可以启用擦除模式。

  1. 选择要擦除的页需要使用FLASH_AR寄存器进行设置。

  1. 设置FLASH_CR寄存器中的STRT位为‘1’,开始执行擦除操作。

  1. 等待BSY位变为‘0’,表示擦除操作已经完成。

  1. 最后,需要读出被擦除的页并进行验证,以确保擦除操作成功。

需要注意的是,在进行实际硬件擦除操作时,需要具备相应的硬件技术和安全措施,以避免对设备造成损坏或安全风险。

Flash操作注意事项

在进行STM32内置Flash的操作时,必须遵循一些重要的操作规则。其一是擦除操作必须先于写操作。其二是内置Flash以页为单位进行擦除,每页的大小以具体芯片型号而定,其起始地址为0x08000000。 同时,写操作必须以16位宽度为单位,且可以跨页进行写入。在进行内置Flash的擦写操作时,必须打开外部/内部高速振荡器,以确保操作的准确性和稳定性。但是需要注意的是,内置Flash最多只能进行10万次的擦写操作,因此不能进行死循环的擦写操作,否则会损坏内置Flash。 此外,在进行擦写操作时,需要注意避开用户程序可能存储的区域,以免意外擦写导致错误。虽然每一页的擦除时间为10ms,比较慢,但是不能单个字节的擦写,需要以页为单位进行擦除操作。总之,要正确而有效地进行STM32内置Flash操作,需要遵循一系列操作规则,包括正确的操作顺序、正确的宽度单位、必要的振荡器打开、避开用户程序存储区域等等。

Flash 控制寄存器解锁

  1. 当STM32芯片复位后,Flash控制寄存器(FLASH_CR)不允许执行写操作,以避免因电气干扰等原因而出现对Flash的意外操作。因此,在进行Flash控制寄存器的写操作之前,必须先执行解锁操作。解锁的顺序为:

  1. 在Flash密钥寄存器(FLASH_KEYR)中写入KEY1=0x45670123;

  1. 在Flash密钥寄存器(FLASH_KEYR)中写入KEY2=0xCDEF89AB。

如果解锁操作的顺序出错,将会返回总线错误并锁定FLASHCR寄存器,直到下一次复位。同时,也可以通过软件将FLASHCR寄存器中的LOCK位置为1来锁定FLASH_CR寄存器。

需要特别注意的是,当FLASHSR寄存器中的BSY位为1时,将不能在写模式下访问FLASHCR寄存器。在这种情况下,对该寄存器的任何写操作尝试都会导致AHB总线阻塞,直到BSY位清零。

擦除和编程操作

执行任何 Flash 编程操作(擦除或编程)时,CPU 时钟频率 (HCLK) 不能低于 1 MHz。如果 在 Flash 操作期间发生器件复位,无法保证 Flash 中的内容。 在对 Flash 执行写入或擦除操作期间,任何读取 Flash 的尝试都会导致总线 阻塞。只有在完成编程操作后,才能正确处理读操作。这意味着,写/擦除操作进行期间不能 从 Flash 中执行代码或数据获取操作

FLASH擦除具体步骤

Flash 擦除操作可针对扇区或整个 Flash(批量擦除)执行。执行批量擦除时,不会影响 OTP 扇区或配置扇区

在进行STM32芯片的Flash扇区擦除操作时,需要遵循以下具体步骤:

  1. 检查FLASH_SR寄存器中的BSY位,以确认当前未执行任何Flash操作;

  1. 在FLASH_CR寄存器中,将SER位置1,并从主存储块的12个或24个和扇区中选择要擦除的扇区(SNB);

  1. 将FLASH_CR寄存器中的STRT位置1;

  1. 等待BSY位清零。

如果要执行批量擦除,采用以下步骤:

  1. 检查FLASH_SR寄存器中的BSY位,以确认当前未执行任何Flash操作;

  1. 将FLASH_CR寄存器中的MER位置1;

  1. 将FLASH_CR寄存器中的MER和MER1位置1;

  1. 将FLASH_CR寄存器中的STRT位置1;

  1. 等待BSY位清零。

在进行扇区擦除或批量擦除操作时,都必须先确认BSY位为0,以确保当前未执行任何Flash操作。

  1. #include "stmflash.h"
  2. //读取指定地址的半字(16位数据)
  3. //faddr:读地址
  4. //返回值:对应数据.
  5. u16 STMFLASH_ReadHalfWord(u32 faddr)
  6. {
  7. return *(vu16*)faddr;
  8. }
  9. #if STM32_FLASH_WREN //如果使能了写
  10. //不检查的写入
  11. //WriteAddr:起始地址
  12. //pBuffer:数据指针
  13. //NumToWrite:半字(16位)数
  14. void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
  15. {
  16. u16 i;
  17. for(i=0;i<NumToWrite;i++)
  18. {
  19. HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
  20. WriteAddr+=2;//地址增加2.
  21. }
  22. }
  23. //从指定地址开始写入指定长度的数据
  24. //WriteAddr:起始地址(此地址必须为2的倍数!!)
  25. //pBuffer:数据指针
  26. //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
  27. #if STM32_FLASH_SIZE<256
  28. #define STM_SECTOR_SIZE 1024 //字节
  29. #else
  30. #define STM_SECTOR_SIZE 2048
  31. #endif
  32. u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
  33. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
  34. {
  35. u32 secpos; //扇区地址
  36. u16 secoff; //扇区内偏移地址(16位字计算)
  37. u16 secremain; //扇区内剩余地址(16位字计算)
  38. u16 i;
  39. u32 offaddr; //去掉0X08000000后的地址
  40. if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
  41. HAL_FLASH_Unlock(); //解锁
  42. offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.
  43. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
  44. secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
  45. secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
  46. if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
  47. while(1)
  48. {
  49. STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
  50. for(i=0;i<secremain;i++) //校验数据
  51. {
  52. if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
  53. }
  54. if(i<secremain) //需要擦除
  55. {
  56. FLASH_PageErase(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区
  57. FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
  58. CLEAR_BIT(FLASH->CR, FLASH_CR_PER); //清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成!((((FLASH_TypeDef *)((0x40000000UL + 0x00020000UL) + 0x00002000UL))->CR) &= ~((0x1UL << (1U))))
  59. //但是HAL库里面并没有做,应该是HAL库bug!
  60. for(i=0;i<secremain;i++)//复制
  61. {
  62. STMFLASH_BUF[i+secoff]=pBuffer[i];
  63. }
  64. STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
  65. }else
  66. {
  67. FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
  68. STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.
  69. }
  70. if(NumToWrite==secremain)break;//写入结束了
  71. else//写入未结束
  72. {
  73. secpos++; //扇区地址增1
  74. secoff=0; //偏移位置为0
  75. pBuffer+=secremain; //指针偏移
  76. WriteAddr+=secremain*2; //写地址偏移(16位数据地址,需要*2)
  77. NumToWrite-=secremain; //字节(16位)数递减
  78. if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
  79. else secremain=NumToWrite;//下一个扇区可以写完了
  80. }
  81. };
  82. HAL_FLASH_Lock(); //上锁
  83. }
  84. #endif
  85. //从指定地址开始读出指定长度的数据
  86. //ReadAddr:起始地址
  87. //pBuffer:数据指针
  88. //NumToWrite:半字(16位)数
  89. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
  90. {
  91. u16 i;
  92. for(i=0;i<NumToRead;i++)
  93. {
  94. pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
  95. ReadAddr+=2;//偏移2个字节.
  96. }
  97. }
  98. //测试用///
  99. //WriteAddr:起始地址
  100. //WriteData:要写入的数据
  101. void Test_Write(u32 WriteAddr,u16 WriteData)
  102. {
  103. STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字
  104. }
  1. #ifndef __STMFLASH_H__
  2. #define __STMFLASH_H__
  3. #include "user_includes.h"
  4. #include "config.h"
  5. #include "stm32f1xx_hal.h"
  6. #include "stm32f1xx.h"
  7. //
  8. //用户根据自己的需要设置
  9. #define STM32_FLASH_SIZE 512 //所选STM32的FLASH容量大小(单位为K)
  10. #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能)
  11. #define FLASH_WAITETIME 50000 //FLASH等待超时时间
  12. //FLASH起始地址
  13. #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
  14. #define FLASH_SAVE_ADDR 0X0801E000
  15. u8 STMFLASH_GetStatus(void); //获得状态
  16. u8 STMFLASH_WaitDone(u16 time); //等待操作结束
  17. u8 STMFLASH_ErasePage(u32 paddr); //擦除页
  18. u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);//写入半字
  19. u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字
  20. void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据
  21. u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度数据
  22. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
  23. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据
  24. //测试写入
  25. void Test_Write(u32 WriteAddr,u16 WriteData);
  26. #endif
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号