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 // 大容量 每页是2K
if(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 STM32F103RBT6
secoff=(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++; //扇区地址增1
secoff=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个字节.
}
}