stm32Flash模拟eeprom心得(原创)
STM32之EEPROM驱动
![STM32之EEPROM驱动](https://img.taocdn.com/s3/m/68c21f36a4e9856a561252d380eb6294dc88225f.png)
STM32之EEPROM驱动本⽂介绍如何使⽤STM32标准外设库驱动EEPROM,本例程驱动的EEPROM为AT24C02,通讯协议为IIC,使⽤IO⼝模拟⽅式。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. EEPROM简介EEPROM全称为EEPROM(Electrically Erasable Programmable Read Only Memory)是电可擦除可编程只读存储器。
虽然名称为只读存储器,但是擦除和写⼊都是直接使⽤电路控制,不需要再使⽤外部设备来擦写,即设备在运⾏过程中即可随时擦除和写⼊。
可以按字节为单位修改数据,⽆需整个芯⽚擦除,且掉电后数据不丢失,⼀般⽤来存储⼀些配置信息,以便系统重新上电的时候加载。
2. 常⽤EEPROM⼀般常⽤的EEPROM为ATMEL公司(已被Microchip收购)的AT24Cxx系列,常⽤容量从1K到64Kbit不等,换算成字节为128到8K Bytes,可以根据项⽬需求和价格综合考虑选型。
3. EEPROM操作说明AT24C02容量为2Kbit,即256Byte,地址范围为0~255,即0~0xFF,使⽤1个字节即可表⽰,因此地址长度为1字节。
3.1. 通讯⽅式IAT24C02使⽤IIC协议跟MCU通讯。
3.2. 设备地址如果仅接⼊⼀个AT24C02,可以将设备的A0、A1、A2引脚全部接⼊低电平,那么此时该设备的地位为0x50,再增加⼀位读写标志位,最终读取操作时地址为0xA1,写⼊操作时地址为0xA0。
3.3. 读取数据读取当前字节:MCU直接发起读操作,设备返回当前字节,当前字节⾃动加1,该操作较少使⽤。
读取指定地址⼀个字节:MCU先向AT24C02写⼊⼀个地址,然后再发起⼀个读操作,AT24C02返回该地址存储的字节。
连续读取:MCU发起读当前字节,或者读指定地址字节,设备返回数据,MCU发送ACK,设备继续返回后续地址数据,直到MCU发送NACK,设备不再返回数据。
STM32学习笔记:读写内部Flash(介绍+附代码)
![STM32学习笔记:读写内部Flash(介绍+附代码)](https://img.taocdn.com/s3/m/e29da3acf424ccbff121dd36a32d7375a417c6c1.png)
STM32学习笔记:读写内部Flash(介绍+附代码)⼀、介绍⾸先我们需要了解⼀个内存映射:stm32的flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯⽚实际的flash⼤⼩,不同的芯⽚flash⼤⼩不同。
RAM起始地址是0x2000 0000,结束地址是0x2000 0000加上芯⽚的RAM⼤⼩。
不同的芯⽚RAM也不同。
Flash中的内容⼀般⽤来存储代码和⼀些定义为const的数据,断电不丢失,RAM可以理解为内存,⽤来存储代码运⾏时的数据,变量等等。
掉电数据丢失。
STM32将外设等都映射为地址的形式,对地址的操作就是对外设的操作。
stm32的外设地址从0x4000 0000开始,可以看到在库⽂件中,是通过基于0x4000 0000地址的偏移量来操作寄存器以及外设的。
⼀般情况下,程序⽂件是从 0x0800 0000 地址写⼊,这个是STM32开始执⾏的地⽅,0x0800 0004是STM32的中断向量表的起始地址。
在使⽤keil进⾏编写程序时,其编程地址的设置⼀般是这样的:程序的写⼊地址从0x08000000(数好零的个数)开始的,其⼤⼩为0x80000也就是512K的空间,换句话说就是告诉编译器flash的空间是从0x08000000-0x08080000,RAM的地址从0x20000000开始,⼤⼩为0x10000也就是64K的RAM。
这与STM32的内存地址映射关系是对应的。
M3复位后,从0x08000004取出复位中断的地址,并且跳转到复位中断程序,中断执⾏完之后会跳到我们的main函数,main函数⾥边⼀般是⼀个死循环,进去后就不会再退出,当有中断发⽣的时候,M3将PC指针强制跳转回中断向量表,然后根据中断源进⼊对应的中断函数,执⾏完中断函数之后,再次返回main函数中。
⼤致的流程就是这样。
1.1、内部Flash的构成:STM32F429 的内部 FLASH 包含主存储器、系统存储器、 OTP 区域以及选项字节区域,它们的地址分布及⼤⼩如下:STM32F103的中容量内部 FLASH 包含主存储器、系统存储器、 OTP 区域以及选项字节区域,它们的地址分布及⼤⼩如下:注意STM32F105VC的是有64K或128页x2K=256k字节的内置闪存存储器,⽤于存放程序和数据。
stm32实训心得体会
![stm32实训心得体会](https://img.taocdn.com/s3/m/6bac84b5580216fc700afdb8.png)
stm32实训心得体会篇一:STM32 实验2报告实验2MINI STM32按键控制LED灯实验一、实验目的1、掌握嵌入式程序设计流程。
2、熟悉STM32固件库的基本使用。
二、实验内容1、编程使用I/O口作为输入,控制板载的两个LED 灯。
2、使用固件库编程。
三、实验设备硬件: PC机一台MINI STM32开发板一套软件: RVMDK 一套Windows XP 一套四、实验步骤1、设计工程,使用固件库来编程设置。
、在这里我们建立一个文件夹为: STM32-Projects.点击Keil 的菜单:Project –>New Uvision Project ,然后将目录定位到刚才建立的文件夹STM32-Projecst 之下,在这个目录下面建立子文件夹shiyan1, 然后定位到 shiyan1目录下面,我们的工程文件就都保存到shiyan1 文件夹下面。
工程命名为shiyan1, 点击保存.是这个型号。
、这里我们定位到STMicroelectronics 下面的STM32F103RB( 针对我们的mini 板子、弹出对话框“Copy STM32 Startup Code to project ?.”,询问是否添加启动代码到我们的工程中,这里我们选择“否”,因为我们使用的ST固件库文件已经包含了启动文件。
、接下来,我们在 Template 工程目录下面,新建3 个文件夹 CORE, USER,STM32F10x_FWLib 。
USER 用来放我们主函数文件 , 以及其他包括system_ 等等,CORE 用来存放启动文件等,STM32F10x_FWLib 文件夹顾名思义用来存放ST官方提供的库函数源码文件.、.打开官方固件库包,定位到我们之前准备好的固件库包的目录。
STM32F10x_StdPeriph_Lib_\Libraries\STM32F10x_StdPer iph_Driver 下面,将目录下面的src,inc 文件夹 copy 到我们刚才建立的STM32F10x_FWLib 文件夹下面。
stm32学习经历(5篇可选)
![stm32学习经历(5篇可选)](https://img.taocdn.com/s3/m/a350b907ae45b307e87101f69e3143323968f50e.png)
stm32学习经历(5篇可选)第一篇:stm32学习经历随便写写,关于stm32 最近在学习stm32,写点东西,虽然简单,但都是原创啊开发板是前辈画的,好像是用来测试一个3G功能的,不过对于我来说太远;我要来了3个,自己焊了一个最小系统,好在公司资源还是不错的,器件芯片有,还可以问问前辈--对公司还是比较满意的,虽然工资少了点,但学东西第一位O(∩_∩)O~。
最开始当然是建工程了,这个真不太会,前前后后竟用了一周(时间真长,别见笑啊),上网查资料,问前辈,自己琢磨。
总算搞定,然后从GPIO开始学,开始还真没什么头绪(虽然在大学学点51,但完全没有真正应用,顶多是跑马灯实验),开始纠结是从寄存器开始学还是从库函数开始学,后来看到一句“用库函数入门,用寄存器提高”于是下定决心用库,但当时没有库的概念,结果走了很多弯路,看了很多不必要的东西,当时竟没理解到只是调用库就OK了,别的不用管。
最后潜心的在教程网看完一个例程后照猫画虎写了一个,经过了多次调试以后,灯终于亮了!那个兴奋啊。
再次还要感谢希望自己坚持下去,早日能写出一个属于自己的程序,完成一个说的过去的功能,下面把我的程序粘出来,和大家分享下,大虾看到了别见笑啊注:1.有两个灯,PA4 B12,都是低电平点亮2.有两个按键,PB8 和 PB9,按下是低电平3.程序开始后两个灯常亮,按下按键后熄灭,抬起后继续亮main.c中#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_exti.h" void RCC_Configuration(void) //时钟配置函数{ ErrorStatus HSEStartUpStatus; //使能外部晶振RCC_HSEConfig(RCC_HSE_ON); //等待外部晶振稳定HSEStartUpStatus = RCC_WaitForHSEStartUp(); //如果外部晶振启动成功,则进行下一步操作if(HSEStartUpStatus==SUCCESS) { //设置HCLK(AHB时钟)=SYSCLK 将系统时钟进行分频后,作为AHB总线时钟RCC_HCLKConfig(RCC_SYSCLK_Div1); //PCLK1(APB1) = HCLK/2 将HCLK时钟2分频后给低速外部总线RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK2(APB2) = HCLK HCLK时钟配置给高速外部总线 RCC_PCLK2Config(RCC_HCLK_Div1); //外部高速时钟HSE 4倍频RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_4); //启动PLL RCC_PLLCmd(ENABLE); //等待PLL稳定while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //系统时钟SYSCLK来自PLL输出RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //切换时钟后等待系统时钟稳定 while(RCC_GetSYSCLKSource()!=0x08); } // 下面这些都是外设总线上所挂的外部设备时钟的配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_AP B2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); }void GPIO_Configuration(void) //GPIO配置函数{ //GPIO_DeInit(GPIOA); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; GPIO_Init(GPIOB,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStructure); } void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; // 管脚选择GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource8);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource9); // 清除 EXTI线路挂起位EXTI_ClearITPendingBit(EXTI_Line8|EXTI_Line9); // EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_Line = EXTI_Line8|EXTI_Line9; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } void NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; // 注意名称是“_IRQn”,不是“_IRQChannel”NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } int main(void) { RCC_Configuration(); GPIO_Configuration(); EXTI_Config(); NVIC_Config();while(1) { GPIO_ResetBits(GPIOB,GPIO_Pin_12); GPIO_ResetBits(GPIOA,GPIO_Pin_4); } } 中断文件 it.c中void EXTI9_5_IRQHandler(void) { if ( EXTI_GetITStatus(EXTI_Line8) != RESET ) { EXTI_ClearITPendingBit(EXTI_Line8);GPIO_SetBits(GPIOA,GPIO_Pin_4);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)==0); } if ( EXTI_GetITStatus(EXTI_Line9) != RESET ){ EXTI_ClearITPendingBit(EXTI_Line9);GPIO_SetBits(GPIOB,GPIO_Pin_12);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==0);勤劳的蜜蜂有糖吃} }第二篇:STM32入门经历,高手不要进!现在STM32初学入门,写些关于入门的帮助,也算答谢帮助过我的人.希望象我这样想学STM32的朋友不用迷茫.(本入门只适合低手,高手不要见笑).1.硬件平台.现在可以买到学习有的有英蓓特的MCBSTM32 和万利的EK-STM32F,可能目前出来最好的还是的神舟系列开发板,包括神舟I号(103RBT),神舟II号(103VCT),神舟III号(103ZET),神舟iv号(107VCT)几款都有,反正这几个板我都买了,学校出钱买的,还挺实惠,让老板打了个折扣,如果你自己开板做,成本还比这高.学会了才自己做自己的板子吧.2.软件平台.现在流行的有Keil MDK 3.15b和 IAR EWARM 4.42A. 购买评估板时,里面的光盘已经带了.为什么选这两个平台,用的人多,你以后遇到问题,可以找人解决的机会就大.英蓓特的MCBSTM32用的是Keil MDK 平台, 万利的是 IAR EWARM.3.C语言知识如果想补这推荐一本入门的书C Primer Plus 中文版.这本也是入门的好书.4.ST的数据手册STM32F10x参考手册看完这个就对STM32的内部有认识.STM32 Document and library rules 个人认为这个最重要.因为你学会了C语言看例程时.很多如GPIO_SetBits GPIO_ResetBits.很多C语言以外的函数库.这些都是STM32的库文件.5.看例程.如keil MDK 3.15b下的C:/Keil/ARM/Boards/Keil/MCBSTM32 有很多例程.GPIO口,RTC,PWM,USB,CAN等等....你想到的都有例程.6.多上论坛,呵呵.....有不明问下高手,我也是这样.只要不断努力,你一定会成功的.第三篇:STM32学习心得笔记STM32学习心得笔记时钟篇在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。
flash做EEPROM用
![flash做EEPROM用](https://img.taocdn.com/s3/m/411a1fe25ef7ba0d4a733b93.png)
STM32 本身没有自带 EEPROM,但是 STM32 具有 IAP(在应用编程)功能,所以我们可以把它的 FLASH 当成 EEPROM 来使用STM32 FLASH 简介不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了1024K 字节。
战舰 STM32 开发板选择的 STM32F103ZET6 的 FLASH 容量为 512K 字节,属于大容量产品(另外还有中容量和小容量产品),STM32 的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。
主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。
对于大容量产品,其被划分为 256 页,每页 2K 字节。
注意,小容量和中容量产品则每页只有 1K 字节。
从上图可以看出主存储器的起始地址就是 0X08000000, B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。
信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3,B1 接 GND 的时候,运行的就是这部分代码。
用户选择字节,则一般用于配置写保护、读保护等功能,闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
闪存的读取内置闪存模块可以在通用地址空间直接寻址,任何 32 位数据的读操作都能访问闪存模块的内容并得到相应的数据。
读接口在闪存端包含一个读控制器,还包含一个 AHB 接口与 CPU 衔接。
这个接口的主要工作是产生读闪存的控制信号并预取 CPU 要求的指令块,预取指令块仅用于在 I-Code 总线上的取指操作,数据常量是通过 D-Code 总线访问的。
这两条总线的访问目标是相同的闪存模块,访问 D-Code 将比预取指令优先级高这里要特别留意一个闪存等待时间,因为 CPU 运行速度比 FLASH 快得多,STM32F103的 FLASH 最快访问速度≤24Mhz,如果 CPU 频率超过这个速度,那么必须加入等待时间,比如我们一般使用 72Mhz 的主频,那么 FLASH 等待周期就必须设置为 2,该设置通过 FLASH_ACR寄存器设置。
STM32学习心得笔记
![STM32学习心得笔记](https://img.taocdn.com/s3/m/61189c8571fe910ef12df87d.png)
STM32学习心得笔记时钟篇在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。
①、HSI是高速内部时钟,RC振荡器,频率为8MHz。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③、LSI是低速内部时钟,RC振荡器,频率为40kHz。
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。
倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
其中40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。
另外,实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。
RTC的时钟源通过RTCSEL[1:0]来选择。
STM32中有一个全速功能的USB 模块,其串行接口引擎需要一个频率为48MHz的时钟源。
该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL 必须使能,并且时钟频率配置为48MHz或72MHz。
另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。
系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。
系统时钟可选择为PLL 输出、HSI或者HSE。
系统时钟最大频率为72MHz,它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。
其中AHB分频器输出的时钟送给5大模块使用:①、送给AHB 总线、内核、内存和DMA使用的HCLK时钟。
②、通过8分频后送给Cortex的系统定时器时钟。
③、直接送给Cortex的空闲运行时钟FCLK。
④、送给APB1分频器。
APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。
关于单片机EEPROM数据保存的若干经验总结
![关于单片机EEPROM数据保存的若干经验总结](https://img.taocdn.com/s3/m/7d7a3143a55177232f60ddccda38376baf1fe02a.png)
关于单片机EEPROM数据保存的若干经验总结因为要保存的数据可能是千变万化的,字长可能从8位到32位,其中包括char(8)、short int(16)、int(32)、float(32),而不同数据类型在不同体系架构上字长各不相同,复杂点的甚至包括结构体Struct, 因为结构体包含数据大小未知,完成由用户定义,如果保存数据时要考虑到这么多的变化,那能把人都搞晕,因此设计一个以不变应万变的数据保存机制就很好了,好比是复杂平台中的数据串行化保存。
在单片机里面不可能实现这么高级的技术,但是也可以通过一个小小的技巧实现类似功能,方式就是通过联合体来保存,比如下面所示struct e2prom_data{char TEM_compensate;unsigned int sterilization_temperature[10];//0.1unsigned char sterilization_time_min[10];unsigned char exhaust_times;unsigned char prebalance_time_min;};union sector{struct e2prom_data sterlization_data;unsigned char storage[ sizeof(struct e2prom_data) ];} e2prom;联合sector代表实际的扇区,大小不能超过扇区大小,而上面的结构体就用来保存真正要用到的变量,然后通过联合体sector里面的unsigned char storage,统一转换成1个字节来保存实际数据,极其方便。
而要读取数据的时候可以通过下面的void read_sector(char secn)函数来统一操作,把数据统一读取到内存中,确认保存后再通过void write_sector(char secn)统一保存。
效率很高,用内存来缓存数据,可以减小EEPROM擦写次数,提高寿命。
flash做EEPROM用
![flash做EEPROM用](https://img.taocdn.com/s3/m/bf37c31df68a6529647d27284b73f242336c31b2.png)
STM32 本身没有自带E EPROM,但是 STM32 具有 IAP(在应用编程)功能,所以我们可以把它的 FLASH 当成 EEPROM来使用STM32 FLASH 简介不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了1024K字节。
战舰 STM32 开发板选择的S TM32F103ZET6的 FLASH 容量为 512K 字节,属于大容量产品(另外还有中容量和小容量产品),STM32 的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。
主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。
对于大容量产品,其被划分为 256 页,每页 2K 字节。
注意,小容量和中容量产品则每页只有 1K 字节。
从上图可以看出主存储器的起始地址就是0X08000000, B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。
信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3,B1 接 GND 的时候,运行的就是这部分代码。
用户选择字节,则一般用于配置写保护、读保护等功能,闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
闪存的读取内置闪存模块可以在通用地址空间直接寻址,任何 32 位数据的读操作都能访问闪存模块的内容并得到相应的数据。
读接口在闪存端包含一个读控制器,还包含一个 AHB 接口与 CPU 衔接。
这个接口的主要工作是产生读闪存的控制信号并预取 CPU 要求的指令块,预取指令块仅用于在 I-Code 总线上的取指操作,数据常量是通过D-Code 总线访问的。
这两条总线的访问目标是相同的闪存模块,访问 D-Code 将比预取指令优先级高这里要特别留意一个闪存等待时间,因为 CPU 运行速度比 FLASH 快得多,STM32F103的 FLASH 最快访问速度≤24Mhz,如果 CPU 频率超过这个速度,那么必须加入等待时间,比如我们一般使用 72Mhz 的主频,那么 FLASH 等待周期就必须设置为 2,该设置通过 FLASH_A CR寄存器设置。
STM32F10x Flash 模拟 EEPROM
![STM32F10x Flash 模拟 EEPROM](https://img.taocdn.com/s3/m/4611c4c98bd63186bcebbce4.png)
STM32F10x Flash 模拟EEPROMSTM32F10x芯片本身没有集成EEPROM,替代方案是用片上Flash来模拟EEPROM。
Flash与EEPROM的区别主要是:一、EEPROM可以按位擦写,而Flash只能按块(页)擦除;二、Flash的擦除寿命约1万次,较EEPROM低一个量级。
ST网站有个Flash模拟EEPROM的范例:AN2594: EEPROM emulation in STM32F10x microcontrollers(包括源码和文档)。
范例在保存修改的数据时,以写入新数据来替代对原数据的修改,并使用两个页面轮流写入,单页写满后进行数据迁移,再一次性擦除旧页面。
这个策略可以有效降低Flash擦除次数。
不过,范例代码只能保存固定大小的数据(16bits),虽然容易改成不同的固定大小,但实际用起来还是很不方便。
我改写了一下,新的特性包括:∙支持不同大小数据(字符数组、结构体等)的混合存储;∙增加对数据的校验和(Checksum)检查。
源码在附件中。
使用方法很简单,比如要保存一个字符数组 title 和一个 point 结构体:#include "eeprom.h"#define TITLE_SIZE 80#define TITLE_KEY 1#define POINT_KEY 2typedef struct {float x;float y;float z;} Point;char title[TITLE_SIZE] ="eeprom test string.";Point point;执行必要的初始化操作后,就可以进行写入和读取:uint16_t result = 0;/* Unlock the Flash Program Erase controller */FLASH_Unlock();/* EEPROM Init */EE_Init();/* Write to EEPROM */result = memcpy_to_eeprom_with_checksum(TITLE_KEY, title, TITLE_SIZE);result = memcpy_to_eeprom_with_checksum(POINT_KEY, &point, sizeof(point));/* Read from EEPROM */result = memcpy_from_eeprom_with_checksum(title, TITLE_KEY, TITLE_SIZE);result = memcpy_from_eeprom_with_checksum(&point, POINT_KEY, sizeof(point));实现混合存储的办法是,给每个变量附加8字节的控制信息。
关于STM32的FLASH操作
![关于STM32的FLASH操作](https://img.taocdn.com/s3/m/e81dc20168eae009581b6bd97f1922791688be09.png)
关于STM32的FLASH操作STM32是一款由意法半导体(STMicroelectronics)推出的32位精简型单片机系列。
它采用了ARM Cortex-M内核,并在Flash和内存等方面进行了优化,提供了强大的性能和丰富的外设功能。
FLASH是STM32芯片上的一种非易失性存储器,可以用于存储应用程序代码、数据、配置信息等。
在STM32中进行FLASH操作可以通过编程和标准库函数两种方式实现。
下面将依次介绍这两种方法的使用。
一、编程方式编程方式是直接对FLASH进行底层的读写操作,需要对相关寄存器进行配置和控制。
1.写入数据在STM32的FLASH中写入数据的操作需要经历以下步骤:1) 解锁FLASH:通过FLASH_Unlock(函数解锁FLASH,使其可写入。
2) 擦除FLASH:通过FLASH_ErasePage(函数擦除要写入的FLASH扇区。
3) 写入数据:通过FLASH_ProgramByte(、FLASH_ProgramHalfWord(、FLASH_ProgramWord(等函数对FLASH进行数据写入。
4) 上锁FLASH:通过FLASH_Lock(函数上锁FLASH,防止误操作。
以下是一个简单的例子,演示如何通过编程方式对STM32的FLASH进行数据写入:```c#include "stm32f10x_flash.h"void ConfigureFlash(void)FLASH_Unlock(; // 解锁FlashFLASH_ClearFlag(FLASH_FLAG_EOP , FLASH_FLAG_PGERR ,FLASH_FLAG_WRPRTERR); // 清除状态标志FLASH_ErasePage(FLASH_START_ADDR); // 擦除扇区FLASH_Lock(; // 上锁Flash```2.读取数据读取STM32的FLASH数据需要通过指针的方式直接读取存储器的地址,如下所示:```c#define READ_FLASH(addr) (*(volatile uint32_t*)(addr))void ReadFlash(void)uint32_t data = READ_FLASH(FLASH_START_ADDR);//在这里对读取的数据进行处理```二、标准库函数方式STM32提供了一套标准库函数,方便开发者进行FLASH操作。
FLASH模拟eeprom实验笔记
![FLASH模拟eeprom实验笔记](https://img.taocdn.com/s3/m/6a78423458fb770bf78a5542.png)
STM32 FLASH模拟EEPROM实验笔记一、根据所选IC确定每页的大小ST系列芯片中,FLASH的大小大于或者等于256k,则每页大小为2k(最多2k大小);FLASH的大小于256k大小,则每页大小为1k.二、在模拟EEPROM中写数据(1)在模拟EEPROM中写数据,有以下几个参数必须具备:写入数据的虚拟地址,写入的数据,写入的数据的数量(注:写入的数据必须每次写入半字)。
(2)在写入数据之前必须判断,要求写入的数据的虚拟地址是否非法(写入的虚拟地址值小于该芯片的FLASH的起始地址或者大于该芯片的FLASH的最大地址值均是视为非法地址)。
(3)解锁FLASH》》》》写入关键字1,关键字2(注:写入关键字就是写入解锁序列,写入的顺序不能乱)。
(4)根据要求写入的数据的虚拟地址计算出实际的偏移地址(相对于FLASH的起始地址),扇区地址,在扇区中的偏移(注意:在扇区内的偏移以2个字节为基本单位),扇区剩余空间大小。
(5)判断要求写入的数据的个数是否不大于该扇区剩余空间的大小,如成立将要求写入的数据的个数赋值给表示空间大小的变量。
(6)进入循环,进行实际的写入数据操作》》》》》》读取该扇区整个扇区的数据--→检验在该扇区中从扇区内的实际偏移开始,一直到扇区剩余空间大小(此时的剩余空间大小值等于要求写入数据的数量),检验这整个区域内是否需要擦除,如果检烟道数据不等于0xFFFF,就需要擦除该扇区的整个扇区----→该扇区擦除前复制到数组中的有效的数据重新写入到该扇区----→写入要求写入的数据到要求写入的虚拟地址中--→判断是否写入结束,如果该扇区不能够则扇区一到一个扇区,写入新扇区的数据数量等于总数量减去写入到上一个扇区的数据数量。
每次写操作完都必须做如次判定,直至要求写入的数据完全写入为止。
写入结束后跳出循环。
(7)上锁FLASH。
注意:在写入和从模拟的EEPROM读出数据都必须是半字读写,所以地址必须每次加2.。
stm32f单片机内部flash模拟EEPROM操作源码
![stm32f单片机内部flash模拟EEPROM操作源码](https://img.taocdn.com/s3/m/0c701a33f02d2af90242a8956bec0975f465a49d.png)
#include "stmflash.h"#include "delay.h"#include "usart.h"//解锁STM32的FLASHvoid STMFLASH_Unlock(void){FLASH->KEYR=FLASH_KEY1; //写入解锁序列.FLASH->KEYR=FLASH_KEY2;}//flash上锁void STMFLASH_Lock(void){FLASH->CR|=(u32)1<<31;//上锁}//得到FLASH状态//返回值://0,操作完成//1,忙//2,操作异常u8 STMFLASH_GetStatus(void){u32 res=0;res=FLASH->SR;if(res&(1<<16))return 1; //忙else if(res&(1<<4))return 2; //操作异常else if(res&(1<<5))return 2; //操作异常else if(res&(1<<6))return 2; //操作异常else if(res&(1<<7))return 2; //操作异常return 0; //没有任何状态/操作完成. }//等待操作完成//time:要延时的长短(单位:10us)//返回值://0,完成//2,操作异常//0XFF,超时u8 STMFLASH_WaitDone(u32 time){u8 res;do{res=STMFLASH_GetStatus();if(res!=1)break;//非忙,无需等待了,直接退出.delay_us(10);time--;}while(time);if(time==0)res=0xff;//TIMEOUTreturn res;}//擦除扇区//sectoraddr:扇区地址,范围是:0~11.//0~3,16K扇区;4,64K扇区;5~11,128K扇区.//返回值:执行情况u8 STMFLASH_EraseSector(u32 sectoraddr){u8 res=0;res=STMFLASH_WaitDone(200000);//等待上次操作结束,最大2sif(res==0){FLASH->CR&=~(3<<8); //清除PSIZE原来的设置FLASH->CR|=2<<8; //设置为32bit宽,确保VCC=2.7~3.6V之间!!FLASH->CR&=~(0X1F<<3);//清除原来的设置FLASH->CR|=sectoraddr<<3;//设置要擦除的扇区FLASH->CR|=1<<1; //扇区擦除FLASH->CR|=1<<16; //开始擦除res=STMFLASH_WaitDone(200000);//等待操作结束,最大2sif(res!=1) //非忙{FLASH->CR&=~(1<<1);//清除扇区擦除标志.}}return res;}//在FLASH指定地址写一个字//faddr:指定地址(此地址必须为4的倍数!!)//dat:要写入的数据//返回值:0,写入成功// 其他,写入失败u8 STMFLASH_WriteWord(u32 faddr, u32 dat){u8 res;res=STMFLASH_WaitDone(0XFF);if(res==0)//OK{FLASH->CR&=~(3<<8); //清除PSIZE原来的设置FLASH->CR|=2<<8; //设置为32bit宽,确保VCC=2.7~3.6V之间!!FLASH->CR|=1<<0; //编程使能*(vu32*)faddr=dat; //写入数据res=STMFLASH_WaitDone(0XFF);//等待操作完成,一个字编程,最多100us.if(res!=1)//操作成功{FLASH->CR&=~(1<<0);//清除PG位.}}return res;}//读取指定地址的一个字(32位数据)//faddr:读地址//返回值:对应数据.u32 STMFLASH_ReadWord(u32 faddr){return *(vu32*)faddr;}//获取某个地址所在的flash扇区//addr:flash地址//返回值:0~11,即addr所在的扇区u8 STMFLASH_GetFlashSector(u32 addr){if(addr<ADDR_FLASH_SECTOR_1)return 0;else if(addr<ADDR_FLASH_SECTOR_2)return 1;else if(addr<ADDR_FLASH_SECTOR_3)return 2;else if(addr<ADDR_FLASH_SECTOR_4)return 3;else if(addr<ADDR_FLASH_SECTOR_5)return 4;else if(addr<ADDR_FLASH_SECTOR_6)return 5;else if(addr<ADDR_FLASH_SECTOR_7)return 6;else if(addr<ADDR_FLASH_SECTOR_8)return 7;else if(addr<ADDR_FLASH_SECTOR_9)return 8;else if(addr<ADDR_FLASH_SECTOR_10)return 9;else if(addr<ADDR_FLASH_SECTOR_11)return 10;return 11;}//从指定地址开始写入指定长度的数据//特别注意:因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数// 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以// 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里// 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.//该函数对OTP区域也有效!可以用来写OTP区!//OTP区域地址范围:0X1FFF7800~0X1FFF7A0F(注意:最后16字节,用于OTP数据块锁定,别乱写!!)//WriteAddr:起始地址(此地址必须为4的倍数!!)//pBuffer:数据指针//NumToWrite:字(32位)数(就是要写入的32位数据的个数.)void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite){u8 status=0;u32 addrx=0;u32 endaddr=0;if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址STMFLASH_Unlock(); //解锁FLASH->ACR&=~(1<<10); //FLASH擦除期间,必须禁止数据缓存搞了我两晚上才发现这个问题!addrx=WriteAddr; //写入的起始地址endaddr=WriteAddr+NumToWrite*4; //写入的结束地址if(addrx<0X1FFF0000) //只有主存储区,才需要执行擦除操作!!{while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除){if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区{status=STMFLASH_EraseSector(STMFLASH_GetFlashSector(addrx));if(status)break; //发生错误了}else addrx+=4;}}if(status==0){while(WriteAddr<endaddr)//写数据{if(STMFLASH_WriteWord(WriteAddr,*pBuffer))//写入数据{break; //写入异常}WriteAddr+=4;pBuffer++;}}FLASH->ACR|=1<<10; //FLASH擦除结束,开启数据fetchSTMFLASH_Lock();//上锁}//从指定地址开始读出指定长度的数据//ReadAddr:起始地址//pBuffer:数据指针//NumToRead:字(32位)数void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead){u32 i;for(i=0;i<NumToRead;i++){pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.ReadAddr+=4;//偏移4个字节.}}//////////////////////////////////////////测试用/////////////////////////////////////////////WriteAddr:起始地址//WriteData:要写入的数据void Test_Write(u32 WriteAddr,u32 WriteData){STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字}//////////////////////////////////////////h文件///////////////////////////////////////////#ifndef __STMFLASH_H__#define __STMFLASH_H__#include "sys.h"//FLASH起始地址#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址//FLASH解锁键值#define FLASH_KEY1 0X45670123#define FLASH_KEY2 0XCDEF89AB//FLASH 扇区的起始地址#define ADDR_FLASH_SECTOR_0 ((u32)0x08000000) //扇区0起始地址, 16 Kbytes #define ADDR_FLASH_SECTOR_1 ((u32)0x08004000) //扇区1起始地址, 16 Kbytes #define ADDR_FLASH_SECTOR_2 ((u32)0x08008000) //扇区2起始地址, 16 Kbytes #define ADDR_FLASH_SECTOR_3 ((u32)0x0800C000) //扇区3起始地址, 16 Kbytes #define ADDR_FLASH_SECTOR_4 ((u32)0x08010000) //扇区4起始地址, 64 Kbytes #define ADDR_FLASH_SECTOR_5 ((u32)0x08020000) //扇区5起始地址, 128 Kbytes #define ADDR_FLASH_SECTOR_6 ((u32)0x08040000) //扇区6起始地址, 128 Kbytes #define ADDR_FLASH_SECTOR_7 ((u32)0x08060000) //扇区7起始地址, 128 Kbytes #define ADDR_FLASH_SECTOR_8 ((u32)0x08080000) //扇区8起始地址, 128 Kbytes#define ADDR_FLASH_SECTOR_9 ((u32)0x080A0000) //扇区9起始地址, 128 Kbytes #define ADDR_FLASH_SECTOR_10 ((u32)0x080C0000) //扇区10起始地址,128 Kbytes #define ADDR_FLASH_SECTOR_11 ((u32)0x080E0000) //扇区11起始地址,128 Kbytesvoid STMFLASH_Unlock(void); //FLASH解锁void STMFLASH_Lock(void); //FLASH上锁u8 STMFLASH_GetStatus(void); //获得状态u8 STMFLASH_WaitDone(u32 time); //等待操作结束u8 STMFLASH_ErasePage(u32 sectoraddr); //擦除页u8 STMFLASH_WriteWord(u32 faddr, u32 dat); //写入字u32 STMFLASH_ReadWord(u32 faddr); //读出字void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite); //从指定地址开始写入指定长度的数据void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead); //从指定地址开始读出指定长度的数据//测试写入void Test_Write(u32 WriteAddr,u32 WriteData);#endif。
关于STM32像EPROM一样可以单字节写内部Flash的理解
![关于STM32像EPROM一样可以单字节写内部Flash的理解](https://img.taocdn.com/s3/m/51626f4b3a3567ec102de2bd960590c69ec3d811.png)
关于STM32像EPROM一样可以单字节写内部Flash的理解都有说STM32的内部Flash可以像EPROM一样操作,单个字节单个字节的写入。
根据本人的拙见,其实也就仅仅是“像”而已。
原因有以下几点:1.首先Flash这种东西,其写入数据的原理是便是将1变成0,所以你的某地址Flash一旦已经写过数据而且不为0,则当你再次需要向该地址写数据时,必须要先擦除,即把该地址先全部变成1,否则你将数据写入该地址后,基本上该地址里面的值已经不是你写入的值了。
2.STM32对内部Flash有页(1k或者2K)擦除指令,也就是STM32不能单独对某一个或者某几个字节进行擦除。
而擦除操作并不需要大量内存。
3.如果要像EPROM一样操作Flash,其原理是先把该一页里面的数据全部读到一个buf里面,如果你希望整页的数据不丢失,那么buf 的大小至少应该和页大小一样,即为1K或者2K(大小视MCU型号而定)。
然后对整页进行擦除,再把要写入的数据先写到之前的buf 里面(用要写入的数据替换buf里相应位置的原来的数据),再把整个buf写到Flash里面。
如此便实现了任意数据长度的数据写入Flash。
4.正如第3点,如果你跑的是系统,诸如(UCOS,RTX,FRERTOS)等,那么你进行此操作的task的堆栈至少应该为1K+N或者2K+N。
否则直接堆栈溢出而进hard fault。
我想这是我们大家所不能忍受的。
当然,对于上述问题有一些变通的方法,如果你要写入的数据比较少,比如几十个Byte,那么你可以将buf改小,只要大于等于你要保存的数据数量就可以了,这样便可以大大减小内存。
另外,STM32支持字(4Byte)或者半字(2Byte)写入,所以你的buf应该为偶数。
如果你要写入的数量为奇数,buf也应该为偶数,不足的在buf里补0或任意一个数就好。
因为,如果你的buf为奇数的话,在写入最后一个数据时,系统会在内存中随便抓一个地址里的数据,写进去,这可能出问题。
STM32模拟EEPROM的使用和优化
![STM32模拟EEPROM的使用和优化](https://img.taocdn.com/s3/m/a30661314028915f814dc235.png)
((uint8_t)0x05)
uint16_t VirtAddVarTab[NumbOfVar] = {0, 1, 2}; //NumbOfVar 定义的比用的多实际是{0, 1, 2, 0, 0},虚拟地址 0 的数据换页后将出现旧数据覆盖最新数据
EE_WriteVariable(VirtAddVarTab[1], VarValue); temp=0; EE_ReadVariable(0, &temp);//不换页读出数据是对的,换页后读出数据错误 }
}
//====================================================================================
int main(void) { uint16_t temp;
for (VarValue = 0; VarValue < 100; VarValue++) {
EE_WriteVariable(VirtAddVarTab[0], VarValue); } for (VarValue = 0; VarValue < 50; VarValue++) {
} }
STM32 FLASH 模拟 EEPROM 使用注意: 不少人问该程序的 FLASH 保存数据多少和使用寿命 保存数据多少跟 FLASH 页大小有关,如果页大小是 1K 那么只能保存 1024/4-1=256-1 个 16 位数据,如果你保 存 8 位数,你可以 2 个 8 位数据组合后保存或者直接保存,如果保存 32 位数据那就拆成 2 个 16 位保存,当然 关于寿命 现在 STM32 的 FLASH 寿命是 10000 次, 如果你保存 255 个数据那么每次修改 1 个数据 FLASH 就要擦写 1 次,如果你保存 1 个数据,那么你修改 255 次该页才擦 1 次,继续用另外 1 页,建议保存数据个数不要超过 50%,当然如果你的数据基本都不修改你保存 255 个也是没有任何问题(你的数据都不修改根本不用关心寿命问题了:)。
解决STM32Flash擦写操作导致USART接收丢数据
![解决STM32Flash擦写操作导致USART接收丢数据](https://img.taocdn.com/s3/m/23c17ad16037ee06eff9aef8941ea76e58fa4ab4.png)
解决STM32Flash擦写操作导致USART接收丢数据问题:该问题由客户提出,发生在STM32F103VDT6器件上。
据客户工程师讲述,在其产品设计中使用了STM32片上Flash模拟了EEPROM 的功能,用于存贮数据。
在软件调试时,发现开启此功能,会影响到USART通信,导致偶尔发生个别数据接收不到的现象。
调研:检查其软件代码,发现其中对Flash上数据的更新操作分为如下几个步骤:1. 保存Flash页上的数到RAM中;2. 擦除Flash页;3. 修改RAM中的数据;4. 将RAM中的数据写回Flash页上;对照STM32的数据手册,查找到相关的数据:1. 字写入时间 40uS ~ 70uS;2. 页擦除时间40mS;检查软件代码,找到对USART的设置:1. 波特率115200BPS;2. 帧格式为1 个起始位,8个数据位,2个停止位;检查软件代码,发现其对USART的接收数据采用中断的方式进行读取。
结论:通过计算,USART的每个帧的传输时间为:该时间大于Flash的字写入时间,小于Flash的页擦除时间。
所以,在Flash页擦除期间有可能发生多次字节帧的传输。
而在此其间,由于Flash接口不可用,CPU不能取指令,导致中断得不到及时响应,从而发生接收到的数据未及时读走而被覆盖的现象。
处理:在内存中建立循环缓冲区,开启DMA 通道。
一旦USART 有数据收到,DMA 负责将其传输至循环缓冲区中。
软件定期检测循环缓冲区中是否有接收到的数据,如果有则加以处理。
建议:在只有一个Flash 模块的STM32 中,CPU对Flash 接口的使用具有独占性。
该接口不能同时进行多个操作,比如,在写操作的同时进行读取操作,或在擦除操作的同时进行读取操作,即便读、写操作的地址不同,或者所读数据不在被擦除的页上也不行。
因此,当程序运行在Flash 上情况下,在对Flash 进行写入、擦除操作时,往往会因为CPU 取不到指令而造成程序执行的停顿。
STM32使用内部FLASH模拟EEPROM
![STM32使用内部FLASH模拟EEPROM](https://img.taocdn.com/s3/m/b9aa791bef06eff9aef8941ea76e58fafab045c9.png)
每个页面都有三种可能的状态 :
●ERASED:: 页面为空。
●RECEIVE_DATA: 该页面正在接收来自另一个完整页面的数据。
●VALID_PAGE: 页面包含有效数据,该状态不会改变,直到所有有效数据完全转移到被擦除的页面。
下图为不同页的切换状态图,
一般情况下,在使用这种方法时,用户并不预先知道更新频率的变化。
本文档中描述的软件和实现使用两个 Flash 内存页面来模拟 EEPROM
每个变量元素由一个虚拟地址和一个存储在 Flash 内存中的值定义,以供后续检索或更新(在实现的软件中,虚拟地址和数据都是 16 位长)。
当数据被修改时,与之前的虚拟地址相关联的修改后的数据被存储到一个新的 Flash 内存位置中。
数据检索返回最新 Flash 内存位置中的修改数据
下图为 EEPROM 格式结构
下面图片的例子展示了三个 EEPROM 变量(Var1,Var2 及 Var3),并附有下列虚拟地址 :Var1: 5555h, Var2: 6666h, Var3: 7777h。
stm32Flash模拟eeprom心得(原创)
![stm32Flash模拟eeprom心得(原创)](https://img.taocdn.com/s3/m/a515242a2bf90242a8956bec0975f46527d3a772.png)
stm32Flash模拟eeprom心得(原创)stm32f10xflash模拟eeprom心得花了几天时间研究STM32用闪存模拟EEPROM的问题,最终彻底理解了这一机制。
因为我的英语很好,我没有仔细阅读官方文件,而是直接拿起了官方给出的示例程序。
当然,这种方法很愚蠢,但最终的效果是一样的。
下面仅将我学习过程中的一些心得体会给大家介绍一下,希望能对需要的人有所帮助,有不足之处望大家积极指正。
我们向您推荐的第一批文件是st的官方文件an2594。
stm32flash模拟EEPROM的使用和优化。
前人总结的PDF,以及优化的例程代码fw_v3。
1.0优化(闪存模拟EEPROM)。
拉尔下面开始进入主题1.为什么使用flash来模拟EEPROM?在许多应用场合下需要用eeprom保存非易失性的数据,但是意法半导体为了控制成本,没有在stm32f10x系列芯片中集成eeprom,所以我们就需要用其内部集成的flash通过软件模拟eeprom来达到同样的效果。
2.stm32中的片上flash特点根据stm32f10x flash memory programming中的介绍,以小容量为例(如下图所示),我们需要使用32 1K字节/页的主存储空间,也就是说,在这个空间中,除了保存用户代码的部分外,其余部分可以用作数据存储。
stm32的flash分为主存储块和信息块。
主存储块用于保存具体的程序代码和用户数据,信息块用于负责由stm32出厂是放置2kb的启动程序(bootloader)并锁死,用户无法更改。
选项字节存储芯片的配置信息及对主存储块的保护信息。
STM32的闪存主存储块按页组织。
有些产品是1KB/页,有些产品是2KB/页。
页面的典型用途是逐页擦除flash。
从这个角度来看,该页面有点像通用flash的扇区上图中flash一页大小为1kb。
范围为从地址0x08000000开始的32kb内。
flash的写入操作应遵循“先擦除后写入”的原则;闪存的读写涉及一个概念,字(word)32bit和半字(halfword)16bit,虽然stm32flash也是由字节组成,但stm32flash的编程每次都是以16bit半字为单位,且flash地址必须为偶数,否则会出错。
stm32f302内部flash模拟eeprom_HAL库
![stm32f302内部flash模拟eeprom_HAL库](https://img.taocdn.com/s3/m/36dc757a32687e21af45b307e87101f69e31fb80.png)
STM32F302微控制器可以使用其内部Flash内存模拟EEPROM存储器。
这种方法的优点在于,无需使用外部存储器,同时减少了PCB布局和成本的限制。
要在STM32F302的内部Flash中模拟EEPROM,需要完成以下步骤:1.定义Flash内存的页面大小。
根据芯片的具体配置,可能需要在编译器中设定适当的页面大小。
对于STM32F302,Flash的页面大小通常为1024字节。
2.定义EEPROM的起始地址。
在STM32F302中,EEPROM的起始地址通常设定为ADDR_FLASH_PAGE_62。
这是因为Flash内存被划分为256页,每页2KB,而EEPROM通常位于第62页之后的Flash内存空间。
当使用内部Flash模拟EEPROM时,需要注意以下几点:1.使用内部Flash仿真EEPROM具有一定的风险。
在进行系统升级或编译烧写文件时,可能会覆盖Flash中的数据,导致数据丢失。
2.STM32F302的Flash内存分为三个部分:系统代码、应用程序代码和用户数据。
在仿真EEPROM时,只能使用用户数据部分,不能覆盖系统代码和应用程序代码。
源码如下由于STM32F302的内部Flash内存不具有EEPROM功能,因此需要使用HAL库来模拟EEPROM。
下面是一个使用HAL库实现STM32F302内部Flash模拟EEPROM 的示例代码:c复制代码#include"stm32f3xx_hal.h"#define EEPROM_START_ADDRESS ((uint32_t)0x08080000) // EEPROM的起始地址#define EEPROM_SIZE ((uint32_t)0x2000) // EEPROM的大小为8KBuint32_t pageError = 0; // 用于保存Flash编程时的错误代码void Flash_Unlock(void){HAL_FLASH_Unlock(); // 解锁STM32的FLASH}void Flash_Lock(void){HAL_FLASH_Lock(); // 上锁STM32的FLASH}uint32_t Flash_GetStatus(void){uint32_t status;status = HAL_FLASH_GetStatus(); // 获取FLASH状态if(status & HAL_FLASH_BUSY) return1; // 忙else if(status & HAL_FLASH_ERROR_PG) return2; // 编程错误else return0; // 正常状态}uint8_t* Flash_ReadData(uint32_t address, uint32_t size){uint8_t* data = (uint8_t*)malloc(size); // 分配内存空间if(data == NULL) return NULL; // 内存分配失败,返回NULLuint32_t i;for(i=0; i<size; i+=HAL_FLASH_PAGE_SIZE){uint32_t pageAddress = address + i; // 计算页地址uint32_t pageIndex = pageAddress / HAL_FLASH_PAGE_SIZE; // 计算页号uint32_t offset = pageAddress % HAL_FLASH_PAGE_SIZE; // 计算偏移量uint32_t dataLength = HAL_FLASH_PAGE_SIZE - offset; // 每页读取的数据长度if(dataLength > size - i) dataLength = size - i; // 确保不会读取过量Flash_Unlock(); // 解锁if(HAL_FLASH_ProgramPage(pageAddress, (uint32_t*)data + i,&pageError) == HAL_ERROR) // 编程错误{free(data); // 释放内存空间Flash_Lock(); // 上锁return NULL; // 返回NULL,表示读取失败}}return data; // 返回读取的数据}。
STM32 片内FLASH模拟EEPROM的优化方法
![STM32 片内FLASH模拟EEPROM的优化方法](https://img.taocdn.com/s3/m/d163399284868762caaed530.png)
STM32 片内FLASH模拟EEPROM的优化方法芯片:STM32F407,1M片内FLASH。
首先要知道STM32F407的FLASH的每个扇区的大小并不都一样,第0-3扇区每个扇区是16K,第4扇区是64K,后面的每个扇区是128K。
而出于某些方面的考虑,我们需要把某些数据存储到FLASH中,也就是把FLASH当作EEPROM来使用,这时就有个问题,STM32F407的FLASH在写操作之前,必须保证写入地址的数据为OXFF,否则无法写入。
如果数据为非0XFF,则需要先把该扇区擦除后才能操作。
而STM32F407的固件库只提供了四个 FLASH擦除函数: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);对于前面两个函数比较好理解,一个是用来擦除某个 Sector,一个使用来擦除全部的 sectors。
对于第三个和第四个函数,这里的话主要是针对 STM32F42X 系列和 STM32F43X 系列芯片而言的,因为它们将所有的 sectors 分为两个bank。
所以这两个函数用来擦除 2个 bank 下的sectors的。
第一个参数取值范围在固件库有相关宏定义标识符已经定义好,为FLASH_Sector_0~FLASH_Sector_11(对于我们使用的 STM32F407 最大是FLASH_Sector_11),对于这些函数的第二个参数,我们这里电源电压范围是3.3V,所以选择 VoltageRange_3 即可。
stm32学习心得体会
![stm32学习心得体会](https://img.taocdn.com/s3/m/4451142fdc36a32d7375a417866fb84ae45cc3ac.png)
stm32学习⼼得体会 stm32作为现在嵌⼊式物联⽹单⽚机⾏业中经常要⽤多的技术,相信⼤家都有所接触,今天这篇就给⼤家详细的分析下有关于stm32的出⼝,还不是很清楚的朋友要注意看看了哦,在最后还会为⼤家分享有些关于stm32的视频资料便于学习参考。
什么是串⼝ UART : Universal Asynchronous Receiver/Transmitter 通⽤异步收发器 USART : Universal Synchronous Asynchronous Receiver/Transmitter 通⽤同步/异步收发器 ⼀种是常⽤也是最简单的串⾏数据传输协议。
数据线只需要两根就可以实现全双⼯。
Tx: 发送数据线 Rx: 接收数据线 A B TX -----------> Rx Rx <------------Tx 全双⼯: 两个设备可以同时发送和接收 串⾏数据: 发送只⼀根线,⼀次只能发送⼀bit. ⼀bit接着⼀bit发送和接收。
模块通信: 上位机 下位机 通信⼀般需要两个设备,我们把这两个设备,⼈为叫做上位机, 下位机。
上位机: 把处理性能强的机⼦,上位机。
数据⼤部分处理都在上位机完成。
下位机: 把数据采集的终端,处理性能单⼀的机⼦,下位机。
串⼝只有⼀根发送数据线,假如 A要发送⼀个字符数据 10101010 给B A -------- --------- -------- ------- ... ⾼电平周期是多长?即使是不发送数据Tx线上也有⼀个电平状态,接收⽅ 它怎么知道你是在发送呢?.... UART数据如何传输? UART protocol 串⼝协议。
串⼝发送和接收数据是以帧为单位. Frame 1帧(Frame)= 1 start bit(起始位) + 5-9bits数据位 + 0/1bit 校验位 + stop bits(0.5, 1,1,5,2) 起始位: ⼀个周期的低电平 数据位: 5-9bits数据位,具体是多少bits,需要双⽅协商。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
所有文档打包资料CSDN下载地址:/ddf7d/5077420
STM32F10X FLASH模拟EEPROM心得
微博:
花了几天时间研究stm32用Flash模拟EEPROM的问题,终于彻底弄懂了这种机制,由于我英文很菜,所以官方文档没有仔细看,而是直接去抠官方给出的例子程序,当然这种方法比较笨,但最终效果是一样的。
下面仅将我学习过程中的一些心得体会给大家介绍一下,希望能对需要的人有所帮助,有不足之处望大家积极指正。
首先推荐大家看的文档就是ST的官方文档《AN2594.pdf》和前辈总结出的《STM32 FLASH 模拟EEPROM使用和优化.pdf》和已经优化过的例程代码《FW_
下面开始进入主题
1.为什么要用flash模拟eeprom?
在许多应用场合下需要用eeprom保存非易失性的数据,但是意法半导体为了控制成本,没有在STM32F10X系列芯片中集成EEPROM,所以我们就需要用其内部集成的FLASH通过软件模拟EEPROM来达到同样的效果。
2.stm32中的片上FLASH特点
根据《STM32F10X闪存编程》中的介绍,以小容量为例(如下图),我们要使用的是32个1K字节/页的主存储空间,也就是说这段空间里除了保存用户代码的部分,其余部分我们是可以利用其作为数据存储使用的。
stm32的FLASH分为主存储块和信息块。
主存储块用于保存具体的程序代码和用户数据,信息块用于负责由stm32出厂是放置2KB的启动程序(Bootloader)并锁死,用户无法更改。
选项字节存储芯片的配置信息及对主存储块的保护信息。
STM32的FLASH主存储块按页组织,有的产品每页1KB,有的产品每页2KB。
页面典型的用途就是用于按页擦除FLASH。
从这点来看,页面有点像通用FLASH的扇区
上图中FLASH一页大小为1KB。
范围为从地址0x08000000开始的32KB 内。
对Flash 的写入操作要“先擦除后写入”的原则;
闪存的读写涉及一个概念,字(Word)32bit和半字(HalfWord)16bit,虽然STM32 FLASH也是由字节组成,但STM32 FLASH的编程每次都是以16bit半字为单位,且FLASH地址必须为偶数,否则会出错。
3.对AN259
4.pdf中模拟EEPROM机制的解释
官方例程中用了2页FLASH空间来作为模拟EEPROM进行数据存储,例如页3(0x08000C00-0x08000FFF)和页4(0x08001000-0x080013FF),分别将其标记为PAGE0和PAGE1,简单流程如下图
按照《使用和优化.pdf》中的解释,如果0 页空间写满数据,那么把0 页空间里面的【有效数据】复制到1 页,如果1页数据满那么把1 页空间里面的【有效数据】复制到0 页,这样循环使用,当然如果你想增加使用寿命可以增加多页循环。
每页前面4 字节保留,其中前2 字节是该页状态标志。
是的,看到这里我开始感觉到了迷惑,迫切的需要弄清楚这种机制。
官方文档中的这张图说明了虚拟的EEPROM在FLASH中的保存形式,对页进行以4字节为单位的分块,每块的前2字节保存虚拟EEPROM的16bit数据,后两字节保存此数据的16bit虚拟地址,虚拟地址必须为(0x0000-0xFFFE)。
那么先在这里说一下页面的三种状态
●ERASED 页面是空的或者刚刚擦除数据,此时整个页面都是0xFFFF
●RECEIVE_DATA 按照官方解释是,此页面处在接收已满页面的有效数据
过程中。
一旦另一页面完成擦除(即数据搬运完毕),此页面状态即变成VALID_PAGE。
搬运的时候先将最新更新的数据写入,然后再将所有有效数据(除刚刚更新的虚拟地址的数据)写入页面。
状态字:0xEEEE
●VALID_PAGE 页面含有有效数据,这种状态会一直保持,直到所有有效数
据搬运到已擦除的页面(有效数据搬运到新页面)。
状态字:0x0000
1.写数据
前面已经说到每页前4个字节保留,其中前2字节为页面状态字。
假设保存的数据虚拟地址是0x7777,那么程序写数据是从当前有效页页首地址开始查询虚拟地址位置为0xFFFF的空间,如果是0xFFFF 那么该位置可以保存数据;如果不是,那么继续找下1 个位置,如果本页无0XFFFF 的空间那么表示本页已满,那么将本页【有效数据】复制到另外1 页继续保存数据。
2.读数据
读数据时是从有效页的末尾地址开始检测是否是有效数据,如果是那么立即返回,程序是通过虚拟地址判断有效数据的,第1 个匹配的虚拟地址的数据才是有效的。
3.对【有效数据】的解释
在两次保存虚拟地址为0x7777的数据时(如下图所示)由于写数据时总是在FLASH中从首至尾依次存放,而读的时候总是从尾至首查找匹配,所以最后一次写入的虚拟地址是0x7777对应的数据1245 才是有效的。
这就是虚拟数据的更新。
4.页满时的数据处理
当有新数据要写入而页面内无0xFFFF地址即页面已满时,会将数据写入新的页面,并将原页面的有效数据也复制至新的页面,紧接着擦除已满的页面。
如下图所示:
5.优化的问题
STM32 FLASH 模拟EEPROM优化
官方例程中读写数据每次要查询读写位置,写数据是从页首地址开始查询,读地址是从页末地址查询。
假如只有1 个数据,读数据时效率是很低的,要查到最后才能找到有效数据,如果页快满了写数据效率也很低,读效率反而好一点了。
实际程序中记录下一个可以写数据的位置将提高数据的读写效率,这样的话:写数据就是立即写不用查询,读数据不从页末地址查询,而是从最后1 个写入数据处查询,这样特别在页数据少时效率提高不少。
优化过的例子代码只需要增加很少部分就能实现。
增加关键代码
uint32_t CurWrAddress;
// 初始化写地址,减少每次读写时查询时间
uint16_t InitCurrWrAddress(void)
详细请看修改后的例子,读写函数也做了相应更改
剩下的就是大家根据官方代码与优化过的代码进行对比,并通过eeprom.h 中的三个入口函数进行细致研究了。
本人水平有限,如有不妥之处请及时指正
邮箱:
微博:。