STM32标准库+HAL库 | CPU片内FLASH存储器数据掉电读写

一、片内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

③、字符串读写测试

④、整形数读写测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/820510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArduPilot开源飞控之ROS系统简介

ArduPilot开源飞控之ROS系统简介 1. 源由2. ROS系统3. 安装2.1 安装Docker2.2 安装ROS2 4. 总结5. 补充资料 1. 源由 之前在ArduPilot开源飞控之硬件SBC分析中讨论过&#xff0c;个人角度最推荐其中两个系统是&#xff1a; Rpanion-server【推荐&#xff0c;简单】BlueOS【推…

Unity之Unity面试题(四)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Unity面试题&#xff08;四&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取…

什么是并行通信、串行通信?什么是全双工、半双工、单工? 什么是异步通信、同步通信? 什么是RS232、RS485?什么是pwm?

什么是并行通信、串行通信&#xff1f; 嵌入式系统中的通信是指两个或两个以上的主机之间的数据互交&#xff0c;这里的主机可以是计算机也可以是嵌入式主机&#xff0c;甚至可以是芯片。主机间通信的方式一般可以分为两类&#xff1a;并行通信和串行通信。并行通信是指多个比特…

华为配置静态ARP示例

华为配置静态ARP示例 组网图形 图1 配置静态ARP组网图 静态ARP简介配置注意事项组网需求配置思路操作步骤配置文件相关信息 静态ARP简介 静态ARP表项是指网络管理员手工建立IP地址和MAC地址之间固定的映射关系。 正常情况下网络中设备可以通过ARP协议进行ARP表项的动态学习&…

论文略读:Window Attention is Bugged: How not to Interpolate Position Embeddings

iclr 2024 reviewer 打分 6666 窗口注意力、位置嵌入以及高分辨率微调是现代Transformer X CV 时代的核心概念。论文发现&#xff0c;将这些几乎无处不在的组件简单地结合在一起&#xff0c;可能会对性能产生不利影响问题很简单&#xff1a;在使用窗口注意力时对位置嵌入进行插…

华为再次布局新行业:合作伙伴已超前谋划,该领域将大有可为

华为布局新行业 华为向外界公布了一个重要信息&#xff1a;在过去的三年里&#xff0c;尽管受到美国的制裁&#xff0c;华为仍然成功地完成了超过13000个元器件的国产替代研发&#xff0c;以及4000多块电路板的迭代开发。 不仅在硬件领域取得了显著成就&#xff0c;在软件和生…

oracle 19c数据库W00n进程使用很多PGA内存资源的分析

今天&#xff0c;客户反馈测试环境的数据库PGA资源不足&#xff0c;报错ORA-04036: 实例使用的 PGA 内存超出 PGA_AGGREGATE_LIMIT&#xff1b;分析是多个W00n进程使用大量PGA-触发了BUG&#xff0c;对应解决办法就是打补丁。&#xff08;民间办法就是KILL进程、重启数据库&…

3d视图模型乱了怎么调?---模大狮模型网

在进行3D建模时&#xff0c;有时候您可能会遇到视图模型混乱的情况。这可能是由于模型结构问题、导入导出错误或编辑操作不当等原因造成的。混乱的模型不仅影响工作效率&#xff0c;还可能导致渲染结果不理想。本文将介绍六种有效的方法来调整混乱的3D视图模型&#xff0c;帮助…

【数据可视化包Matplotlib】Matplotlib基本绘图方法

目录 一、Matplotlib绘图的基本流程&#xff08;一&#xff09;最简单的绘图&#xff08;仅指定y的值&#xff09;&#xff08;二&#xff09;更一般的绘图&#xff08;同时指定x和y的值&#xff09;&#xff08;三&#xff09;增加更多的绘图元素 二、布局相关的对象——Figur…

Python 物联网入门指南(四)

原文&#xff1a;zh.annas-archive.org/md5/4fe4273add75ed738e70f3d05e428b06 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第九章&#xff1a;构建光学字符识别的神经网络模块 本章介绍以下主题&#xff1a; 使用光学字符识别&#xff08;OCR&#xff09;系统 使…

多种方式打开SOLIDWORKS文件

在 SOLIDWORKS 中有多种打开文件的方法。一些最常用的方法包括双击文件资源管理器中的文件或拖放到 SOLIDWORKS 窗口中。当然&#xff0c;还有一种传统的方法&#xff0c;就是在SOLIDWORKS软件上方单击打开。 使用SOLIDWORKS“打开“命令 SOLIDWORKS 中的“打开“命令与任何其…

基于springboot实现在线考试系统设计【项目源码+论文说明】

基于springboot实现在线考试管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于JavaWeb技术的在线考试系统设计与实现的开发全过程。通过分析基于Java Web技术的在线考试系统设计与实现管理的不…

Python不可变序列类型--字符串

🥇作者简介:CSDN内容合伙人、新星计划第三季Python赛道Top1 🔥本文已收录于Python系列专栏: 零基础学Python 💬订阅专栏后可私信博主进入Python学习交流群,进群可领取Python视频教程以及Python相关电子书合集 私信未回可以加V:hacker0327 备注零基础学Python 订阅专…

ObjectMapper的具体介绍与使用

文章目录 声明一、前言二、ObjectMapper与JSONObject比较1、核心主要有三个部分&#xff1a;依赖包不同 2、ObjectMapper使用概述2.1、工程的pom.xml导包信息2.2、创建案例中的测试对象2.3、对象和JSON相互转化2.3.1、测试代码2.3.2、测试结果展示 2.4、集合和JSON像话转化2.4.…

Matlab|电价型负荷需求响应(考虑电价变化)

程序复现来源于《计及需求响应消纳风电的电-热综合能源系统经济调度 》第四章内容。 一、原理 需求响应的基本原理是需求侧根据电力市场价格和电网要求改变其负荷需求以 获取一定的利益回报。其中 PDR 可通过直观的电价变化信号引导用户调节用电方式&#xff0c; 从而达到优…

Qt for Android 开发环境

在搭建环境时开始感觉还挺顺利的&#xff0c;从 Qt 配置的环境里面看并没有什么问题&#xff0c;可真正编译程序的时候发现全是错误。 最开始的时候安装了 JDK21 最新版本&#xff0c;然后根据 JDK21 安装 ndk, build-tools, Platform-Tools 和 Gradle&#xff0c;但是不管这么…

零基础自学Python,啃透这五本书就够了!

选择合适的学习资源 在自学Python的前期&#xff0c;选择一本适合初学者的Python入门书籍或在线教程&#xff0c;从基础开始学习&#xff0c;好的入门书籍或在线教程会按照逻辑顺序组织知识&#xff0c;从基础概念开始&#xff0c;逐步引导你深入学习Python编程语言。这种系统…

如何在深度学习中调用CAME

1、介绍 CAME&#xff1a;一种以置信度为导向的策略&#xff0c;以减少现有内存高效优化器的不稳定性。基于此策略&#xff0c;我们提出CAME同时实现两个目标:传统自适应方法的快速收敛和内存高效方法的低内存使用。大量的实验证明了CAME在各种NLP任务(如BERT和GPT-2训练)中的…

必应bing竞价广告推广开户联系方式?

随着互联网广告市场的日益繁荣与细分&#xff0c;必应Bing作为全球重要的搜索引擎之一&#xff0c;在国内市场也逐渐展现出强大的潜力与吸引力。越来越多的企业开始关注并探索必应Bing搜索广告所带来的巨大商机。其中&#xff0c;云衔科技以其卓越的专业素养和全面的服务体系&a…

stable diffusion--小白学习步骤

1.看一下Unet网络的讲解_哔哩哔哩_bilibili&#xff0c;了解Unet网络 2.看一下【生成式AI】Diffusion Model 原理剖析 (1/4)_哔哩哔哩_bilibili&#xff0c;起码要看前3/6个视频 3.看一下超详细的扩散模型&#xff08;Diffusion Models&#xff09;原理代码 - 知乎 (zhihu.co…