Skip to content

读写内部Flash

image-20240106155515167

image-20240106160003826

内部Flash的结构

实际上就是一个内部存储器, 用于储存代码, 掉电不会丢失

下载过程: 读写内部flash的程序放在内存, 之后把程序写入flash

Cortex-M3使用ICode与Flash连接, 有三十二跟数据线与地址线, 所以读取比外部SPI-FLASH速度快

同时可以进行上锁, 防止程序被抄袭

image-20230728223227078

系统存储区, 用户不可以访问, ISP支持, 以及芯片保护

选项字节, 存储的是芯片保护以及电压操作, 待机/停机复位, 软件/硬件看门狗功能, 共16字节, 可以修改

  • 主存储器

一般说的Flash实际上就是这一个位置, 一共有256页, 在写入之前按照页进行擦除, 设置擦除用到的位置

image-20230728223845130

image-20230728224013755

参考文件: STM32F10xxx闪存编程手册

实际操作过程

image-20240106160244789

  • 写入

image-20240106161454026

image-20240106161606246

  1. 解锁, 为了防止误操作, 芯片复位之后会自动上锁, 这时候不允许设置FLASH的控制寄存器以及修改内容

解锁步骤: 向秘钥寄存器FLASH_KEYR写入KEY1=0x45670123

再向里面写入KEY2=0xCDEF89AB

image-20230729113032490

image-20240106161145385

通过CR寄存器的这一个位可以检查现在的状态

  1. 擦除扇区,

image-20240106161514924

检查FLSH_SR寄存器中的忙标志

image-20230729113621736

FLASH_CR寄存器的PER位置1

image-20230729113410995

使用FLASH_AR寄存器选择要擦除的页

image-20230729113521923

FLASR_CR的STRT设置为1

image-20230729113658672

等待BUSY位清零

  1. 写入扇区

检查FLSH_SR寄存器中的忙标志

image-20230729113621736

FLASH_CR寄存器的PG位置1

image-20230729114330237

向指定的位置写入数据, 每一次16位

等待操作完成

  • 读取

image-20240106160627357

设置等待周期是通过寄存器进行的, 设置好以后就可以直接使用指针进行读取

image-20240106160841661

没有读保护可以直接进行读取

由于FLASH本身存储有程序数据, 若不是有意的删除某段程序代码, 一般不修改程序的空间内容, 所以使用之前了解一下哪些空间被写入程序, 通过查询.map文件获取信息, 在Map of the image

image-20230729132537851

Load Regina LR ROM1以及Execution Region ER_IROM1分别是程序加载以及执行的空间

实践

c
	uint32_t *p;
	//解锁
	FLASH_Unlock();
	//擦除第五页
	FLASH_ErasePage(0x08000000 + 2*1024*5);
	//写入
	FLASH_ProgramWord(0x08000000 + 2*1024*5, 0x12345678);
	printf("已经写入完成\r\n");
	FLASH_Lock();
	
	p = (uint32_t*)(0x08000000 + 2*1024*5);
	printf("读取的值是0x%x\r\n", *p);

设置读写保护以及解除

实际发布的产品芯片中包含了程序, 但是J-Link等下载器可以获取, 所以有一个读写保护, 这样就会让程序非法读取

选项字节以及读写保护

需要更改Flash的选项字节, stm32会根据他的内容进行写保护配置

image-20230729154939142

n=> 取反, 就是前一列的取反

  • RDP读保护

设置为0xA5的时候是没有写保护的, 不是这个的时候就会处于保护的状态

image-20230729155340372

  • USER: 设置看门狗
  • Datax: 这个地址留给用户使用

image-20230729155614243

  • WRPx, 用于用于进行写保护

image-20230729155647635

这里的写保护是针对下载器以及仿真器, 或者是内部SRAM自举都不能对FLASH进行读写, 但是如果是从内部FLASH自举的时候就可以对Flash进行读

设置成读保护之后前4K字节会加上写保护, 也就是说Flash内部也不能进行读写, 利用这个特性可以编写IAP代码, 更新FLASH中的程序, 原理是通过通讯接口接收将要更新的程序内容, 然后利用内部的FLASH擦写操作把内容烧写到内部的FLASH中, 实现程序的更新, 类似于ISP程序下载

写保护会导致所有的方式都无法对代码进行更改, 解除之后不会丢失

解除保护

需要把RDP位重新设置为0xA5, 在解除保护之前芯片会擦除FLASH的所有的内容, 所有的代码丢失

image-20230729161117176

解除写保护, 把对应的WRP设置为1

选项字节修改

复位之后的状态是可读但是进行写保护的, 因此FLASH_CR寄存器访问限制, 想要修改需要对FLASH_OPTKEYR寄存器写入解锁编码, 由于需要访问FLASH_CR, 所以要先解锁

  • 解除FLASH_CR寄存器
  • 解除选项字节的访问权限, 向FLASH_OPTKEYR写入KEY1=0x45670123, 以及KEY2=0xCDEF89AB
  • 配置FLASH_CR的OPTPG位, 准备修改选项字节
  • 使用指针修改
  • 解除读保护之后确认FLASH的擦除操作完成
  • 设置读保护以及解除需要给芯片重新上电, 配置选项字节生效, 写保护配置以及解除需要进行系统复位, 有专门的函数
c
typedef struct
{
  __IO uint16_t RDP;
  __IO uint16_t USER;
  __IO uint16_t Data0;
  __IO uint16_t Data1;
  __IO uint16_t WRP0;
  __IO uint16_t WRP1;
  __IO uint16_t WRP2;
  __IO uint16_t WRP3;
} OB_TypeDef;
#define OB                  ((OB_TypeDef *) OB_BASE) 
#define OB_BASE               ((uint32_t)0x1FFFF800)    /*!< Flash Option Bytes base address */

在写入低字节高位设置为0的时候, stm32会自动进行设置高字节为补码

c
FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)
{
  FLASH_Status status = FLASH_COMPLETE;
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  status = FLASH_WaitForLastOperation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* Authorizes the small information block programming */
    FLASH->OPTKEYR = FLASH_KEY1;
    FLASH->OPTKEYR = FLASH_KEY2;
      //恢复选项字节为出厂设置
    FLASH->CR |= CR_OPTER_Set;
    FLASH->CR |= CR_STRT_Set;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(EraseTimeout);
    if(status == FLASH_COMPLETE)
    {
      /* if the erase operation is completed, disable the OPTER Bit */
      FLASH->CR &= CR_OPTER_Reset;
      /* Enable the Option Bytes Programming operation */
      FLASH->CR |= CR_OPTPG_Set; 
      if(NewState != DISABLE)
      {
          //写入非0xA5的值设置为读保护
        OB->RDP = 0x00;
      }
      else
      {
        OB->RDP = RDP_Key;  
      }
      /* Wait for last operation to be completed 等待擦除完成*/
      status = FLASH_WaitForLastOperation(EraseTimeout); 
    
      if(status != FLASH_TIMEOUT)
      {
        /* if the program operation is completed, disable the OPTPG Bit */
        FLASH->CR &= CR_OPTPG_Reset;
      }
    }
    else 
    {
      if(status != FLASH_TIMEOUT)
      {
        /* Disable the OPTER Bit */
        FLASH->CR &= CR_OPTER_Reset;
      }
    }
  }
  /* Return the protection operation Status */
  return status;       
}

在使用库函数去除读保护的时候, 会擦除之前设置的选项字节, 包括写保护

image-20230731135507154

这里在使用RAM调试的时选择不进行擦除, 因为Flash被写保护了, 擦除不了

c
void Write_Protect(void)
{

	FLASH_Unlock(); 
	//擦除之前的选项, 这是由于flash的特性, 写入之前需要进行擦除
	FLASH_EraseOptionBytes();
	
	FLASH_EnableWriteProtection (FLASH_WRProt_AllPages );
	
	NVIC_SystemReset();

}


void Write_Protect_Disable(void)
{

	FLASH_Unlock(); 
	
	FLASH_EraseOptionBytes();
	
	FLASH_EnableWriteProtection (0x00000000 );
	//进行一个系统的复位请求
	NVIC_SystemReset();

}




void Read_Protect(void)
{

	FLASH_Unlock(); 
	
	FLASH_EraseOptionBytes();
	
	FLASH_ReadOutProtection (ENABLE );
	
//	NVIC_SystemReset();

}


void Read_Protect_Disable(void)
{

	FLASH_Unlock(); 
	
	FLASH_EraseOptionBytes();
	
	FLASH_ReadOutProtection (DISABLE );
	
//	NVIC_SystemReset();

}

F4/F7/H7

image-20240106162004955

image-20240106162110887

image-20240106162230905

image-20240106162304719

image-20240106162445472

image-20240106162501675

image-20240106162539957

image-20240106162558399

image-20240106162617920

image-20240106162852298

实际操控

image-20240106162921111

image-20240106162957212