/*
* @Author: duanhuisheng
* @Date: 2021-11-03 21:36:14
* @LastEditTime: 2022-05-24 18:22:23
* @LastEditors: duanhuisheng
* @Description:
* stm32f407用于参数平衡擦写读写flash的注意事项: 解锁前一定要确定是锁住的,或者执行解锁指令会产生 hardfauat
* 错误中断 导致程序跑飞 ,另外在擦除扇区前一定要关闭数据缓存
* 或者擦除失败,擦除完成后要执行一下缓存复位,不然擦除后去访问flash数据,最开始访问的数据可能是擦除前的数据,而不是此刻真实的flash中的数据
*
*/
#include <MString.h>
static void delay(volatile int32_t length)
{
while (length)
length--;
}
// FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 // STM32 FLASH的起始地址
// FLASH解锁键值
#define FLASH_KEY1 0X45670123
#define FLASH_KEY2 0XCDEF89AB
// 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
// 解锁STM32的FLASH
static void STMFLASH_Unlock(void)
{
FLASH->KEYR = FLASH_KEY1; // 写入解锁序列.
FLASH->KEYR = FLASH_KEY2;
}
// flash上锁
static void STMFLASH_Lock(void)
{
FLASH->CR |= (u32)1 << 31; // 上锁
}
// 得到FLASH状态
// 返回值:
// 0,操作完成
// 1,忙
// 2,操作异常
static uint8_t STMFLASH_GetStatus(void)
{
u32 res = 0;
res = FLASH->SR;
if (res & (1 << 16))
return 1; // 忙
else if (res & (1 << 4))
return 2; // 操作异常
else if (res & (1 << 5))
return 2; // 操作异常
else if (res & (1 << 6))
return 2; // 操作异常
else if (res & (1 << 7))
return 2; // 操作异常
return 0; // 没有任何状态/操作完成.
}
// 等待操作完成
// time:要延时的长短(单位:10us)
// 返回值:
// 0,完成
// 2,操作异常
// 0XFF,超时
static uint8_t STMFLASH_WaitDone(u32 time)
{
uint8_t res;
do
{
res = STMFLASH_GetStatus();
if (res != 1)
break; // 非忙,无需等待了,直接退出.
// delay_us(10);
time--;
} while (time);
if (time == 0)
res = 0xff; // TIMEOUT
return res;
}
// 在FLASH指定地址写一个字
// faddr:指定地址(此地址必须为4的倍数!!)
// dat:要写入的数据
// 返回值:0,写入成功
// 其他,写入失败
static uint8_t STMFLASH_WriteWord(u32 faddr, u32 dat)
{
uint8_t res;
FLASH->SR |= 0xc0;
res = STMFLASH_WaitDone(0XFFFFFf);
if (res == 0) // OK
{
FLASH->CR &= ~(3 << 8); // 清除PSIZE原来的设置
FLASH->CR |= 2 << 8;
FLASH->CR |= 1 << 0; // 编程使能
while ((FLASH->CR & 0x01) != 0x01)
{
FLASH->CR |= 1 << 0;
}
*(vu32 *)faddr = dat; // 写入数据
res = STMFLASH_WaitDone(0XFFFFFf); // 等待操作完成,一个字编程,最多100us.
if (res != 1) // 操作成功
{
FLASH->CR &= ~(1 << 0); // 清除PG位.
}
}
return res;
}
// 擦除扇区
// sectoraddr:扇区地址,范围是:0~11.
// 0~3,16K扇区;4,64K扇区;5~11,128K扇区.
// 返回值:执行情况
static uint8_t STMFLASH_EraseSector(u32 sectoraddr)
{
uint8_t res = 0;
res = STMFLASH_WaitDone(5000000000); // 等待上次操作结束,最大5s
if (res == 0)
{
FLASH->CR &= ~(3 << 8); // 清除PSIZE原来的设置
FLASH->CR |= 2 << 8; // 设置为32bit宽,确保VCC=2.7~3.6V之间!!
FLASH->CR &= ~(0X1F << 3); // 清除原来的设置
FLASH->CR |= sectoraddr << 3; // 设置要擦除的扇区
FLASH->CR |= 1 << 1; // 扇区擦除
FLASH->CR |= 1 << 16; // 开始擦除
res = STMFLASH_WaitDone(5000000000); // 等待操作结束,最大5s
if (res != 1) // 非忙
{
FLASH->CR &= ~(1 << 1); // 清除扇区擦除标志.
}
}
return res;
}
void CHAL_Flash_init(void)
{
STMFLASH_Lock(); //
/* 解锁前必须要确保是已经上锁的 或者执行解锁指令会导致程序进入 hard_fault */
STMFLASH_Unlock(); // 解锁
FLASH->CR |= 2 << 8; // 设置为32bit宽,确保VCC=2.7~3.6V之间!!
}
uint32_t CHAL_Flash_Rd(void *addr, void *buffer, uint32_t size)
{
uint32_t len = size;
uint8_t *sou = (uint8_t *)addr;
uint8_t *des = (uint8_t *)buffer;
// if(FLASH->CR&0x80000000)
// {
// STMFLASH_Unlock(); // 解锁
// }
while (len--)
{
*des++ = *sou++;
}
return size;
}
uint8_t CHAL_Flash_EraseSector(uint8_t *addr, uint32_t sct)
{
int i = 0;
int ret = 1;
__disable_irq();
if (FLASH->CR & 0x80000000)
{
STMFLASH_Unlock(); // 解锁
}
// delay(1000);
FLASH->CR &= ~(3 << 8); // 清除PSIZE原来的设置
FLASH->CR |= 2 << 8; // 设置为32bit宽,确保VCC=2.7~3.6V之间!!
FLASH->ACR &= ~(1 << 10); // FLASH擦除期间,必须禁止数据缓存!!!搞了我两晚上才发现这个问题!
if ((uint32_t)(addr) < ADDR_FLASH_SECTOR_3)
{
STMFLASH_EraseSector(2);
}
else
{
STMFLASH_EraseSector(3);
}
FLASH->ACR |= 1 << 12; // 数据缓存复位
delay(10);
FLASH->ACR &= ~(1 << 12); // 一定要执行缓存复位 有可能导致 读flash的数据可能是擦除前缓存的数据
FLASH->ACR |= 1 << 10; // FLASH擦除结束,开启数据fetch
delay(10);
// delay(1000);
STMFLASH_Lock(); // 上锁
__enable_irq(); // 开启总中
return ret;
}
uint32_t CHAL_Flash_Wt(void *addr, const void *buffer, uint32_t size)
{
int i = 0, len = size / 4;
if (FLASH->CR & 0x80000000)
{
STMFLASH_Unlock(); // 解锁
}
__disable_irq();
/* Program data. */
FLASH->ACR &= ~(1 << 10);
for (i = 0; i < len; i++)
{
STMFLASH_WriteWord((uint32_t)addr + i * 4, ((uint32_t *)buffer)[i]);
}
FLASH->ACR |= 1 << 10; // FLASH擦除结束,开启数据fetch
STMFLASH_Lock(); // 上
// delay(1000);
__enable_irq(); // 开启总中
return size;
}