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个字节. }
}