STM32嵌入式FLASH擦除与写入

嵌入式Flash

Flash具有以下主要特性:
1.对于STM32F40x和 STM32F41x,容量高达1 MB;对于STM32F42x和STM32F43x,容量高达2MB。128位宽数据读取---------意思就是128/8=16(字节)

2.字节、半字、字和双字数据写入----对应一个字节,两个字节,四个字节,八个字节。(推荐以字读取和写入,即四个字节,刚好32位)

3.扇区擦除与全部擦除
扇区擦除就像你删除电脑的C盘内容,不删除D-F盘内容一样。
扇区擦除完的数据都为0XFF

4.存储器组织结构Flash 结构如下:
-主存储器块,分为4个16 KB扇区、1个64 KB扇区和7个128 KB扇区-系统存储器,器件在系统存储器自举模式下从该存储器启动

编程的时候,要注意写入数据是在哪个扇区,而且要知道扇区地址,不能够超过芯片的FLASH大小,例如你是512KB的,最多就到扇区7。

512字节OTP (一次性可编程,once time program),用于存储用户数据。OTP区域还有16个额外字节,用于锁定对应的OTP数据块。选项字节,用于配置读写保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。低功耗模式(有关详细信息,请参见参考手册的“电源控制(PWR)”部分)

STM32片内自带SRAM和FLASH,FLASH是用来存储程序的,SRAM是用来存储程序运行中的中间变量,通常不同型号的STM32的SRAM和FLASH大小是不相同的。例如STM32L431RCT6,SRAM容量大小为64KB,闪存FLASH的容量大小为256KB。

库函数的识别方法:硬件开头+大小写结合,ucos系统的代码量可能有100kb。

编程(根据固件库手册的例子进行模仿编写)
(1)解锁保护机制
(2)清空标志位
(3)获取扇区的起始地址和末地址(用于擦除扇区)
(4)使用循环,擦除扇区
(5)写入数据(一定要先擦除,才能写入数据)
(6)读取数据,验证是否正确写入
(7)锁定FLASH,进行保护

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include "stdio.h"

static GPIO_InitTypeDef  	GPIO_InitStructure;
static USART_InitTypeDef 	USART_InitStructure;
static NVIC_InitTypeDef 	NVIC_InitStructure;		


//重定义fputc函数 
int fputc(int ch, FILE *f)
{
    
     	
	USART_SendData(USART1,ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
	
	return ch;
}   

void delay_us(uint32_t nus)
{
    
    		
	uint32_t temp;	    	 
	SysTick->LOAD =SystemCoreClock/8/1000000*nus; 	//时间加载	  		 
	SysTick->VAL  =0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; 		//使能滴答定时器开始倒数 	 
	do
	{
    
    
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));			//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; 		//关闭计数器
	SysTick->VAL =0X00;       						//清空计数器 
}

void delay_ms(uint16_t nms)
{
    
    	 		  	  
	uint32_t temp;		   
	SysTick->LOAD=SystemCoreClock/8/1000*nms;		//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;    	//能滴答定时器开始倒数 
	do
	{
    
    
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));			//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    	//关闭计数器
	SysTick->VAL =0X00;     		  				//清空计数器	  	    
} 



void USART1_Init(uint32_t baud)
{
    
    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 							//使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);							//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); 						//GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); 						//GPIOA10复用为USART1
	
	//USART1端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 						//GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;									//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;								//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 									//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 									//上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); 											//初始化PA9,PA10

	//USART1 初始化设置
	USART_InitStructure.USART_BaudRate = baud;										//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;						//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;							//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;								//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;					//收发模式
	USART_Init(USART1, &USART_InitStructure); 										//初始化串口1
	
	USART_Cmd(USART1, ENABLE);  													//使能串口1 
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);									//开启相关中断

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;								//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;							//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;								//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);													//根据指定的参数初始化VIC寄存器
}


uint32_t uwStartSector = 0;
uint32_t uwEndSector = 0;
uint32_t uwAddress = 0;
uint32_t uwSectorCounter = 0;

__IO uint32_t uwData32 = 0;
__IO uint32_t uwMemoryProgramStatus = 0;

//这些地址从F4中文手册的第三章FLASH可以查到
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes   */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base address of Sector 8, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 Kbytes */

#define ADDR_FLASH_SECTOR_12     ((uint32_t)0x08100000) /* Base address of Sector 12, 16 Kbytes  */
#define ADDR_FLASH_SECTOR_13     ((uint32_t)0x08104000) /* Base address of Sector 13, 16 Kbytes  */
#define ADDR_FLASH_SECTOR_14     ((uint32_t)0x08108000) /* Base address of Sector 14, 16 Kbytes  */
#define ADDR_FLASH_SECTOR_15     ((uint32_t)0x0810C000) /* Base address of Sector 15, 16 Kbytes  */
#define ADDR_FLASH_SECTOR_16     ((uint32_t)0x08110000) /* Base address of Sector 16, 64 Kbytes  */
#define ADDR_FLASH_SECTOR_17     ((uint32_t)0x08120000) /* Base address of Sector 17, 128 Kbytes */
#define ADDR_FLASH_SECTOR_18     ((uint32_t)0x08140000) /* Base address of Sector 18, 128 Kbytes */
#define ADDR_FLASH_SECTOR_19     ((uint32_t)0x08160000) /* Base address of Sector 19, 128 Kbytes */
#define ADDR_FLASH_SECTOR_20     ((uint32_t)0x08180000) /* Base address of Sector 20, 128 Kbytes */
#define ADDR_FLASH_SECTOR_21     ((uint32_t)0x081A0000) /* Base address of Sector 21, 128 Kbytes */
#define ADDR_FLASH_SECTOR_22     ((uint32_t)0x081C0000) /* Base address of Sector 22, 128 Kbytes */
#define ADDR_FLASH_SECTOR_23     ((uint32_t)0x081E0000) /* Base address of Sector 23, 128 Kbytes */

//这里就以6起始,7结束
#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_6   /* Start address of user Flash area */

#define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_7  /* End address of user Flash area */

//获取扇区地址
static uint32_t GetSector(uint32_t Address)
{
    
    
	uint32_t sector = 0;

	if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
	{
    
    
		sector = FLASH_Sector_0;  
	}
	else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
	{
    
    
		sector = FLASH_Sector_1;  
	}
	else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
	{
    
    
		sector = FLASH_Sector_2;  
	}
	else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
	{
    
    
		sector = FLASH_Sector_3;  
	}
	else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
	{
    
    
		sector = FLASH_Sector_4;  
	}
	else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
	{
    
    
		sector = FLASH_Sector_5;  
	}
	else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
	{
    
    
		sector = FLASH_Sector_6;  
	}
	else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
	{
    
    
		sector = FLASH_Sector_7;  
	}
	else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
	{
    
    
		sector = FLASH_Sector_8;  
	}
	else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
	{
    
    
		sector = FLASH_Sector_9;  
	}
	else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
	{
    
    
		sector = FLASH_Sector_10;  
	}

	else if((Address < ADDR_FLASH_SECTOR_12) && (Address >= ADDR_FLASH_SECTOR_11))
	{
    
    
		sector = FLASH_Sector_11;  
	}

	else if((Address < ADDR_FLASH_SECTOR_13) && (Address >= ADDR_FLASH_SECTOR_12))
	{
    
    
		sector = FLASH_Sector_12;  
	}
	else if((Address < ADDR_FLASH_SECTOR_14) && (Address >= ADDR_FLASH_SECTOR_13))
	{
    
    
		sector = FLASH_Sector_13;  
	}
	else if((Address < ADDR_FLASH_SECTOR_15) && (Address >= ADDR_FLASH_SECTOR_14))
	{
    
    
		sector = FLASH_Sector_14;  
	}
	else if((Address < ADDR_FLASH_SECTOR_16) && (Address >= ADDR_FLASH_SECTOR_15))
	{
    
    
		sector = FLASH_Sector_15;  
	}
	else if((Address < ADDR_FLASH_SECTOR_17) && (Address >= ADDR_FLASH_SECTOR_16))
	{
    
    
		sector = FLASH_Sector_16;  
	}
	else if((Address < ADDR_FLASH_SECTOR_18) && (Address >= ADDR_FLASH_SECTOR_17))
	{
    
    
		sector = FLASH_Sector_17;  
	}
	else if((Address < ADDR_FLASH_SECTOR_19) && (Address >= ADDR_FLASH_SECTOR_18))
	{
    
    
		sector = FLASH_Sector_18;  
	}
	else if((Address < ADDR_FLASH_SECTOR_20) && (Address >= ADDR_FLASH_SECTOR_19))
	{
    
    
		sector = FLASH_Sector_19;  
	}
	else if((Address < ADDR_FLASH_SECTOR_21) && (Address >= ADDR_FLASH_SECTOR_20))
	{
    
    
		sector = FLASH_Sector_20;  
	} 
	else if((Address < ADDR_FLASH_SECTOR_22) && (Address >= ADDR_FLASH_SECTOR_21))
	{
    
    
		sector = FLASH_Sector_21;  
	}
	else if((Address < ADDR_FLASH_SECTOR_23) && (Address >= ADDR_FLASH_SECTOR_22))
	{
    
    
		sector = FLASH_Sector_22;  
	}
	else/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23))*/
	{
    
    
		sector = FLASH_Sector_23;  
	}

	return sector;
}


int main(void)
{
    
     
	char buf[128]={
    
    0};

	//使能GPIOG的硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
	

	//系统定时器初始化,时钟源来自HCLK,且进行8分频,
	//系统定时器时钟频率=168MHz/8=21MHz
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
		
	//设置中断优先级分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//串口1,波特率115200bps,开启接收中断
	USART1_Init(115200);
	

	/* Enable the flash control register access ,使能FLASH寄存器的访问,解除保护机制*/
	FLASH_Unlock();

	/* Clear pending flags (if any) ,清空标志位*/  
	FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
					FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); 
#if 1
	/* 通过扇区地址转换为扇区号,因为进行扇区擦除的时候,需要填写扇区的编号*/
	uwStartSector 	= GetSector(FLASH_USER_START_ADDR);
	uwEndSector 	= GetSector(FLASH_USER_END_ADDR);
	
	
	/* Strat the erase operation ,开始扇区擦除*/
	uwSectorCounter = uwStartSector;
	while (uwSectorCounter <= uwEndSector) 
	{
    
    
		/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
		be done by word */ 
		if (FLASH_EraseSector(uwSectorCounter, VoltageRange_3) != FLASH_COMPLETE)
		{
    
     
			/* Error occurred while sector erase. ,擦除错误
			User can add here some code to deal with this error  */
			while (1)
			{
    
    
				printf("Erase error\r\n");
				delay_ms(500);
			}
		}
		/* jump to the next sector */
		if (uwSectorCounter == FLASH_Sector_11)
		{
    
    
			uwSectorCounter += 40;
		} 
		else 
		{
    
    
			uwSectorCounter += 8;
		}
	}
	
	//添加读取数据的代码,测试擦除完之后读到的值是?
	uwAddress = FLASH_USER_START_ADDR;				//扇区6的起始地址
	while (uwAddress < FLASH_USER_END_ADDR)
	{
    
    
		//指向扇区地址并取指就等同于读取数据
		uwData32 = *(__IO uint32_t*)uwAddress;

		//因为每次读取是4个字节,所以每次地址偏移4个字节
		uwAddress = uwAddress + 4;
		
		//[可选]打印一些调试信息,每偏移1000地址就打印一次数据
		if(uwAddress % 1000 ==0)
			printf("%8X \r\n",uwData32);
	}  	
	
#endif
	/* Program the user Flash area word by word ,开始写入数据*/
	/* area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR */

	uwAddress = FLASH_USER_START_ADDR;				//扇区6的起始地址

	while (uwAddress < FLASH_USER_END_ADDR)			//以扇区7的起始地址作为结束地址
	{
    
    
		//每次写入数据为0x12345678的时候以4字节也就是以字为单位
		if (FLASH_ProgramWord(uwAddress, 0x12345678) == FLASH_COMPLETE)
		{
    
    
			//每写完一次,地址偏移4个字节
			uwAddress = uwAddress + 4;
		}
		else
		{
    
     
			/* Error occurred while writing data in Flash memory,写入失败 
			 User can add here some code to deal with this error */
			while (1)
			{
    
    
				printf("Program error at addr %8X \r\n",uwAddress);
				delay_ms(500);
			}
		}
	 }

	 /* Lock the Flash to disable the flash control register access ,写入后,锁定FLASH*/
	FLASH_Lock(); 


	/* Check if the programmed data is OK ,读取FLASH数据并进行校验*/
	/*  MemoryProgramStatus = 0: data programmed correctly
	MemoryProgramStatus != 0: number of words not programmed correctly */
	uwAddress = FLASH_USER_START_ADDR;
	uwMemoryProgramStatus = 0;

	while (uwAddress < FLASH_USER_END_ADDR)
	{
    
    
		//指向扇区地址并取指就等同于读取数据!!!!!!!!!!!!!!!!!!!!!!!!
		uwData32 = *(__IO uint32_t*)uwAddress;

		//读取到的数据不等于0x12345678的时候,就进行错误的计数
		if (uwData32 != 0x12345678)
		{
    
    
			uwMemoryProgramStatus++;  
		}
		
		//因为每次读取是4个字节,所以每次地址偏移4个字节

		uwAddress = uwAddress + 4;
		
		//[可选]打印一些调试信息,每偏移1000地址就打印一次数据
		if(uwAddress % 1000 ==0)
			printf("%8X \r\n",uwData32);
	}  

	/* Check Data correctness ,最后进行数据错误计数输出*/
	if(uwMemoryProgramStatus)
	{
    
    
		printf("Read check is not correct\r\n");
	}
	else
	{
    
    
		printf("Read check is correct\r\n");	 
	}
	

	while(1)
	{
    
    
	
	}
}

如何知道当前保存数据的数目﹖
方法1:将数据的数目保存到其他扇区或者其他地址。
方法2:当读取到数据为0xFF的时候,就是该区域没有被改写过。

猜你喜欢

转载自blog.csdn.net/ABCisCOOL/article/details/115128808