一、片内FLASH
在STM32芯片内部有一个FLASH存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中, 由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行。
从下图所示的官方数据手册可知,STM32的Flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯片实际的Flash大小,不同的芯片Flash大小不同,FLASH一般用来存储代码和一些定义为const的数据,断电不丢失。RAM起始地址是0x2000 0000,结束地址是0x2000 0000加上芯片的RAM大小,不同的芯片RAM也不同,是MCU的内存,用来存储代码运行时的数据,变量等等,掉电数据丢失。
对FLASH进行操作时,有必要提前知道FLASH的内存大小,方便后面芯片选型和开发过程中对FLASH数据读写,掉电保存等操作。如下图可知,STM32内部FLASH的容量类型可根据它的型号名确定,本次HAL库使用的STM32G431RBT6芯片,其FLASH空间大小为128KB;标准库使用的是STM32F407VET6芯片,其FLASH空间大小为512KB。
如下图所示,是从某元器件商城查询到的常用AT24C02及W25Q16系列存储器的价格,如果只是做单个原型设备,那么一个小存储器芯片的价格可能是不痛不痒的,但对能进行大批量生产的电子产品,成本压缩几毛钱,都能创造一笔不菲的收入,甚至节约下来的成本可以供很多工程师的月工资。因此对于数据存储量不是很大的设备产品,就可以考虑直接使用MCU内置的FLASH进行数据掉电存储读写。
因此如果MCU内部FLASH存储了应用程序后还有剩余的空间,我们可以把它像外部SPI-FLASH那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。由于访问内部FLASH的速度要比外部的SPI-FLASH快得多,所以在紧急状态下常常会使用内部FLASH存储关键记录;为了防止应用程序被抄袭, 有的应用会禁止读写内部FLASH中的内容,或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部FLASH的操作。
二、FLASH读写编程思路
1、写Flash思路
0、确定写数据地址
1、FLASH解锁
2、擦除待写区域数据
3、写入数据
4、FLASH上锁
写Flash时会用到的HAL库API接口:
//对 FLASH 进行写操作前必须先解锁,解锁操作也就是必须在 FLASH_KEYR 寄存器写入特定的序列;有解锁当然就有上锁,为了保护Flash,读写和擦除全部完需要的Flash空间后,需要上锁操作。
//FLASH解锁
HAL_StatusTypeDef HAL_FLASH_Unlock(void);
//擦除数据
void FLASH_PageErase(uint32_t Page, uint32_t Banks);
void FLASH_MassErase(uint32_t Banks);
//写数据
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data);
void FLASH_Program_Fast(uint32_t Address, uint32_t DataAddress);
//FLASH上锁
HAL_StatusTypeDef HAL_FLASH_Lock(void);
写FLASH时的标准库API接口:
//FLASH解锁
void FLASH_Unlock(void);
//擦除数据
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);
//写数据
FLASH_Status FLASH_ProgramDoubleWord(uint32_t Address, uint64_t Data);
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
FLASH_Status FLASH_ProgramByte(uint32_t Address, uint8_t Data);
//FLASH上锁
void FLASH_Lock(void);
提醒:同一个库的不同版本,API的命名也能会有变动。不同芯片其库内的函数封装也可能存在差异。
2、读Flash数据
1、确定读数据地址
2、指针偏移间接读取
3、读取数据成功
三、HAL库FLASH读写
①、flash.c
#include "flash.h"
//STM32G431RBT6的FLASH为128KB,因此FLASH地址起始地址:0x0800 0000,结束地址是:0x0802 0000
/**
* @brief HAL库版写一个uint64_t类型的数据
* @param addr: 存储数据的地址
* @param data: 写入的数据
* @retval 成功返回0, 失败返回-1
*/
int Flash_HAL_Write_Data(uint32_t addr, uint64_t data)
{
//1、FLASH解锁
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
//2、FLASH擦除
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //页擦除
EraseInitStruct.Banks = FLASH_BANK_1;
EraseInitStruct.Page = 15-1; //从第几个页开始擦除(0开始)
EraseInitStruct.NbPages = 5; //擦除多少个页
uint32_t PageError = 0; //记录擦除出错时的起始地址
if(HAL_FLASHEx_Erase(&EraseInitStruct, &PageError)!=HAL_OK)
{
printf("FLASH擦除出错,开始出错地址:%#x\r\n", PageError);
return -1;
}
//3、FLASH写入
if(HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, addr, data)!=HAL_OK)
{
printf("FLASH写入失败\r\n");
return -1;
}
//4、FLASH上锁
HAL_FLASH_Lock();
return 0;
}
/**
* @brief HAL库版写N个uint64_t类型的数据
* @param addr: 存储数据的地址
* @param data: 数据数组
* @param num: 数据的个数
* @retval 成功返回0, 失败返回-1
*/
int Flash_HAL_Write_N_Data(uint32_t addr, uint64_t *data, uint16_t num)
{
//1、FLASH解锁
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
//2、FLASH擦除
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //页擦除
EraseInitStruct.Banks = FLASH_BANK_1;
EraseInitStruct.Page = 15-1; //从第几个页开始擦除(0开始)
EraseInitStruct.NbPages = 5; //擦除多少个页
uint32_t PageError = 0; //记录擦除出错时的起始地址
if(HAL_FLASHEx_Erase(&EraseInitStruct, &PageError)!=HAL_OK)
{
printf("FLASH擦除出错,开始出错地址:%#x\r\n", PageError);
return -1;
}
//3、FLASH写入
for(uint16_t i=0; i<num; i++)
{
if(HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, addr, data[i])!=HAL_OK)
{
printf("FLASH写入失败\r\n");
return -1;
}
addr += sizeof(uint64_t);
}
//4、FLASH上锁
HAL_FLASH_Lock();
return 0;
}
/**
* @brief HAL库版读取N个uint64_t类型的数据
* @param addr: 读取数据的地址(用户空间的地址)
* @param data: 数据数组
* @param num: 数据的个数
* @retval NONE
*/
void Flash_HAL_Read_N_Data(uint32_t addr, uint64_t *data, uint32_t num)
{
for(uint32_t i=0; i<num; i++)
{
data[i] = *(volatile uint64_t*)addr;
addr += sizeof(uint64_t);//根据读取的数据类型进行内存地址递增
}
}
/**
* @brief HAL库版读取N个uint8_t类型的数据
* @param addr: 读取数据的地址
* @param data: 数据数组
* @param num: 数据的个数
* @retval NONE
*/
void Flash_HAL_Read_N_Byte(uint32_t addr, uint8_t *data, uint32_t num)
{
for(uint32_t i=0; i<num; i++)
{
data[i] = *(volatile uint8_t*)addr;
addr += sizeof(uint8_t);//根据读取的数据类型进行内存地址递增
}
}
②、flash.h
#ifndef __FLASH_H
#define __FLASH_H
#include "stm32g4xx_hal.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//STM32G431RBT6的FLASH大小为128KB,只有63个页
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 2 Kbytes */
#define ADDR_FLASH_PAGE_1 ((uint32_t)0x08000800) /* Base @ of Page 1, 2 Kbytes */
#define ADDR_FLASH_PAGE_2 ((uint32_t)0x08001000) /* Base @ of Page 2, 2 Kbytes */
#define ADDR_FLASH_PAGE_3 ((uint32_t)0x08001800) /* Base @ of Page 3, 2 Kbytes */
#define ADDR_FLASH_PAGE_4 ((uint32_t)0x08002000) /* Base @ of Page 4, 2 Kbytes */
#define ADDR_FLASH_PAGE_5 ((uint32_t)0x08002800) /* Base @ of Page 5, 2 Kbytes */
#define ADDR_FLASH_PAGE_6 ((uint32_t)0x08003000) /* Base @ of Page 6, 2 Kbytes */
#define ADDR_FLASH_PAGE_7 ((uint32_t)0x08003800) /* Base @ of Page 7, 2 Kbytes */
#define ADDR_FLASH_PAGE_8 ((uint32_t)0x08004000) /* Base @ of Page 8, 2 Kbytes */
#define ADDR_FLASH_PAGE_9 ((uint32_t)0x08004800) /* Base @ of Page 9, 2 Kbytes */
#define ADDR_FLASH_PAGE_10 ((uint32_t)0x08005000) /* Base @ of Page 10, 2 Kbytes */
#define ADDR_FLASH_PAGE_11 ((uint32_t)0x08005800) /* Base @ of Page 11, 2 Kbytes */
#define ADDR_FLASH_PAGE_12 ((uint32_t)0x08006000) /* Base @ of Page 12, 2 Kbytes */
#define ADDR_FLASH_PAGE_13 ((uint32_t)0x08006800) /* Base @ of Page 13, 2 Kbytes */
#define ADDR_FLASH_PAGE_14 ((uint32_t)0x08007000) /* Base @ of Page 14, 2 Kbytes */
#define ADDR_FLASH_PAGE_15 ((uint32_t)0x08007800) /* Base @ of Page 15, 2 Kbytes */
#define ADDR_FLASH_PAGE_16 ((uint32_t)0x08008000) /* Base @ of Page 16, 2 Kbytes */
#define ADDR_FLASH_PAGE_17 ((uint32_t)0x08008800) /* Base @ of Page 17, 2 Kbytes */
#define ADDR_FLASH_PAGE_18 ((uint32_t)0x08009000) /* Base @ of Page 18, 2 Kbytes */
#define ADDR_FLASH_PAGE_19 ((uint32_t)0x08009800) /* Base @ of Page 19, 2 Kbytes */
#define ADDR_FLASH_PAGE_20 ((uint32_t)0x0800A000) /* Base @ of Page 20, 2 Kbytes */
#define ADDR_FLASH_PAGE_21 ((uint32_t)0x0800A800) /* Base @ of Page 21, 2 Kbytes */
#define ADDR_FLASH_PAGE_22 ((uint32_t)0x0800B000) /* Base @ of Page 22, 2 Kbytes */
#define ADDR_FLASH_PAGE_23 ((uint32_t)0x0800B800) /* Base @ of Page 23, 2 Kbytes */
#define ADDR_FLASH_PAGE_24 ((uint32_t)0x0800C000) /* Base @ of Page 24, 2 Kbytes */
#define ADDR_FLASH_PAGE_25 ((uint32_t)0x0800C800) /* Base @ of Page 25, 2 Kbytes */
#define ADDR_FLASH_PAGE_26 ((uint32_t)0x0800D000) /* Base @ of Page 26, 2 Kbytes */
#define ADDR_FLASH_PAGE_27 ((uint32_t)0x0800D800) /* Base @ of Page 27, 2 Kbytes */
#define ADDR_FLASH_PAGE_28 ((uint32_t)0x0800E000) /* Base @ of Page 28, 2 Kbytes */
#define ADDR_FLASH_PAGE_29 ((uint32_t)0x0800E800) /* Base @ of Page 29, 2 Kbytes */
#define ADDR_FLASH_PAGE_30 ((uint32_t)0x0800F000) /* Base @ of Page 30, 2 Kbytes */
#define ADDR_FLASH_PAGE_31 ((uint32_t)0x0800F800) /* Base @ of Page 31, 2 Kbytes */
#define ADDR_FLASH_PAGE_32 ((uint32_t)0x08010000) /* Base @ of Page 32, 2 Kbytes */
#define ADDR_FLASH_PAGE_33 ((uint32_t)0x08010800) /* Base @ of Page 33, 2 Kbytes */
#define ADDR_FLASH_PAGE_34 ((uint32_t)0x08011000) /* Base @ of Page 34, 2 Kbytes */
#define ADDR_FLASH_PAGE_35 ((uint32_t)0x08011800) /* Base @ of Page 35, 2 Kbytes */
#define ADDR_FLASH_PAGE_36 ((uint32_t)0x08012000) /* Base @ of Page 36, 2 Kbytes */
#define ADDR_FLASH_PAGE_37 ((uint32_t)0x08012800) /* Base @ of Page 37, 2 Kbytes */
#define ADDR_FLASH_PAGE_38 ((uint32_t)0x08013000) /* Base @ of Page 38, 2 Kbytes */
#define ADDR_FLASH_PAGE_39 ((uint32_t)0x08013800) /* Base @ of Page 39, 2 Kbytes */
#define ADDR_FLASH_PAGE_40 ((uint32_t)0x08014000) /* Base @ of Page 40, 2 Kbytes */
#define ADDR_FLASH_PAGE_41 ((uint32_t)0x08014800) /* Base @ of Page 41, 2 Kbytes */
#define ADDR_FLASH_PAGE_42 ((uint32_t)0x08015000) /* Base @ of Page 42, 2 Kbytes */
#define ADDR_FLASH_PAGE_43 ((uint32_t)0x08015800) /* Base @ of Page 43, 2 Kbytes */
#define ADDR_FLASH_PAGE_44 ((uint32_t)0x08016000) /* Base @ of Page 44, 2 Kbytes */
#define ADDR_FLASH_PAGE_45 ((uint32_t)0x08016800) /* Base @ of Page 45, 2 Kbytes */
#define ADDR_FLASH_PAGE_46 ((uint32_t)0x08017000) /* Base @ of Page 46, 2 Kbytes */
#define ADDR_FLASH_PAGE_47 ((uint32_t)0x08017800) /* Base @ of Page 47, 2 Kbytes */
#define ADDR_FLASH_PAGE_48 ((uint32_t)0x08018000) /* Base @ of Page 48, 2 Kbytes */
#define ADDR_FLASH_PAGE_49 ((uint32_t)0x08018800) /* Base @ of Page 49, 2 Kbytes */
#define ADDR_FLASH_PAGE_50 ((uint32_t)0x08019000) /* Base @ of Page 50, 2 Kbytes */
#define ADDR_FLASH_PAGE_51 ((uint32_t)0x08019800) /* Base @ of Page 51, 2 Kbytes */
#define ADDR_FLASH_PAGE_52 ((uint32_t)0x0801A000) /* Base @ of Page 52, 2 Kbytes */
#define ADDR_FLASH_PAGE_53 ((uint32_t)0x0801A800) /* Base @ of Page 53, 2 Kbytes */
#define ADDR_FLASH_PAGE_54 ((uint32_t)0x0801B000) /* Base @ of Page 54, 2 Kbytes */
#define ADDR_FLASH_PAGE_55 ((uint32_t)0x0801B800) /* Base @ of Page 55, 2 Kbytes */
#define ADDR_FLASH_PAGE_56 ((uint32_t)0x0801C000) /* Base @ of Page 56, 2 Kbytes */
#define ADDR_FLASH_PAGE_57 ((uint32_t)0x0801C800) /* Base @ of Page 57, 2 Kbytes */
#define ADDR_FLASH_PAGE_58 ((uint32_t)0x0801D000) /* Base @ of Page 58, 2 Kbytes */
#define ADDR_FLASH_PAGE_59 ((uint32_t)0x0801D800) /* Base @ of Page 59, 2 Kbytes */
#define ADDR_FLASH_PAGE_60 ((uint32_t)0x0801E000) /* Base @ of Page 60, 2 Kbytes */
#define ADDR_FLASH_PAGE_61 ((uint32_t)0x0801E800) /* Base @ of Page 61, 2 Kbytes */
#define ADDR_FLASH_PAGE_62 ((uint32_t)0x0801F000) /* Base @ of Page 62, 2 Kbytes */
#define ADDR_FLASH_PAGE_63 ((uint32_t)0x0801F800) /* Base @ of Page 63, 2 Kbytes */
#define FLASH_USER_START_ADDR ADDR_FLASH_PAGE_15 /* Start @ of user Flash area */
#define FLASH_USER_END_ADDR ADDR_FLASH_PAGE_18 /* End @ of user Flash area */
int Flash_HAL_Write_Data(uint32_t addr, uint64_t data);
int Flash_HAL_Write_N_Data(uint32_t addr, uint64_t *data, uint16_t num);
void Flash_HAL_Read_N_Data(uint32_t addr, uint64_t *data, uint32_t num);
void Flash_HAL_Read_N_Byte(uint32_t addr, uint8_t *data, uint32_t num);
#endif
③、字符串读写测试
④、整形数读写测试
四、标志库FLASH读写
①、flash.c
#include "flash.h"
/**
* @brief 清除用户FLASH扇区的数据
* @param NONE
* @retval NONE
*/
int Flash_Clean_User_Area_Data(uint32_t addr)
{
//1、FLASH解锁
FLASH_Unlock();
//2、FLASH数据擦除
if(FLASH_EraseSector(Flash_Addr_Get_Sector(addr), VoltageRange_3) != FLASH_COMPLETE)
{
printf("FLASH擦除出错\r\n");
//4、FLASH上锁
FLASH_Lock();
return -1;
}
//4、FLASH上锁
FLASH_Lock();
return 0;
}
/**
* @brief 写N个字节(uint8_t)的数据
* @param addr: 存储数据的地址
* @param data: 数据数组
* @param num: 数据的个数
* @retval 成功返回0,失败返回-1
*/
int Flash_Write_N_Byte(uint32_t addr, uint8_t *data, uint16_t num)
{
//1、FLASH解锁
FLASH_Unlock();
//2、擦除数据
//数据擦除操作会将一整个扇区擦除,如果需要连续写,最初用一次就行了
//3、FLASH写入
for(uint16_t i=0; i<num; i++)
{
if(FLASH_ProgramByte(addr, data[i]) != FLASH_COMPLETE)
{
printf("写多字节Byte数据失败\r\n");
FLASH_Lock();
return -1;
}
addr += sizeof(uint8_t);
}
//4、FLASH上锁
FLASH_Lock();
return 0;
}
/**
* @brief 写N个半字(uint16_t)的数据
* @param addr: 存储数据的地址
* @param data: 数据数组
* @param num: 数据的个数
* @retval 成功返回0,失败返回-1
*/
int Flash_Write_N_HalfWord(uint32_t addr, uint16_t *data, uint16_t num)
{
//1、FLASH解锁
FLASH_Unlock();
//2、擦除数据
//数据擦除操作会将一整个扇区擦除,如果需要连续写,最初用一次就行了
//3、FLASH写入
for(uint16_t i=0; i<num; i++)
{
if(FLASH_ProgramHalfWord(addr, data[i]) != FLASH_COMPLETE)
{
printf("写多个半字HalfWord数据失败\r\n");
FLASH_Lock();
return -1;
}
addr += sizeof(uint16_t);
}
//4、FLASH上锁
FLASH_Lock();
return 0;
}
/**
* @brief 写N个字(uint32_t)的数据
* @param addr: 存储数据的地址
* @param data: 数据数组
* @param num: 数据的个数
* @retval 成功返回0,失败返回-1
*/
int Flash_Write_N_Word(uint32_t addr, uint32_t *data, uint16_t num)
{
//1、FLASH解锁
FLASH_Unlock();
//2、擦除数据
//数据擦除操作会将一整个扇区擦除,如果需要连续写,最初用一次就行了
//3、FLASH写入
for(uint16_t i=0; i<num; i++)
{
if(FLASH_ProgramWord(addr, data[i]) != FLASH_COMPLETE)
{
printf("写多个字Word数据失败\r\n");
FLASH_Lock();
return -1;
}
addr += sizeof(uint32_t);
}
//4、FLASH上锁
FLASH_Lock();
return 0;
}
/**
* @brief 读N个字节(uint8_t)的数据
* @param addr: 数据的存储地址
* @param data: 数据数组
* @param num: 需要读取的数据个数
* @retval 成功返回0,失败返回-1
*/
void Flash_Read_N_Byte(uint32_t addr, uint8_t *data, uint16_t num)
{
for(uint16_t i=0; i<num; i++)
{
data[i] = *(volatile uint8_t*)addr;
addr += sizeof(uint8_t);//根据读取的数据类型进行内存地址递增
}
}
/**
* @brief 读N个半字(uint16_t)的数据
* @param addr: 数据的存储地址
* @param data: 数据数组
* @param num: 需要读取的数据个数
* @retval 成功返回0,失败返回-1
*/
void Flash_Read_N_HalfWord(uint32_t addr, uint16_t *data, uint16_t num)
{
for(uint16_t i=0; i<num; i++)
{
data[i] = *(volatile uint16_t*)addr;
addr += sizeof(uint16_t);//根据读取的数据类型进行内存地址递增
}
}
/**
* @brief 读N个字(uint32_t)的数据
* @param addr: 数据的存储地址
* @param data: 数据数组
* @param num: 需要读取的数据个数
* @retval 成功返回0,失败返回-1
*/
void Flash_Read_N_Word(uint32_t addr, uint32_t *data, uint16_t num)
{
for(uint16_t i=0; i<num; i++)
{
data[i] = *(volatile uint32_t*)addr;
addr += sizeof(uint32_t);//根据读取的数据类型进行内存地址递增
}
}
/**
* @brief 计算FLASH地址所在的扇区
* @param addr:FLASH地址
* @retval 返回所在扇区数
*/
uint32_t Flash_Addr_Get_Sector(uint32_t addr)
{
uint32_t sector = 0;
if((addr < ADDR_FLASH_SECTOR_1) && (addr >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_Sector_0;
}
else if((addr < ADDR_FLASH_SECTOR_2) && (addr >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_Sector_1;
}
else if((addr < ADDR_FLASH_SECTOR_3) && (addr >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_Sector_2;
}
else if((addr < ADDR_FLASH_SECTOR_4) && (addr >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_Sector_3;
}
else if((addr < ADDR_FLASH_SECTOR_5) && (addr >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_Sector_4;
}
else if((addr < ADDR_FLASH_SECTOR_6) && (addr >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_Sector_5;
}
else if((addr < ADDR_FLASH_SECTOR_7) && (addr >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_Sector_6;
}
else if((addr < ADDR_FLASH_SECTOR_8) && (addr >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_Sector_7;
}
}
②、flash.h
#ifndef __FLASH_H
#define __FLASH_H
#include "stm32f4xx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//STM32F407VET6的FLASH内存空间大小为512KB,起始地址:0x0800 0000,结束地址:0x0807 FFFF
/* Base address of the Flash sectors */
#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 */
//用户自由使用的FLASH起始地址,需要根据实际代码占用的内存空间进行变动
#define FLASH_USER_START_ADDR ADDR_FLASH_SECTOR_5 /* Start address of user Flash area */
#define FLASH_USER_END_ADDR ADDR_FLASH_SECTOR_6 /* End address of user Flash area */
int Flash_Clean_User_Area_Data(uint32_t addr);
int Flash_Write_N_Byte(uint32_t addr, uint8_t *data, uint16_t num);
int Flash_Write_N_HalfWord(uint32_t addr, uint16_t *data, uint16_t num);
int Flash_Write_N_Word(uint32_t addr, uint32_t *data, uint16_t num);
void Flash_Read_N_Byte(uint32_t addr, uint8_t *data, uint16_t num);
void Flash_Read_N_HalfWord(uint32_t addr, uint16_t *data, uint16_t num);
void Flash_Read_N_Word(uint32_t addr, uint32_t *data, uint16_t num);
uint32_t Flash_Addr_Get_Sector(uint32_t addr);
#endif