STM32学习笔记一一FLASH 模拟 EEPROM

发布者:科技梦行者最新更新时间:2019-01-09 来源: eefocus关键字:STM32  FLASH  模拟  EEPROM 手机看文章 扫描二维码
随时随地手机看文章

1. 简述

STM32 本身没有自带 EEPROM,但是 STM32 具有在应用编程(IAP:In Application Programming)功能,可以把它的 FLASH 当成 EEPROM 来使用。


不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了1024K 字节。MiniSTM32 开发板选择的 STM32F103RCT6 的 FLASH 容量为 256K 字节,属于大容量产品,闪存模块组织如下图:


在这里插入图片描述


1.1 主存储器:

该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。**注意:**小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是 0X08000000, B0、 B1 (BOOT0、BOOT1)都接 GND 的时候,就是从 0X08000000开始运行代码的。


在这里插入图片描述


1.2 信息块:

该部分分为 2 个小部分,其中启动程序代码(2K),用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3, B1 接 GND 的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能.


1.3 闪存存储器接口寄存器:

该部分用于控制闪存读写等,是整个闪存模块的控制机构。


2. 闪存读取

内置闪存模块可以在通用地址空间直接寻址,任何 32 位数据的读操作都能访问闪存模块的内容并得到相应的数据。读接口在闪存端包含一个读控制器,还包含一个 AHB 接口与 CPU 衔接,这个接口的主要工作是产生读闪存的控制信号并预取 CPU 要求的指令块,预取指令块仅用于在 I-Code 总线上的取指操作,数据常量是通过 D-Code 总线访问的。这两条总线的访问目标是相同的闪存模块,访问 D-Code 将比预取指令优先级高。


闪存等待时间:


因为 CPU 运行速度比 FLASH 快得多, STM32F103的 FLASH 最快访问速度≤24Mhz,如果 CPU 频率超过这个速度,那么必须加入等待时间,比如我们一般使用 72Mhz的主频,那么 FLASH等待周期就必须设置为 2,该设置通过 FLASH_ACR寄存器设置。


等待周期体现了系统时钟(SYSCLK)频率与闪存访问时间的关系:

等待周期 系统时钟

0等待周期 0 < SYSCLK < 24MHz

1等待周期 24MHz < SYSCLK ≤ 48MHz

2等待周期 48MHz < SYSCLK ≤ 72MHz

读取地址设置:

要从地址 addr,读取一个半字(半字为 16 为,字为 32 位),可以通过如下的语句读取:

data=(vu16)addr;

将 addr 强制转换为 vu16 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。类似的,将上面的 vu16 改为 vu8,即可读取指定地址的一个字节。

对于指针的解释,可学习求求你,不要再纠结指针了,通过与别人的交流讨论,对于提高对知识的理解是大有裨益的!


3. 闪存的编程和擦除

STM32 的闪存编程是由 FPEC(闪存编程和擦除控制器) 模块处理的,这个模块包含 7 个32 位寄存器。


3.1 FPEC 键寄存器(FLASH_KEYR)

FPEC 键寄存器总共有 3 个键值:RDPRT 键=0X000000A5;KEY1=0X45670123;KEY2=0XCDEF89AB。


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 位将得到一个警告。


3.2 STM32 闪存编程过程

(1)检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁;

(2)检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的编程操作;

(3)设置 FLASH_CR 寄存器的 PG 位为’ 1’;

(3)在指定的地址写入要编程的半字;

(4) 等待 BSY 位变为’ 0’;

(5)读出写入的地址并验证数据。


在这里插入图片描述


3.3 STM32 闪存页擦除过程

在 STM32 的 FLASH 编程的时候,要先判断缩写地址是否被擦除了。 STM32 的闪存擦除分为两种:页擦除和整片擦除。


(1)检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁;

(2)检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作;

(3)设置 FLASH_CR 寄存器的 PER 位为’ 1’;

(4) 用 FLASH_AR 寄存器选择要擦除的页;

(5) 设置 FLASH_CR 寄存器的 STRT 位为’ 1’;

(6) 等待 BSY 位变为’ 0’;

(7)读出被擦除的页并做验证。


在这里插入图片描述


3.4 FLASH相关寄存器介绍

(1)PEC 键寄存器: FLASH_KEYR


在这里插入图片描述


寄存器主要用来解锁 FPEC,必须在该寄存器写入特定的序列(KEY1 和 KEY2)解锁后,才能对 FLASH_CR 寄存器进行写操作。


(2)闪存控制寄存器: FLASH_CR


在这里插入图片描述


LOCK 位:用于指示 FLASH_CR 寄存器是否被锁住,该位在检测到正确的解锁序列后,硬件将其清零。在一次不成功的解锁操作后,在下次系统复位之前,该位将不再改变。

STRT 位:用于开始一次擦除操作。在该位写入 1 , 将执行一次擦除操作。

PER 位:用于选择页擦除操作,在页擦除的时候,需要将该位置 1。

PG 位:用于选择编程操作,在往 FLASH 写数据的时候,该位需要置 1。


(3)闪存状态寄存器: FLASH_SR


在这里插入图片描述


该寄存器主要用来指示当前 FPEC 的操作编程状态。


(4)存地址寄存器: FLASH_AR


在这里插入图片描述


4. 软件实现

4.1 FLASH操作

#ifndef __STMFLASH_H__

#define __STMFLASH_H__

#include "sys.h"

#define STM32_FLASH_SIZE 256 //所选STM32的FLASH容量大小(单位为K)

#define STM32_FLASH_WREN 1              //使能FLASH写入(0,不使能;1,使能)


//FLASH起始地址

#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址


u16 STMFLASH_ReadHalfWord(u32 faddr);   //读出半字  

void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据

u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度数据

void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据

void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);    //从指定地址开始读出指定长度的数据


//测试写入

void Test_Write(u32 WriteAddr,u16 WriteData);    

#endif


//读取指定地址的半字(16位数据)

//faddr:读地址(此地址必须为2的倍数!!)

//返回值:对应数据.

u16 STMFLASH_ReadHalfWord(u32 faddr)

{

return *(vu16*)faddr; 

}

#if STM32_FLASH_WREN //如果使能了写   

//不检查的写入

//WriteAddr:起始地址

//pBuffer:数据指针

//NumToWrite:半字(16位)数   

void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   

{  

u16 i;

for(i=0;i

{

FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);

    WriteAddr+=2;//地址增加2.

}  

//从指定地址开始写入指定长度的数据

//WriteAddr:起始地址(此地址必须为2的倍数!!)

//pBuffer:数据指针

//NumToWrite:半字(16位)数(就是要写入的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

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

{

if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除    

}

if(i

{

FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区

for(i=0;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();//上锁

}



#endif


//从指定地址开始读出指定长度的数据

//ReadAddr:起始地址

//pBuffer:数据指针

//NumToWrite:半字(16位)数

void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   

{

u16 i;

for(i=0;i

{

pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.

ReadAddr+=2;//偏移2个字节.

}

}


//////////////////////////////////////////////////////////////////////////////////////////////////////

//WriteAddr:起始地址

//WriteData:要写入的数据

void Test_Write(u32 WriteAddr,u16 WriteData)   

{

STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 

}


4.2 测试


在这里插入图片描述


测试实现就是想 FLASH 特定的地址写入一定长度的数据,再读出来,类似 SPI 读取外部 FLASH 的操作。


现象:


在这里插入图片描述


读取的数据为:u8 TEXT_Buffer[]={“STM32 FLASH TEST”};



关键字:STM32  FLASH  模拟  EEPROM 引用地址:STM32学习笔记一一FLASH 模拟 EEPROM

上一篇:STM32学习笔记一一HEX文件和BIN文件格式
下一篇:STM32程序移植技巧总结

推荐阅读最新更新时间:2024-03-16 16:22

MCU内嵌Flash内存成趋势 出货比重过半
因应MCU成长快速及程序数据储存需要,MCU内嵌Flash内存设计成为主流趋势,MCU大厂也纷纷以购并或结盟掌握内嵌Flash的相关IP与制程技术。本文将探讨内嵌Flash IP制程技术,为下一代Flash MCU带来的技术变革。 Flash MCU出货比重过半 掌握Flash成为MCU开发关键 因应MCU成长快速,所带动的程序代码与数据储存需要,MCU内嵌内存类型也从早期Maskrom、EPROM、EEPROM到Flash内存。据市调机构预估,内嵌Flash内存的MCU在2010年出货比重超过50%。也因此MCU大厂也急于掌握内嵌Flash内存相关IP与制程技术。像Microchip就购并闪存大厂SST。提供MCU内
[手机便携]
基于STM32单片机的雷管电子保险装置设计
雷管使用中如果引爆系统屏蔽不够完善,使雷管中流过泄漏电流和电容电流达到一定的数值和作用时间,电流转化成足够的热能达到雷管炸药点燃温度(约180℃)时即可引爆,电雷管耐静电压为(1~3)×104V,超过(1~3)×104V 的静电压,从而引爆电雷管,造成爆破材料发生意外爆炸事故。本课题是在参阅了国内外关于雷管安全保险发展技术资料的基础上进行的国内雷管防护措施开发和研制的一次有益的尝试和探索,简要介绍了STM32F103RBT6 的主要功能和性能,完成了系统整体设计。 1.STM32F103RBT6简介 STM32F103RBT6 是一种高性能32 位微控制器(MICrocontrollerUnit),是意法半导体公司
[单片机]
基于<font color='red'>STM32</font>单片机的雷管电子保险装置设计
STM32-FSMC-LCD详解
LCD有如下控制线: CS:Chip Select 片选,低电平有效 RS:Register Select 寄存器选择 WR:Write 写信号,低电平有效 RD:Read 读信号,低电平有效 RESET:重启信号,低电平有效 DB0-DB15:数据线 假如这些线,全部用普通IO口控制。根据LCD控制芯片手册(大部分控制芯片时序差不多): 如果情况如下: DB0-DB15的IO全部为1(表示数据0xff),也可以为其他任意值,这里以0xff为例。 CS为0(表示选上芯片,CS拉低时,芯片对传入的数据才会有效) RS为1(表示DB0-15上传递的是要被写到寄存器的值),如果为0,表示传递的是数据。 WR为0,RD为1(表示是写动作)
[单片机]
STM32时钟初始化函数SystemInit()详解
花了一天的时间,总算是了解了SystemInit()函数实现了哪些功能,初学STM32,,现记录如下(有理解错误的地方还请大侠指出): 使用的是3.5的库,用的是STM32F107VC,开发环境RVMDK4.23 我已经定义了STM32F10X_CL,SYSCLK_FREQ_72MHz 函数调用顺序: startup_stm32f10x_cl.s(启动文件) → SystemInit() → SetSysClock () → SetSysClockTo72() 初始化时钟用到的RCC寄存器复位值: RCC_CR = 0x0000 xx83; RCC_CFGR = 0x0000 0000;RCC_CIR = 0x0000 0000;
[单片机]
<font color='red'>STM32</font>时钟初始化函数SystemInit()详解
STM32 DMA 多通道
ADC_init.C #define ADC1_DR_Address ((uint32_t)0x4001244C) extern vu16 After_filter ; //用来存放求平均值之后的结果 extern vu16 ADCConvertedValue ; DMA_InitTypeDef DMA_InitStructure; //DMA总线枚举类型 ADC_InitTypeDef ADC_InitStructure; //ADC 枚举类型 /******************************************************************************* * 函数名称: GP
[单片机]
使用微控制器集成模拟比较器以提供电源保护并降低电路板空间
简介 现在,越来越多的设计师开始转向电子微控制器,以在电机控制和数字电源系统中控制功率级。 使用微控制器(例如德州仪器 (TI) 的 C2000™ Piccolo™ 微控制器)的集成模拟比较器功能可以保护系统电源,同时也可使设计师减少板级所需的外部模拟组件的数量。在此类电机控制和数字电源系统中,在微控制器自身发生执行错误的情况下防止发生过压或欠压时,设计师仍局限于模拟域。 通过使用 TI C2000 Piccolo 微控制器系列的集成模拟功能,可以围绕单个控制器来设计系统,而不需要外部支持电路。这主要涉及使用模拟比较器来监控功率级模拟域中的过压或欠压以及过流或欠流事件。 Piccolo 微处理器的优势 Piccolo 微控制
[传感器]
使用微控制器集成<font color='red'>模拟</font>比较器以提供电源保护并降低电路板空间
STM32开发板中如何点亮一个LCD
网络上配套STM32开发板有很多LCD例程,主要是TFT LCD跟OLED的。从这些例程,大家都能学会如何点亮一个LCD。但这代码都有下面问题: 分层不清晰,通俗讲就是模块化太差。 接口乱。只要接口不乱,分层就会好很多了。 可移植性差。 通用性差。 为什么这样说呢?如果你已经了解了LCD的操作,请思考如下情景: 1、代码空间不够,只能保留9341的驱动,其他LCD驱动全部删除。能一键(一个宏定义)删除吗?删除后要改多少地方才能编译通过? 2、有一个新产品,收银设备。系统有两个LCD,都是OLED,驱动IC相同,但是一个是128x64,另一个是128x32像素,一个叫做主显示,收银员用;一个叫顾显,顾客看金额。怎么办?这些例
[单片机]
瑞萨e2studio----串口获取数据通过SPI存储于W25Q128外部flash
1.概述 本篇文章主要介绍如何使用e2studio对瑞萨进行spi配置,同时移植stm32上的W25Q128到瑞萨上,同时通过对该FLASH进行读写操作,验证是否正确。 2.硬件准备 首先需要准备一个开发板,这里我准备的是芯片型号 R7FA2L1AB2DFL 的开发板。 3.新建工程 4.工程模板 5.保存工程路径 6.芯片配置 本文中使用R7FA2L1AB2DFL来进行演示。 7 7.工程模板选择 8.SPI配置 点击Stacks- New Stack- Driver- Connectivity- SPI Driver on r_spi。 9.S
[单片机]
瑞萨e2studio----串口获取数据通过SPI存储于W25Q128外部<font color='red'>flash</font>
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved