- /*
- * @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;
- }