嵌入式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的时候,就是该区域没有被改写过。