前言
- 使用的开发工具(clion+msys2+openocd)
- 使用的开发版芯片stm32f407vet6
- 参考手册为stm32f4中文参考文档
查看中文手册
## 驱动代码
头文件(bsp_flash.h)
#ifndef STM32F103VET6_PROJECT_BSP_FLASH_H
#define STM32F103VET6_PROJECT_BSP_FLASH_H
#include "sys.h"
//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
#define STM32_FLASH_END 0x0807FFFFUL
#define FLASH_WAITETIME 50000 //FLASH等待超时时间
//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 Kbytes
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef volatile uint32_t vu32;
typedef volatile uint16_t vu16;
typedef volatile uint8_t vu8;
u32 STMFLASH_ReadBase(u32 Address, void *Buffer, u32 Size);
bool STMFLASH_WriteBase(u32 TypeProgram, u32 WriteAddr, void *pBuffer, u16 byteNum);
bool STMFLASH_Write_u8(u32 WriteAddr, u8 *pBuffer, u16 Size);
bool STMFLASH_Write_u16(u32 WriteAddr, u16 *pBuffer, u16 Size);
bool STMFLASH_Write_u32(u32 WriteAddr, u32 *pBuffer, u16 Size);
bool STMFLASH_Write_u64(u32 WriteAddr, u64 *pBuffer, u16 Size);
#endif //STM32F103VET6_PROJECT_BSP_FLASH_H
源文件(bsp_flash.c)
#include "bsp_flash.h"
static HAL_StatusTypeDef p_Status;
//读取指定地址的字(32位数据)
//faddr:读地址
//返回值:对应数据.
u32 STM32_FLASH_ReadWord(u32 faddr) {
return *(vu32 *) faddr;
}
//获取某个地址所在的flash扇区
//addr:flash地址
//返回值:0~11,即addr所在的扇区
u8 STM32_FLASH_GetFlashSector(u32 addr) {
if (addr < ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
else if (addr < ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
else if (addr < ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
else if (addr < ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;
else if (addr < ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
else if (addr < ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
else if (addr < ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
else if (addr < ADDR_FLASH_SECTOR_8)return FLASH_SECTOR_7;
else if (addr < ADDR_FLASH_SECTOR_9)return FLASH_SECTOR_8;
else if (addr < ADDR_FLASH_SECTOR_10)return FLASH_SECTOR_9;
else if (addr < ADDR_FLASH_SECTOR_11)return FLASH_SECTOR_10;
return FLASH_SECTOR_11;
}
/**
* 获取地址递增数
* @param TypeProgram
* @return
*/
static inline u8 GetIncreasingCnt(uint32_t TypeProgram) {
switch (TypeProgram) {
case FLASH_TYPEPROGRAM_BYTE:
return 1;
case FLASH_TYPEPROGRAM_HALFWORD:
return 3;
case FLASH_TYPEPROGRAM_WORD:
return 4;
default:
return 8;
}
}
/**********************************************************************************
* 函数功能:不检查的写入
* 输入参数: WriteAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数
* 返 回 值: 无
* 说 明:
*/
HAL_StatusTypeDef write_no_check(uint32_t TypeProgram, uint32_t WriteAddr, void *pBuffer, uint16_t NumToWrite) {
uint8_t cnt = GetIncreasingCnt(TypeProgram);
for (uint16_t i = 0; i < NumToWrite; i++) {
switch (TypeProgram) {
case FLASH_TYPEPROGRAM_BYTE:
p_Status = HAL_FLASH_Program(TypeProgram, WriteAddr, ((u8 *) pBuffer)[i]);
break;
case FLASH_TYPEPROGRAM_HALFWORD:
p_Status = HAL_FLASH_Program(TypeProgram, WriteAddr, ((u16 *) pBuffer)[i]);
break;
case FLASH_TYPEPROGRAM_WORD:
p_Status = HAL_FLASH_Program(TypeProgram, WriteAddr, ((u32 *) pBuffer)[i]);
break;
default:
p_Status = HAL_FLASH_Program(TypeProgram, WriteAddr, ((u64 *) pBuffer)[i]);
break;
}
if (p_Status != HAL_OK) {
return p_Status;
}
WriteAddr += cnt; //地址增加cnt.
}
return HAL_OK;
}
/**
* 读FLASH
* @param Address 地址
* @param Buffer 存放读取的数据
* @param Size 要读取的数据大小,单位字节
* @return 读出成功的字节数
*/
u32 STMFLASH_ReadBase(u32 Address, void *Buffer, u32 Size) {
u32 nread = Size;
vu8 *d = (vu8 *) Buffer;
const vu8 *s = (const vu8 *) Address;
if (!Buffer || Address < STM32_FLASH_BASE || Address >= STM32_FLASH_END)
return 0;
while (nread >= sizeof(uint32_t) && (((uint32_t) s) <= (STM32_FLASH_END - 4))) {
*(vu32 *) d = *(vu32 *) s;
d += sizeof(uint32_t);
s += sizeof(uint32_t);
nread -= sizeof(uint32_t);
}
while (nread && (((vu32) s) < STM32_FLASH_END)) {
*d++ = *s++;
nread--;
}
return Size - nread;
}
//从指定地址开始写入指定长度的数据
//特别注意:因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数
// 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
// 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
// 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
//该函数对OTP区域也有效!可以用来写OTP区!
//OTP区域地址范围:0X1FFF7800~0X1FFF7A0F(注意:最后16字节,用于OTP数据块锁定,别乱写!!)
bool STMFLASH_WriteBase(u32 TypeProgram, u32 WriteAddr, void *pBuffer, u16 byteNum) {
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus;
u32 SectorError = 0;
u32 addrx = 0;
u32 endAddr = 0;
if (WriteAddr < STM32_FLASH_BASE || WriteAddr % 4)return false; //非法地址
HAL_FLASH_Unlock(); //解锁
addrx = WriteAddr; //写入的起始地址
endAddr = WriteAddr + byteNum; //写入的结束地址
if (addrx < 0X1FFF0000) {
uint8_t cnt = GetIncreasingCnt(TypeProgram);
while (addrx < endAddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if (STM32_FLASH_ReadWord(addrx) != 0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
FlashEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除
FlashEraseInit.Sector = STM32_FLASH_GetFlashSector(addrx); //要擦除的扇区
FlashEraseInit.NbSectors = 1; //一次只擦除一个扇区
FlashEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
if (HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError) != HAL_OK) {
break;//发生错误了
}
} else addrx += cnt;
FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
}
}
FlashStatus = FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
if (FlashStatus == HAL_OK) {
FlashStatus = write_no_check(TypeProgram, WriteAddr, pBuffer, byteNum);
}
HAL_FLASH_Lock(); //上锁
return FlashStatus == HAL_OK;
}
bool STMFLASH_Write_u8(u32 WriteAddr, u8 *pBuffer, u16 Size) {
return STMFLASH_WriteBase(FLASH_TYPEPROGRAM_BYTE, WriteAddr, pBuffer, Size);
}
bool STMFLASH_Write_u16(u32 WriteAddr, u16 *pBuffer, u16 Size) {
return STMFLASH_WriteBase(FLASH_TYPEPROGRAM_HALFWORD, WriteAddr, pBuffer, Size);
}
bool STMFLASH_Write_u32(u32 WriteAddr, u32 *pBuffer, u16 Size) {
return STMFLASH_WriteBase(FLASH_TYPEPROGRAM_WORD, WriteAddr, pBuffer, Size);
}
bool STMFLASH_Write_u64(u32 WriteAddr, u64 *pBuffer, u16 Size) {
return STMFLASH_WriteBase(FLASH_TYPEPROGRAM_DOUBLEWORD, WriteAddr, pBuffer, Size);
}