STM32 flash

Flash 有以下几个寄存器,在地址0x40022000开始
在这里插入图片描述
上锁:设置 CR的bit8位为1
解锁:往KEYR连续写入KEY1,KEY2

在规格书上看到的page0 page1…,翻译成中文应该翻译成 “扇区”
对于小容量的STM32,一个扇区是1K字节,占地址是0x03FF(0xFF是256,0x3FF=4*0xFF=1024);大容量的STM32一个扇区是2K字节,为什么要清楚这个呢。因为flash的擦除是按扇区来的,一次至少擦除一个扇区。如果想擦除某个地址,必须计算出该地址在那个扇区。另外写入之前必须先擦除,那么如果只写入一个字节,则需要先读出该扇区的其它内容,然后擦除该扇区,再写回去。

//在stm32f1xx_hal_flash_ex.h文件中,根据不同型号,定义有扇区大小
#if defined(STM32F101x6) || defined(STM32F102x6) || defined(STM32F103x6) ||   defined(STM32F100xB) || defined(STM32F101xB) || defined(STM32F102xB) || defined(STM32F103xB)
#define FLASH_PAGE_SIZE          ((uint32_t)0x400)
#endif /
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F103xE) ||  \ defined(STM32F101xG) || defined(STM32F103xG) ||    defined(STM32F105xC) || defined(STM32F107xC)
#define FLASH_PAGE_SIZE          ((uint32_t)0x800)
#endif /

擦除某个地址之后的区域

uint32_t FLASH_If_Erase(uint32_t start)
{
  uint32_t NbrOfPages = 0;
  uint32_t PageError = 0;
  FLASH_EraseInitTypeDef pEraseInit;
  HAL_StatusTypeDef status = HAL_OK;
      /* Unlock the Flash to enable the flash control register access *************/ 
  HAL_FLASH_Unlock();
      /* Get the sector where start the user flash area */
      
  NbrOfPages = (USER_FLASH_END_ADDRESS - start)/FLASH_PAGE_SIZE;//计算扇区数量
      pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;//选择是按扇区擦除还是全部擦除
  pEraseInit.PageAddress = start;//开始地址
  pEraseInit.Banks = FLASH_BANK_1;
  pEraseInit.NbPages = NbrOfPages;//扇区数量
  status = HAL_FLASHEx_Erase(&pEraseInit, &PageError);//调用库函数擦除

  HAL_FLASH_Lock();
  if (status != HAL_OK)
  {
    return FLASHIF_ERASEKO;
  }
  return FLASHIF_OK;
}

按页擦除的程序, 如果要擦除多面,用下面的for语句循环调用FLASH_PageErase(address)

for(address = pEraseInit->PageAddress;  address < (pEraseInit->PageAddress + (pEraseInit->NbPages)*FLASH_PAGE_SIZE); address += FLASH_PAGE_SIZE)

在这里插入图片描述
整个flash都擦除的更简单一点

static void FLASH_MassErase(uint32_t Banks)
{
    /*Only bank1 will be erased*/
    SET_BIT(FLASH->CR, FLASH_CR_MER);   //置CR的MER=1
    SET_BIT(FLASH->CR, FLASH_CR_STRT);  //置CR的STRT=1开始擦除, 跳出后等待BSY=0,然后把MER清0
}

写入数据流程:CR的PG=1---->写入数据到指定地址------->返回上一级后等待BSY=0------->把PG清0,一次只写入半字(2个字节),循环调用可写入多字节。

void FLASH_Program_HalfWord(uint32_t Address, uint16_t Data)
{
    /* Proceed to program the new data */
    SET_BIT(FLASH->CR, FLASH_CR_PG);
  /* Write data in the address */
  *(__IO uint16_t*)Address = Data;
}

查看是否有被保护的扇区:
1、解锁后----->读出WRPR寄存器------>读出OBR中的RDPRT位---->读出OBR中的用户配置位bit2-5------判断WRPR寄存器中的位,为0的表示对应的页被保护了,bit0–0-3页,bit1—4-7页(不同型号1bit对应的页数是不一样的)…

设置保护或解除保护
1、读取当前的保护状态:读取WRPR—>读取OBR中的RDPRT位,表示保护等级(0级和1级)—>读出OBR中的用户配置位bit2-5—>解锁flash(如果CR的lock位为1,写KEY1和KEY2到KEYR解锁)----->解锁OPT(如果CR的OPTWRE位为1,写OPTKEY1和OPTKEY2到OPTKEYR解锁)----->擦除所有OPT选项字-------->写入新的选项字。

擦除所有OPT选项字:
读取OBR(0x4002 201C)中的RDPRT位,该位表示读保护等级(0级未保护和1级保护)----->置1 CR的OPTER位------>置1 CR的STRT位------>等待BSY=0------>清0 CR的OPTER位------->把读保护等级RDPRT写回去(等待BSY=0----->置1 CR 的OPTPG-------->把读保护等级写入RDP寄存器0x1FFF F8000-------->等待BSY=0------->清0 CR的OPTPG)

写入新选项字:
如果是要设置写保护,置1 CR的OPTPG---->(设置这4个字节寄存器WRP0…WRP3 ,小容量的只有WRP0, 这是16位寄存器,低8bit表示被设备保护的32页,即1bit设置4个页,对应的位为1表示未保护,为0则表示保护)----->清0 CR的OPTPG-------->如果是要设置不保护------>则WRP0…都写入0xFFFF---------->设置完写保护状态后,设置读保护状态-------设置读保护等级为0----->设置用户配置----->设置用户数据

更详细解说,参考https://www.cnblogs.com/pertor/p/9484663.html

另外可以参考:这里说到块跟页
http://blog.chinaunix.net/uid-20617446-id-3847242.html