目录
STM32的内部FLASH简介
内部FLASH的构成
主存储器
系统存储区
对内部FLASH的写入过程
解锁
擦除扇区
写入数据
操作内部FLASH的库函数
FLASH解锁、上锁函数
设置操作位数及擦除扇区
写入数据
实验源码
STM32的内部FLASH简介
在STM32芯片内部有一个FLASH(nor Flash)存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中,由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行:
除了使用外部的工具(如下载器)读写内部FLASH外, STM32芯片在运行的时候,也能对自身的内部FLASH进行读写,因此,若内部FLASH存储了应用程序后还有剩余的空间,我们可以把它像外部SPI-FLASH那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。由于访问内部FLASH的速度要比外部的SPI-FLASH快得多,所以在紧急状态下常常会使用内部FLASH存储关键记录;为了防止应用程序被抄袭,有的应用会禁止读写内部FLASH中的内容,或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部FLASH的操作。
STM32的内部FLASH包含主存储器、系统存储器以及选项字节区域,它们的地址分布及大小如下:
内部FLASH的构成
主存储器
般我们说STM32内部FLASH的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的256K FLASH、512K FLASH都是指这个区域的大小。
主存储器分为256页,每页大小为2KB,共512KB。这个分页的概念,实质就是FLASH存储器的扇区,与其它FLASH一样,在写入数据前,要先按页(扇区)擦除。
注意上表中的主存储器是本实验板使用的STM32ZET6型号芯片的参数,即STM32F1大容量产品。若使用超大容量、中容量或小容量产品,它们主存储器的页数量、页大小均有不同,使用的时候要注意区分。
系统存储区
系统存储区是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB以及CAN等ISP烧录功能。
选项字节
选项字节用于配置FLASH的读写保护、待机/停机复位、软件/硬件看门狗等功能,这部分共16字节。可以通过修改FLASH的选项控制寄存器修改。
对内部FLASH的写入过程
解锁
由于内部FLASH空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会结FLASH上锁,这个时候不允许设置FLASH的控制寄存器,并且不能对修改FLASH中的内容。
所以对FLASH写入数据前,需要先给它解锁。解锁的操作步骤如下:
往Flash密钥寄存器 FLASH_KEYR中写入KEY1 = 0x45670123
再往Flash密钥寄存器FLASH KEYR中写入KEY2 = 0xCDEF89AB
擦除扇区
在写入新的数据前,需要先擦除存储区域,STM32提供了扇区擦除指令和整个FLASH擦除(批量擦除)的指令,批量擦除指令仅针对主存储区。扇区擦除的过程如下:
检查FLASH_SR寄存器中的“忙碌寄存器位BSY”,以确认当前未执行任何Flash操作;
在FLASH_CR寄存器中,将“激活页擦除寄存器位PER”置1,
用FLASH_AR寄存器选择要擦除的页;
将FLASH_CR寄存器中的“开始擦除寄存器位STRT”置1,开始擦除;
等待BSY位被清零时,表示擦除完成
写入数据
擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:
检查FLASH_SR 中的BSY位,以确认当前未执行任何其它的内部Flash操作;
将FLASH_CR寄存器中的“激活编程寄存器位PG”置1;
向指定的FLASH存储器地址执行数据写入操作,每次只能以16位的方式写入;
等待BSY位被清零时,表示写入完成
操作内部FLASH的库函数
为简化编程, STM32标准库提供了一些库函数,它们封装了对内部FLASH写入数据操作寄存器的过程。
FLASH解锁、上锁函数
#define FLASH KEY1 ((uint32_t) 0x45670123)
#define FLASH KEY2 ((uint32 t) 0xCDEF89AB)
/** @brief Unlocks the FLASH control register access
* @param None
*@retval Nonevoid
*/
Void FLASH_Unlock (void)
{
if ((FLASH->CR & FLASH_CR_LOCK) != RESET)
{
/* Authorize the FLASH Registers access */
FLASH->KEYR = FLASH KEY1;
FLASH->KEYR = FLASH KEY2;
}
}
/** @brief Locks the FLASH control register access
*@param None
*@retval Nonevoid
*/
Void FLASH_Lock (void)
{
/* Set the LOCK Bit to lock the FLASH Registers access */
FLASH->CR |= FLASH CR LOCK;
}
解锁的时候,它对FLASH KEYR寄存器写入两个解锁参数,上锁的时候,对FLASH_CR寄存器的FLASH_CR_LOCK。位置1。
设置操作位数及擦除扇区
解锁后擦除扇区时可调用FLASH_EraseSector完成:
/**@brief 擦除指定的页
*@param Page_Address:要擦除的页地址,
*@retval FLASH Status:
*可能的返回值: FLASH_BUSY, FLASH_ERROR_PG,
*FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH TIMEOUT.
*/
FLASH_Status FLASH_ErasePage
{
(uint32_t Page_Address)FLASH_Status status FLASH_COMPLETE;
/*检查参数*/
assert param (IS FLASH ADDRESS (Page Address));
/*...此处省略XL超大容量花片的控制部分*/
/*等待上一次操作完成*/
status = FLASH_waitForLastOperation (EraseTimeout)
if (status == FLASH_COMPLETE)
{
/*若上次操作完成,则开始页擦除*/
FLASH->CR |= CR PER_Set;
FLASH->AR = Page_Address;
FLASH->CRI= CR_STRT_Set;
/*等待操作完成*/
status = FLASH_WaitForLastOperation (EraseTimeout);
/*复位PER位*/
FLASH->CR 6= CR PER Reset;
}
/*返回擦除结果*/
return status;
}
写入数据
对内部FLASH写入数据不像对SDRAM操作那样直接指针操作就完成了,还要设置一系列的寄存器,利用FLASH_ProgramWord和FLASH_ProgramHalfWord函数可按字、半字节单位写入数据:
/**@brief 向指定的地址写入一个字的数据(32位)
*eparam Address:要写入的地址
*@param Data:要写入的数据
*@retval FLASH Status:
可能的返回值: FLASH_ERROR_PG,
FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ProgramWord (uint32_t Address, uint32_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
IO uint32_t tmp = 0;
/*检查参数*/
Assert_param (IS FLASH ADDRESS (Address));
/*此处省略XL超大容量芯片的控制部分*/
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation (ProgramTimeout);
if (status FLASH COMPLETE)
{
/*若上次操作完成,则开始页入低16位的数据(输入参数的第1部分) */
FLASH->CR | CR_PG_Set;
*(IO uint16_t*) Address = (uint16_t) Data;
/*等待上一次操作完成*/
status = FLASH WaitForLastOperation (ProgramTimeout);
if (status == FLASH_COMPLETE)
{
/*若上次操作完成,则开始页入高16位的数据(输入参数的第2部分) */
tmp = Address + 2;
*(IO uint16_t*) tmp Data >> 16;
/*等待操作完成*/
status = FLASH_waitForLastOperation (ProgramTimeout);
/*复位PG位*/
FLASH->CR & CR PG Reset;
}else
{
/*复位PG位*/
FLASH->CR &= CR_PG Reset;
}
}
/*返回写入结果*/
return status;
}
从函数代码可了解到,使用指针进行赋值操作前设置了PG寄存器位,在赋值操作后,调用了FLASH_WaitForLastOperation数等待写操作完毕。HalfWord和Byte操作宽度的函数执行过程类似。
实验源码
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
int main(void)
{
uint32_t *p;
/*配置系统中断分组为2位抢占2位响应*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*解锁Flash*/
FLASH_Unlock();
/*擦除指定的页 第10页*/
/*0x8000000是基地址1k等于1024一页是2k*/
FLASH_ErasePage(0x8000000+2*1024*10);
/*写入数据*/
FLASH_ProgramWord(0x8000000+2*1024*10,0x1234567);
/*提示语句*/
printf("写入完成\r\n");
/*上锁Flash*/
FLASH_Lock();
/*指向写入的数据地址*/
p = (uint32_t *)(0x8000000+2*1024*10);
/*读出数据*/
printf("读取的内容为:0x%x\r\n",*p);
/*死循环*/
while(1){
}
}
/************************************************************** END OF FILE ****/