STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程。
读写FLASH的用途:利用程序存储器的剩余空间来保存掉电不丢失的用户数据 ,通过在程序中编程(IAP),实现程序的自我更新,在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序,在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序。
FPEC模块处理闪存的编程和擦除操作,它包括7个32位的寄存器:
FPEC键寄存器(FLASH_KEYR)
选择字节键寄存器(FLASH_OPTKEYR)
闪存控制寄存器(FLASH_CR)
闪存状态寄存器(FLASH_SR)
闪存地址寄存器(FLASH_AR)
选择字节寄存器(FLASH_OBR)
写保护寄存器(FLASH_WRPR)
使用指针读指定地址下的存储器:
uint16_t Data = *((__IO uint16_t *)(0x08000000));
使用指针写指定地址下的存储器:
*((__IO uint16_t *)(0x08000000)) = 0x1234;
其中:
#define __IO volatile
/**
* 函 数:FLASH读取一个32位的字
* 参 数:Address 要读取数据的字地址
* 返 回 值:指定地址下的数据
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
return *((__IO uint32_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
STM32—Flash读写详解_stm32写入flash-CSDN博客https://blog.csdn.net/zhuxinmingde/article/details/131972752?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-131972752-blog-123402701.235^v39^pc_relevant_3m_sort_dl_base1&spm=1001.2101.3001.4242.2&utm_relevant_index=4
下文代码中STMFLASH_Read(FLASH_ADDR,Temporary_storage,size);这里FLASH_ADDR是我们要读取的起始地址,Temporary_storage是16位的指针变量,存放我们读取到的内容,size是我们要读取的大小,值得注意的是,size是半字大小,也就是有多少个两个字节。比如我们要读取100个字节,size就可以填50。
FLASH的读取
直接读取某一地址的内容
因为读取FLASH并不需要解锁,我们可以直接用指针指向所读的地址,之后读取此地址的内容即可。
p = (uint32_t *)(0x08008000);
printf("\r\n读取内部FLASH该地址存储的内容为:0x%x",*p);
此程序就是先将0x08008000赋给指针变量P,之后将P指向地址的内容以16进制的格式输出出来。
读取选定位置的选定大小的内容
1. 首先我们编写一个函数,用以读取指定地址的半字(16位数据)。
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
2. 从指定地址开始读出指定长度的数据
LReadAddr:起始地址
pBuffer:数据指针
NumToWrite:半字(16位)数
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个字节.
}
}
当我们想读取FLASH内容时,只需要直接调用上面的函数即可。
共有三个键值:
RDPRT键 = 0x000000A5
RDP:读出保护选择字节
读出保护功能帮助用户保护存在闪存中的代码。该功能由设置信息块中的一个选择字节启用。
写入正确的数值(RDPRT键=0x00A5)到这个选择字节后,闪存被开放允许读出访问。
KEY1 = 0x45670123
KEY2 = 0xCDEF89AB
解除闪存锁
复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块,这个特定的序列是在FLASH_KEYR写入两个键值(KEY1和KEY2);错误的操作序列都会在下次复位前锁死FPEC模块和FLASH_CR寄存器。
写入错误的键序列还会产生总线错误;总线错误发生在第一次写入的不是KEY1,或第一次写入的是KEY1但第二次写入的不是KEY2时;FPEC模块和FLASH_CR寄存器可以由程序设置FLASH_CR寄存器中的LOCK位锁住,这时可以通过在FLASH_KEYR中写入正确的键值对FPEC解锁。
主闪存编程
对主闪存编程每次可以写入16位。当FLASH_CR寄存器的PG位为’1’时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据,FPEC都会产生总线错误。在编程过程中(BSY位为’1’),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。
标准的闪存编程顺序如下:(即上图详细过程文字描述)
检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作;
设置FLASH_CR寄存器的PG位为’1’;
在指定的地址写入要编程的半字;
等待BSY位变为’0’;
读出写入的地址并验证数据。
注意: 当FLASH_SR寄存器的BSY位为’1’时,不能对任何寄存器执行写操作。
这种模式下CPU以标准的写半字的方式烧写闪存,FLASH_CR寄存器的PG位必须置’1’。FPEC先读出指定地址的内容并检查它是否被擦除,如未被擦除则不执行编程并在FLASH_SR寄存器的PGERR位提出警告(唯一的例外是当要烧写的数值是0x0000时,0x0000可被正确烧入且PGERR位不被置位);如果指定的地址在FLASH_WRPR中设定为写保护,则不执行编程并在FLASH_SR寄存器的WRPRTERR位置’1’提出警告。FLASH_SR寄存器的EOP为’1’时表示编程结束。
闪存擦除
页擦除
闪存的任何一页都可以通过FPEC的页擦除功能擦除;擦除一页应遵守下述过程:
检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
设置FLASH_CR寄存器的PER位为’1’;
用FLASH_AR寄存器选择要擦除的页;
设置FLASH_CR寄存器的STRT位为’1’;
等待BSY位变为’0’;
读出被擦除的页并做验证。
整片擦除
可以用整片擦除功能擦除所有用户区的闪存,信息块不受此操作影响。建议使用下述过程:
检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
设置FLASH_CR寄存器的MER位为’1’;
设置FLASH_CR寄存器的STRT位为’1’;
等待BSY位变为’0’;
读出所有页并做验证。
选择字节说明:
选择字节共有8个字节,由用户根据应用的需要配置;例如:可以选择使用硬件模式的看门狗或软件的看门狗。
在选择字节中每个32位的字被划分为下述格式:
每次系统复位后,选择字节装载器读出信息块的数据并保存在寄存器中;每个选择位都在信息块中有它的反码位,在装载选择位时反码位用于验证选择位是否正确,如果有任何的差别,将产生一个选择字节错误标志(OPTERR)。当发生选择字节错误时,对应的选择字节被强置为0xFF。当选择字节和它的反码均为0xFF时(擦除后的状态),则关闭上述验证功能。
•RDP:写入RDPRT键(0x000000A5)后解除读保护
•USER:配置硬件看门狗和进入停机/待机模式是否产生复位
•Data0/1:用户可自定义使用
•WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)
STM32单片机bootloader扫盲_stm32 bootloader-CSDN博客https://blog.csdn.net/weixin_42378319/article/details/120896348
BootLoader和APP之间的关系
APP就是平时写的单片机上的应用程序,而BootLoader本质上和APP一样,也是平时写的应用程序。BootLoader只不过是拥有从外部接收数据,更新Flash(也就是APP),跳转至APP功能的特殊APP罢了。
以STM32F103为例,如果没有BootLoader,flash分布就如下图左半部分。如果有BootLoader,就如下图右半部分,将flash分为两部分(这里举例用0x800 4000做分界线),存储了两个应用程序(BootLoader和APP)