第四章 STM32 Flash

发布于:2025-03-06 ⋅ 阅读:(9) ⋅ 点赞:(0)

CPU:STM32F103RCT6,LQFP64,FLASH:256KB,SRAM:48KB;
flash起始地址为0x8000000,大小为0x4000(16进制)—>262144字节(10进制)—>256KB
RAM起始地址为0x2000000,大小为0xC000(16进制)—>49125字节(10进制)—>48KB

1、Flash 简介

STM32的闪存模块由:主存储块、信息块和闪存存储器接口寄存器组成。

主存储块:存放代码、数据常量(const类型的数据)

信息块    :分为两部分;

                系统存储器,用来存放bootloader程序

                选择字节,用来配置读保护和写保护等功能。

闪存存储器接口寄存器:用于控制闪存读写操作

从系统结构框图中,可以看到,对内置Flash进行读写操作,使用Icode和Dcode总线,也会使用总线矩阵(AHB)

Icode:当CPU执行代码时,从Flash取指令

Dcode:从Flash中读取常量数据

2、Flash 编程事项

1字 = 32位、半字=16位、1字节=8位

1字 = 2半字 = 4字节

  • STM32 复位后, FPEC 模块是被保护的,不能写入 FLASH_CR 寄存器;通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和 KEY2),只有在写保护被解除后,我们才能操作相关寄存器。

  • STM32 闪存的编程每次必须写入 16 位,当 FLASH_CR 寄存器的PG位为’ 1’时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据,FPEC 都会产生总线错误。在编程过程中(BSY 位为’ 1’ ),任何读写闪存的操作都会使 CPU暂停,直到此次闪存编程结束。

  • STM32 的 FLASH 在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入,在 FLASH_SR寄存器的PGERR位将得到一个警告。

3、Flash 擦除操作

Flash擦除分为:页擦除、整片擦除

1. 检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁
2. 检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作
3. 设置 FLASH_CR 寄存器的 PER 位为’ 1’

Flash 擦除操作函数:

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);            // 页
FLASH_Status FLASH_EraseAllPages(void);                                     // 所有页
FLASH_Status FLASH_EraseOptionBytes(void);                                // 字节数据擦除

FLASH_Status FLASH_ErasePage(uint32_t Page_Address)        // 页擦除操作函数
{
  FLASH_Status status = FLASH_COMPLETE;                     
  assert_param(IS_FLASH_ADDRESS(Page_Address));            // 检验传入地址是否有效(在内存范围内)
#ifdef STM32F10X_XL                                        // 大容量 每页是2K
  if(Page_Address < FLASH_BANK1_END_ADDRESS)  
  {
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank1Operation(EraseTimeout);// 等待上一次操作完成,一个超时等待函数
    if(status == FLASH_COMPLETE)
    { 
      /* if the previous operation is completed, proceed to erase the page */
      FLASH->CR|= CR_PER_Set;                               // 使能CR寄存器的页页擦除位
      FLASH->AR = Page_Address;                             // 将要擦除页的起始地址写入寄存器
      FLASH->CR|= CR_STRT_Set;                              // 使能CR的bit6,开始页擦除操作。
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank1Operation(EraseTimeout);// 等待擦除完成
      /* Disable the PER Bit */                    
      FLASH->CR &= CR_PER_Reset;                            // 将页擦除使能位清零
    }
  }
  else                                                        
  {
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank2Operation(EraseTimeout);
    if(status == FLASH_COMPLETE)
    { 
      /* if the previous operation is completed, proceed to erase the page */
      FLASH->CR2|= CR_PER_Set;
      FLASH->AR2 = Page_Address; 
      FLASH->CR2|= CR_STRT_Set;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank2Operation(EraseTimeout);
      /* Disable the PER Bit */
      FLASH->CR2 &= CR_PER_Reset;
    }
  }
#else                                                        // 非大容量
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  { 
    /* if the previous operation is completed, proceed to erase the page */
    FLASH->CR|= CR_PER_Set;
    FLASH->AR = Page_Address; 
    FLASH->CR|= CR_STRT_Set;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(EraseTimeout);
    /* Disable the PER Bit */
    FLASH->CR &= CR_PER_Reset;
  }
#endif /* STM32F10X_XL */
  /* Return the Erase Status */
  return status;
}
FLASH_Status FLASH_EraseAllPages(void)                // 整片擦除
{
  FLASH_Status status = FLASH_COMPLETE;
#ifdef STM32F10X_XL
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastBank1Operation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to erase all pages */
     FLASH->CR |= CR_MER_Set;                         // 使能整片擦除位
     FLASH->CR |= CR_STRT_Set;                        // 使能开始擦除位
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank1Operation(EraseTimeout);
    /* Disable the MER Bit */
    FLASH->CR &= CR_MER_Reset;                        // 将整片擦除位清零
  }    
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to erase all pages */
     FLASH->CR2 |= CR_MER_Set;
     FLASH->CR2 |= CR_STRT_Set;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank2Operation(EraseTimeout);
    /* Disable the MER Bit */
    FLASH->CR2 &= CR_MER_Reset;
  }
#else
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to erase all pages */
     FLASH->CR |= CR_MER_Set;
     FLASH->CR |= CR_STRT_Set;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(EraseTimeout);
    /* Disable the MER Bit */
    FLASH->CR &= CR_MER_Reset;
  }
#endif /* STM32F10X_XL */
  /* Return the Erase Status */
  return status;
}
FLASH_Status FLASH_EraseOptionBytes(void)            // 字节擦除操作
{
  uint16_t rdptmp = RDP_Key;
  FLASH_Status status = FLASH_COMPLETE;
  /* Get the actual read protection Option Byte value */ 
  if(FLASH_GetReadOutProtectionStatus() != RESET)    // 获取当前读保护状态
  {
    rdptmp = 0x00;  
  }
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* Authorize the small information block programming */
    FLASH->OPTKEYR = FLASH_KEY1;                    // 写入 key1 和key2 序列,获取对寄存器FLASH_CR寄存器操作权限
    FLASH->OPTKEYR = FLASH_KEY2;
    /* if the previous operation is completed, proceed to erase the option bytes */
    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;                  // 使能擦除选择字节位
      /* Restore the last read protection Option Byte value */
      OB->RDP = (uint16_t)rdptmp;                 // 将保护的数据写入到选项字节中
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation(ProgramTimeout);
      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 OPTPG Bit */
        FLASH->CR &= CR_OPTPG_Reset;
      }
    }  
  }
  /* Return the erase status */
  return status;
}

4、Flash读数据操作

u16 STMFLASH_ReadHalfWord(u32 faddr)    // 读半个字
{
	return *(vu16*)faddr; 
}

Flash操作可以直接通过寻址的方式,读写flash中内容。

5、Flash写数据操作

1. 检查FLASH_CR的LOCK是否解锁,如果没有先解锁
2. 检测FLASH_SR寄存器的BSY位,确认没有其它正在进行的编程操作
3. 设置FLASH_CR寄存器的PG位为1,在指定的地址写入要编程的半字
4. 等待BSY位变为0
5. 读出写入的地址并验证数据

Flash写操作函数:

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);                   // 字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);            // 半字
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);    // 字节

// 从指定的地址写入一个32位的数据
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)    //写字操作
{
  FLASH_Status status = FLASH_COMPLETE;
  __IO uint32_t tmp = 0;
  /* Check the parameters */
  assert_param(IS_FLASH_ADDRESS(Address));         //检查传入的地址 Address 是否为有效的
#ifdef STM32F10X_XL                                // 大容量
  if(Address < FLASH_BANK1_END_ADDRESS - 2)
  { 
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank1Operation(ProgramTimeout); 
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new first 
        half word */
      FLASH->CR |= CR_PG_Set;                    // 使能CR寄存器的bit0 开启编程功能
      *(__IO uint16_t*)Address = (uint16_t)Data;    // 将32位数据的低16位写入指定地址
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation(ProgramTimeout);
      if(status == FLASH_COMPLETE)
      {
        /* if the previous operation is completed, proceed to program the new second 
        half word */
        tmp = Address + 2;                        // 地址偏移+2
        *(__IO uint16_t*) tmp = Data >> 16;        // 将32位数据的高16位写入指定地址
        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation(ProgramTimeout);
        /* Disable the PG Bit */
        FLASH->CR &= CR_PG_Reset;                 // 清空编程使能位
      }
      else
      {
        /* Disable the PG Bit */
        FLASH->CR &= CR_PG_Reset;
       }
    }
  }
  else if(Address == (FLASH_BANK1_END_ADDRESS - 1))
  {
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank1Operation(ProgramTimeout);
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new first 
        half word */
      FLASH->CR |= CR_PG_Set;
      *(__IO uint16_t*)Address = (uint16_t)Data;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank1Operation(ProgramTimeout);
	  /* Disable the PG Bit */
      FLASH->CR &= CR_PG_Reset;
    }
    else
    {
      /* Disable the PG Bit */
      FLASH->CR &= CR_PG_Reset;
    }
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank2Operation(ProgramTimeout);
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new second 
      half word */
      FLASH->CR2 |= CR_PG_Set;
      tmp = Address + 2;
      *(__IO uint16_t*) tmp = Data >> 16;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank2Operation(ProgramTimeout);
      /* Disable the PG Bit */
      FLASH->CR2 &= CR_PG_Reset;
    }
    else
    {
      /* Disable the PG Bit */
      FLASH->CR2 &= CR_PG_Reset;
    }
  }
  else
  {
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastBank2Operation(ProgramTimeout);
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new first 
        half word */
      FLASH->CR2 |= CR_PG_Set;
      *(__IO uint16_t*)Address = (uint16_t)Data;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank2Operation(ProgramTimeout);
      if(status == FLASH_COMPLETE)
      {
        /* if the previous operation is completed, proceed to program the new second 
        half word */
        tmp = Address + 2;
        *(__IO uint16_t*) tmp = Data >> 16;
        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastBank2Operation(ProgramTimeout);
        /* Disable the PG Bit */
        FLASH->CR2 &= CR_PG_Reset;
      }
      else
      {
        /* Disable the PG Bit */
        FLASH->CR2 &= CR_PG_Reset;
      }
    }
  }
#else                                                // 非大容量
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(ProgramTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to program the new first 
    half word */
    FLASH->CR |= CR_PG_Set;
    *(__IO uint16_t*)Address = (uint16_t)Data;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(ProgramTimeout);
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new second 
      half word */
      tmp = Address + 2;
      *(__IO uint16_t*) tmp = Data >> 16;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation(ProgramTimeout);
      /* Disable the PG Bit */
      FLASH->CR &= CR_PG_Reset;
    }
    else
    {
      /* Disable the PG Bit */
      FLASH->CR &= CR_PG_Reset;
    }
  }         
#endif /* STM32F10X_XL */
  /* Return the Program Status */
  return status;
}

半字的操作,就是在字的操作上,少了存入高16位的操作和地址偏移+2

FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data) // 写入一个字节数据
{
  FLASH_Status status = FLASH_COMPLETE;
  /* Check the parameters */
  assert_param(IS_OB_DATA_ADDRESS(Address));
  status = FLASH_WaitForLastOperation(ProgramTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* Authorize the small information block programming */
    FLASH->OPTKEYR = FLASH_KEY1;                            // 获取操作寄存器权限
    FLASH->OPTKEYR = FLASH_KEY2;
    /* Enables the Option Bytes Programming operation */
    FLASH->CR |= CR_OPTPG_Set;                              // 设置烧写选择字节位
    *(__IO uint16_t*)Address = Data;                        // 写入数据
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(ProgramTimeout);
    if(status != FLASH_TIMEOUT)
    {
      /* if the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= CR_OPTPG_Reset;
    }
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

6、Flash状态获取函数

FLASH_Status FLASH_GetStatus(void);                                        //获取 FLASH 状态
typedef enum {

        FLASH_BUSY = 1,//忙

        FLASH_ERROR_PG,//编程错误

        FLASH_ERROR_WRP,//写保护错误

        FLASH_COMPLETE,//操作完成

        FLASH_TIMEOUT//操作超时

} FLASH_Status;
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout)        //等待操作完成函数

7、STM32 Flash操作

7.1 写操作

写半字操作

传入带写入flash的起始地址、带写入的数据指针、写入数据

void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
} 

写字操作

传入起始地址、传入写入数据

void Test_Write(u32 WriteAddr,u16 WriteData)   	
{
	STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 
}

从指定地址写入指定长度数据

传入起始地址(此地址必须为2的倍数)

数据指针,这个指针中存放了待写入数据的起始地址

传入半字(16位数据)的个数

#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	FLASH_Lock();//上锁
}

7.2 读操作

读半个字

传入读地址(此地址必须位2的倍数)

u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}

从指定长度开始读出指定长度的数据

传入起始地址、数据指针、半字数据的个数

void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}

8、Flash寄存器

8.1 内存访问控制寄存器(FLASH_ACR)

8.2 FPEC键寄存器(FLASH_KEYR)  

8.3 闪存OPTKEY寄存器(FLASH_OPTKEYR)  

8.4 闪存状态寄存器(FLASH_SR)

8.5 闪存控制寄存器(FLASH_CR)

8.6 闪存地址寄存器(FLASH_AR)  

8.7 选择字节寄存器(FLASH_OBR)  

8.8 写保护寄存器(FLASH_WRPR)