使用内部FLASH模拟EEPROM(STM32F4)
本文最后更新于4 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

基于STM32F407VET6

Flash 结构如下:

— 主存储器块,分为 4 个 16 KB 扇区、1 个 64 KB 扇区和 7 个 128 KB 扇区

— 系统存储器,器件在系统存储器自举模式下从该存储器启动

— 512 字节 OTP(一次性可编程),用于存储用户数据 OTP 区域还有 16 个额外字节,用于锁定对应的 OTP 数据块。

— 选项字节,用于配置读写保护、BOR 级别、软件/硬件看门狗以及器件处于待机或 停止模式下的复位。

一、CPU 时钟频率与 Flash 读取时间之间的关系

为了准确读取 Flash 数据,必须根据 CPU 时钟 (HCLK) 频率和器件电源电压在 Flash 存取控制寄存器 (FLASH_ACR) 中正确地编程等待周期数 (LATENCY)。

在本次实验中,我的HCLK为168MHz

二、需要提高 CPU 频率时的操作步骤

(1)将新的等待周期数编程到 FLASH_ACR 寄存器中的 LATENCY 位

(2)通过读取 FLASH_ACR 寄存器,检查新的等待周期是否设置成功

(3)通过改写 RCC_CFGR 寄存器中的 SW 位来修改 CPU 时钟源

(4)如有需要,可通过改写 RCC_CFGR 中的 HPRE 位来修改 CPU 时钟预分频器

(5)通过读取 RCC_CFGR 寄存器中相应的时钟源状态(SWS 位)和/或 AHB 预分频值 (HPRE 位),检查新的 CPU 时钟源和/或新的 CPU 时钟预分频值是否设置成功

DCRST:数据缓存复位

ICRST:指令缓存复位

DCEN:数据缓存使能

ICEN:指令缓存使能

LATENCY:延迟 (周期数)

000:零等待周期

001:一个等待周期

010:两个等待周期

011:三个等待周期

100:四个等待周期

101:五个等待周期

110:六个等待周期

111:七个等待周期

对应代码如下:

 /*设置Flash周期数*/
 void Flash_Set_Cycle()
 {
     FLASH->ACR &= ~(FLASH_ACR_ICEN | FLASH_ACR_DCEN);   //禁用缓存
     FLASH->ACR |= FLASH_ACR_ICRST; // 复位指令缓存
     FLASH->ACR |= FLASH_ACR_DCRST; // 复位数据缓存
     __NOP(); __NOP(); __NOP();  // 短暂延时
     FLASH->ACR |= FLASH_ACR_ICEN | FLASH_ACR_DCEN; // 使能缓存
     FLASH->ACR |= 0x05; // 设置五个等待周期
 }

三、Flash 控制寄存器解锁

复位后,Flash 控制寄存器 (FLASH_CR) 不允许执行写操作,以防因电气干扰等原因出现对 Flash 的意外操作。此寄存器的解锁顺序如下:

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

对应代码如下:

四、上锁

 /*解锁*/
 void Flash_Unlock()
 {
     FLASH->KEYR = FLASH_KEY1;
     __NOP(); __NOP(); __NOP();  // 短暂延时
     FLASH->KEYR = FLASH_KEY2;
 }

给FLASH的CR寄存器上的LOCK写1即可

 /*上锁*/
 void Flash_Lock()
 {
  FLASH->CR |= FLASH_CR_LOCK;
 }
 ​

五、擦除

把 Flash 的单元从“1”写为“0”时,无需执行擦除操作即可进行连续写操作。

把 Flash 的 单元从“0”写为“1”时,则需要执行 Flash 擦除操作。

如果同时发出擦除和编程操作请求,首先执行擦除操作。

扇区擦除

扇区擦除的具体步骤如下:

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

(2)在 FLASH_CR 寄存器中,将 SER 位置 1,并从主存储块的 12 个(基于STM32F407) 扇区中选择要擦除 的扇区 (SNB)

(3)将 FLASH_CR 寄存器中的 STRT 位置 1

(4)等待 BSY 位清零

 /*擦除扇区*/
 void Flash_Erase_Sector(uint32_t Sector)
 {
     if((FLASH->SR & 0x10000) == 0)  //判断当前有没有执行Flash 操作
     {
         FLASH->CR |= 0x02; // 在 FLASH_CR 寄存器中,将SER位置 1
         FLASH->CR &= ~(0x78);  // 清除SNB位
         FLASH->CR |= Sector;          // 选择要擦除的扇区
         FLASH->CR |= FLASH_CR_STRT;   // 将 FLASH_CR 寄存器中的 STRT 位置 1
 ​
         while((FLASH->SR & 0x10000) != 0); // 等待 BSY 位清零
 ​
         FLASH->CR &= ~(0x02); // 在擦除完成,将SER位置0
 ​
 }

六、写入

Flash 编程顺序如下:

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

(2)将 FLASH_CR 寄存器中的 PG 位置 1。

(3)针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作:

并行位数为 x8 时按字节写入

并行位数为 x16 时按半字写入

并行位数为 x32 时按字写入

并行位数为 x64 时按双字写入

(4)等待 BSY 位清零

 /*写入*/
 void Flash_Write_DataByte(uint32_t address, uint8_t data)
 {
     cli(); //关闭总中断,防止打断数据写入
 ​
     Flash_Unlock();
 ​
 ​
 ​
     if((FLASH->SR & 0x10000) == 0)  //判断当前有没有执行Flash 操作
     {
         FLASH->CR |= 0x01; // 在 FLASH_CR 寄存器中,将PG位置 1
         FLASH->CR &= ~(0x300);  // 设置并行位数为8  字节写入
 ​
          *(__IO uint8_t*)address = data; //写入数据
 ​
         while((FLASH->SR & 0x10000) != 0); // 等待 BSY 位清零
 ​
         FLASH->CR &= ~(0x01); // 写入完成,将PG位置0
     }
 ​
     Flash_Lock();
 ​
     sei();  //开启总中断
 }
 ​

7、读取

读取很简单,读取指定的地址即可

 uint8_t Flash_Read_DataByte(uint32_t address)
 {
  return *(volatile uint8_t*)address;
 ​
 }
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇