第四章 STM32 Flash

embedded/2025/3/5 6:00:01/

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                                        // 大容量 每页是2Kif(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 STM32F103RBT6secoff=(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++;				//扇区地址增1secoff=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)  


http://www.ppmy.cn/embedded/170109.html

相关文章

【Flutter】正方形的Dialog

以为很简单&#xff0c;showDialog&#xff0c;里面再使用Dialog就行&#xff0c;结果Dialog有最小宽度限制&#xff0c;这个最小宽度比UI的宽度还要大&#xff0c;使用UnconstrainedBox又各种包裹Container还是不行&#xff0c;最终决定放弃Dialog&#xff0c;直接使用Contain…

将 XML 文件转换为字典形式

在 Python 中&#xff0c;可以使用 xml.etree.ElementTree 或 lxml 库来解析 XML 文件并将其转换为字典形式。 1、问题背景 您有一个 XML 文件&#xff0c;其中的数据结构如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <form str…

如何使用 Ollama 的 API 来生成聊天

如何使用 Ollama 的 API 来生成聊天 简介 生成聊天 生成聊天的示例 加载模型 卸载模型 简介 Ollama 提供了一个 RESTful API&#xff0c;允许开发者通过 HTTP 请求与 Ollama 服务进行交互。这个 API 覆盖了所有 Ollama 的核心功能&#xff0c;包括模型管理、运行和监控。本…

Python元类(Metaclass)

引言 Python中的**元类(Metaclass)**是一个高级且强大的概念,它允许开发者控制类的创建行为。尽管元类在日常开发中并不常见,但在某些场景下(如框架开发、ORM实现等),元类可以发挥重要作用。本文将深入探讨元类的概念、工作原理、常见用法以及实际应用场景,帮助你全面…

如何在Python用Plot画出一个简单的机器人模型

如何在Python中使用 Plot 画出一个简单的模型 在下面的程序中&#xff0c;首先要知道机器人的DH参数&#xff0c;然后计算出每一个关节的位置&#xff0c;最后利用 plot 函数画出关节之间的连杆就可以了&#xff0c;最后利用 animation 库来实现一个动画效果。 import matplo…

Python 图像处理之 Pillow 库:玩转图片

哈喽,大家好,我是木头左! Pillow 库作为 Python 图像处理的重要工具之一,为提供了便捷且功能丰富的接口,让能够轻松地对图像进行各种操作,从简单的裁剪、旋转到复杂的滤镜应用、图像合成等,几乎无所不能。接下来,就让一起深入探索如何使用 Pillow 库来处理图片,开启一…

【Python项目】基于Python的书籍售卖系统

【Python项目】基于Python的书籍售卖系统 技术简介&#xff1a;采用Python技术、MYSQL数据库等实现。 系统简介&#xff1a;书籍售卖系统是一个基于B/S结构的在线图书销售平台&#xff0c;主要分为前台和后台两部分。前台系统功能模块分为&#xff08;1&#xff09;用户中心模…

将 SSH 密钥添加到 macOS 的钥匙串中

git提交代码时&#xff0c;如果SSH密码并未免密&#xff0c;每次拉取&#xff0c;上传操作时都需要密码输入&#xff0c; 可将SSH密钥添加到钥匙串中 git config --global credential.helper store报错&#xff1a; WARNING: The -K and -A flags are deprecated and have bee…