STM32 本身没有自带 EEPROM,但是 STM32 具有在应用编程(IAP:In Application Programming)功能,可以把它的 FLASH 当成 EEPROM 来使用。
该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。**注意:**小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是 0X08000000, B0、 B1 (BOOT0、BOOT1)都接 GND 的时候,就是从 0X08000000开始运行代码的。
1.2 信息块: 1.3 闪存存储器接口寄存器: 2. 闪存读取 闪存等待时间:
等待周期体现了系统时钟(SYSCLK)频率与闪存访问时间的关系: 0等待周期 0 < SYSCLK < 24MHz 2等待周期 48MHz < SYSCLK ≤ 72MHz 要从地址 addr,读取一个半字(半字为 16 为,字为 32 位),可以通过如下的语句读取: 将 addr 强制转换为 vu16 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。类似的,将上面的 vu16 改为 vu8,即可读取指定地址的一个字节。 3. 闪存的编程和擦除 3.1 FPEC 键寄存器(FLASH_KEYR) STM32 复位后, FPEC 模块是被保护的,不能写入 FLASH_CR 寄存器;通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和 KEY2),只有在写保护被解除后,我们才能操作相关寄存器。
STM32 闪存的编程每次必须写入 16 位(不能单纯的写入 8 位数据!), 当 FLASH_CR 寄存器的 PG 位为’ 1’时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据, FPEC 都会产生总线错误。在编程过程中(BSY 位为’ 1’ ),任何读写闪存的操作都会使 CPU暂停,直到此次闪存编程结束。同样, STM32 的 FLASH 在编程的时候,也必须要求其写入地址的 FLASH 是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入, 在 FLASH_SR 寄存器的 PGERR 位将得到一个警告。
(1)检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁; (3)设置 FLASH_CR 寄存器的 PG 位为’ 1’; (4) 等待 BSY 位变为’ 0’;
在 STM32 的 FLASH 编程的时候,要先判断缩写地址是否被擦除了。 STM32 的闪存擦除分为两种:页擦除和整片擦除。
(2)检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作; (4) 用 FLASH_AR 寄存器选择要擦除的页; (6) 等待 BSY 位变为’ 0’;
(1)PEC 键寄存器: FLASH_KEYR
寄存器主要用来解锁 FPEC,必须在该寄存器写入特定的序列(KEY1 和 KEY2)解锁后,才能对 FLASH_CR 寄存器进行写操作。
STRT 位:用于开始一次擦除操作。在该位写入 1 , 将执行一次擦除操作。 PG 位:用于选择编程操作,在往 FLASH 写数据的时候,该位需要置 1。
(4)存地址寄存器: FLASH_AR
4. 软件实现 #ifndef __STMFLASH_H__ #include "sys.h" #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不使能;1,使能)
#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据 //测试写入 #endif
//faddr:读地址(此地址必须为2的倍数!!) u16 STMFLASH_ReadHalfWord(u32 faddr) return *(vu16*)faddr; #if STM32_FLASH_WREN //如果使能了写 //WriteAddr:起始地址 //NumToWrite:半字(16位)数 { for(i=0;i<numtowrite;i++)</numtowrite;i++) FLASH_ProgramHalfWord(WriteAddr,pBuffer); } //从指定地址开始写入指定长度的数据 //pBuffer:数据指针 #if STM32_FLASH_SIZE<256 #else #endif
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) u32 secpos; //扇区地址 u16 secremain; //扇区内剩余地址(16位字计算) u32 offaddr; //去掉0X08000000后的地址
return;//非法地址 offaddr = WriteAddr - STM32_FLASH_BASE; //实际偏移地址. secoff = (offaddr % STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) if(NumToWrite<=secremain) while(1) STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 { } { for(i=0;i<secremain;i++) 复制 STMFLASH_BUF[i+secoff]=pBuffer; STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 else if(NumToWrite==secremain) else//写入未结束 secpos ++; //扇区地址增1 pBuffer += secremain; //指针偏移 NumToWrite -= secremain; //字节(16位)数递减 secremain = STM_SECTOR_SIZE/2;//下一个扇区还是写不完 secremain=NumToWrite;//下一个扇区可以写完了 }; }
//从指定地址开始读出指定长度的数据 //pBuffer:数据指针 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) u16 i; { ReadAddr+=2;//偏移2个字节. }
//WriteAddr:起始地址 void Test_Write(u32 WriteAddr,u16 WriteData) STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 4.2 测试
测试实现就是想 FLASH 特定的地址写入一定长度的数据,再读出来,类似 SPI 读取外部 FLASH 的操作。
读取的数据为:u8 TEXT_Buffer[]={“STM32 FLASH TEST”};
|