**主存储器:**分为4个16KB扇区、1个64KB扇区和7个128KB扇区。boot0/boot1都接GND时从0x08000000开始运行代码。
**系统存储器:**器件在系统存储器自举模式下从该存储器启动,主要存放芯片的bootloader代码,此代码出厂时固化在芯片内部了。用于给主存储器下载代码。当boot0接3.3V、boot1接GND时从此处开始运行,即进入串口下载模式。
**OTP区域:**512字节OTP(一次性可编程),用于存储用户数据。OTP区域还有额外的16个字节,用于锁定对应的OTP数据块。
**选项字节:**用于配置读写保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。
STM32F4的闪存编程由6个32位寄存器控制:
(1)FLASH访问控制寄存器(FLASH_ACR)
(2)FLASH秘钥寄存器(FLASH_KEYR)
(3)FLASH选项秘钥寄存器(FLASH_OPTKEYR)
(4)FLASH状态寄存器(FLASH_SR)
(5)FLASH控制寄存器(FLASH_CR)
(6)FLASH选项控制寄存器(FLASH_OPTCR)
STM32F4复位后,FLASH编程操作是被保护的,不能写入FLASH_CR。
FALSH_CR解锁步骤为:
(1)写0X45670123到FLASH_KEYR
(2)写0XCDEF89AB到FLASH_KEYR
STM32F4闪存的编程位数通过FLASH_CR的PSIZE字段设置,PSIZE的设置必须和电源电压匹配。
STM32F4标准编程步骤:
(1)检查FLASH_SR中的BSY位,确保当前未执行任何FLASH操作。
(2)将FLASH_CR寄存器中的PG位置1,激活FLASH编程。
(3)执行写入操作
(4)等待BSY位清零,完成一次编程。
扇区擦除步骤:
(1)检查FLASH_CR的LOCK是否解锁,如果没有先解锁
(2)检查FLASH_SR寄存器中的BSY位,确保当前未执行任何FLASH操作
(3)在FLASH_CR寄存器中,将SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)
(4)将FLASH_CR寄存器中的STRT位置1,触发擦除操作
(5)等待BSY位清零
#define FLASH_KEY1 ((uint32_t)0x45670123)
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)
#define FLASH_CR_LOCK ((uint32_t)0x80000000)
//解锁函数
void FLASH_Unlock(void)
{
if((FLASH->CR & FLASH_CR_LOCK) != RESET)
{
/* Authorize the FLASH Registers access */
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
}
//锁定函数
void FLASH_Lock(void)
{
/* Set the LOCK Bit to lock the FLASH Registers access */
FLASH->CR |= FLASH_CR_LOCK;
}
//写入双字
FLASH_Status FLASH_ProgramDoubleWord(uint32_t Address, uint64_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_DOUBLE_WORD;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint64_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
/* if the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Return the Program Status */
return status;
}
//写入字
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_WORD;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint32_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
/* if the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Return the Program Status */
return status;
}
//写入半字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_HALF_WORD;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint16_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
/* if the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Return the Program Status */
return status;
}
//写入字节
FLASH_Status FLASH_ProgramByte(uint32_t Address, uint8_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_BYTE;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint8_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
/* if the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Return the Program Status */
return status;
}
//FLASH_Sector:选择擦除扇区
//VoltageRange:选择电源电压范围
FLASH_Status FLASH_EraseSector(uint32_t FLASH_Sector, uint8_t VoltageRange);
FLASH_Status FLASH_EraseAllSectors(uint8_t VoltageRange);
FLASH_Status FLASH_EraseAllBank1Sectors(uint8_t VoltageRange);
FLASH_Status FLASH_EraseAllBank2Sectors(uint8_t VoltageRange);
FLASH_Status FLASH_GetStatus(void)
{
FLASH_Status flashstatus = FLASH_COMPLETE;
if((FLASH->SR & FLASH_FLAG_BSY) == FLASH_FLAG_BSY)
{
flashstatus = FLASH_BUSY;
}
else
{
if((FLASH->SR & FLASH_FLAG_WRPERR) != (uint32_t)0x00)
{
flashstatus = FLASH_ERROR_WRP;
}
else
{
if((FLASH->SR & FLASH_FLAG_RDERR) != (uint32_t)0x00)
{
flashstatus = FLASH_ERROR_RD;
}
else
{
if((FLASH->SR & (uint32_t)0xE0) != (uint32_t)0x00)
{
flashstatus = FLASH_ERROR_PROGRAM;
}
else
{
if((FLASH->SR & FLASH_FLAG_OPERR) != (uint32_t)0x00)
{
flashstatus = FLASH_ERROR_OPERATION;
}
else
{
flashstatus = FLASH_COMPLETE;
}
}
}
}
}
/* Return the FLASH Status */
return flashstatus;
}
返回值是通过枚举类型定义的
typedef enum
{
FLASH_BUSY = 1,//操作忙
FLASH_ERROR_RD,//读保护错误
FLASH_ERROR_PGS,//编程顺序错误
FLASH_ERROR_PGP,//编程并行位数错误
FLASH_ERROR_PGA,//编程对齐错误
FLASH_ERROR_WRP,//写保护错误
FLASH_ERROR_PROGRAM,//编程错误
FLASH_ERROR_OPERATION,//操作错误
FLASH_COMPLETE//操作结束
}FLASH_Status;
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后 读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。
FLASH_Status FLASH_WaitForLastOperation(void)
{
__IO FLASH_Status status = FLASH_COMPLETE;
/* Check for the FLASH Status */
status = FLASH_GetStatus();
/* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
Even if the FLASH operation fails, the BUSY flag will be reset and an error
flag will be set */
while(status == FLASH_BUSY)
{
status = FLASH_GetStatus();
}
/* Return the operation status */
return status;
}
//读取指定地址的字(32 位数据)
//faddr:读地址
//返回值:对应数据.
u32 STMFLASH_ReadWord(u32 faddr)
{ return *(u32*)faddr; }
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(4位)数
void STM32_FLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)
{
u32 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STM32_FLASH_ReadWord(ReadAddr);//读取4个字节.
ReadAddr+=4;//偏移4个字节.
}
}
//从指定地址开始写入指定长度的数据
//特别注意:因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数
// 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
// 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
// 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
//该函数对OTP区域也有效!可以用来写OTP区!
//OTP区域地址范围:0X1FFF7800~0X1FFF7A0F
//WriteAddr:起始地址(此地址必须为4的倍数!!)
//pBuffer:数据指针
//NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
void STM32_FLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
FLASH_Status status = FLASH_COMPLETE;
u32 addrx=0;
u32 endaddr=0;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
FLASH_Unlock(); //解锁
FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间,必须禁止数据缓存
addrx=WriteAddr; //写入的起始地址
endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
if(addrx<0X1FFF0000) //只有主存储区,才需要执行擦除操作!!
{
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STM32_FLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
status=FLASH_EraseSector(STM32_FLASH_GetFlashSector(addrx),VoltageRange_3);//VCC=2.7~3.6V之间!!
if(status!=FLASH_COMPLETE)break; //发生错误了
}else addrx+=4;
}
}
if(status==FLASH_COMPLETE)
{
while(WriteAddr<endaddr)//写数据
{
if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)//写入数据
{
break; //写入异常
}
WriteAddr+=4;
pBuffer++;
}
}
FLASH_DataCacheCmd(ENABLE); //FLASH擦除结束,开启数据缓存
FLASH_Lock();//上锁
}
//这个宏定义用法保存一下,挺好
const u8 text_buf[]="www.prechin.net";
#define TEXTLEN sizeof(text_buf)
#define TEXTSIZE (TEXTLEN/4+((TEXTLEN%4)?1:0))
讲解不到位的希望大家指出,有需要我讲解的部分,希望大家提出,我会出文档讲解。