206 lines
5.5 KiB
C
206 lines
5.5 KiB
C
#include "app_flash.h"
|
|
#include "stm32f1xx_hal.h"
|
|
#include <string.h>
|
|
|
|
/* ============================================================
|
|
* STM32F103C8T6 Flash 读写模块
|
|
*
|
|
* Flash 布局:
|
|
* 0x08000000 - 0x0800EFFF 程序代码 (60KB)
|
|
* 0x0800F000 - 0x0800FFFF 用户数据 (4KB, 最后4页)
|
|
*
|
|
* Flash 特性:
|
|
* - 读取: 直接指针访问,无需解锁
|
|
* - 写入: 只能将1写成0,写入前必须先擦除(擦除后全0xFF)
|
|
* - 擦除: 按页擦除(1KB/页)
|
|
* - 编程: 按字(32bit)/半字(16bit)/字节(8bit)编程
|
|
* ============================================================ */
|
|
|
|
/* ===================== 读取接口 ===================== */
|
|
|
|
/* 读取单个字节 */
|
|
uint8_t flash_read_byte(uint32_t addr)
|
|
{
|
|
return *(volatile uint8_t *)addr;
|
|
}
|
|
|
|
/* 读取半字(16bit) */
|
|
uint16_t flash_read_halfword(uint32_t addr)
|
|
{
|
|
return *(volatile uint16_t *)addr;
|
|
}
|
|
|
|
/* 读取字(32bit) */
|
|
uint32_t flash_read_word(uint32_t addr)
|
|
{
|
|
return *(volatile uint32_t *)addr;
|
|
}
|
|
|
|
/* 读取任意长度数据 */
|
|
void flash_read_bytes(uint32_t addr, uint8_t *buf, uint32_t len)
|
|
{
|
|
memcpy(buf, (const void *)addr, len);
|
|
}
|
|
|
|
/* ===================== 擦除接口 ===================== */
|
|
|
|
/* 擦除指定页(addr必须是页起始地址) */
|
|
uint8_t flash_erase_page(uint32_t page_addr)
|
|
{
|
|
FLASH_EraseInitTypeDef erase_init;
|
|
uint32_t page_error = 0;
|
|
|
|
/* 地址合法性检查 */
|
|
if (page_addr < FLASH_BASE_ADDR || page_addr >= FLASH_BASE_ADDR + FLASH_TOTAL_SIZE)
|
|
return 0;
|
|
|
|
/* 必须页对齐 */
|
|
if (page_addr % FLASH_PAGE_SIZE != 0)
|
|
return 0;
|
|
|
|
erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
|
|
erase_init.Banks = FLASH_BANK_1;
|
|
erase_init.PageAddress = page_addr;
|
|
erase_init.NbPages = 1;
|
|
|
|
HAL_FLASH_Unlock();
|
|
HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase_init, &page_error);
|
|
HAL_FLASH_Lock();
|
|
|
|
return (status == HAL_OK && page_error == 0xFFFFFFFFU) ? 1 : 0;
|
|
}
|
|
|
|
/* ===================== 写入接口 ===================== */
|
|
uint8_t flash_write_word_root(uint32_t addr, uint32_t data)
|
|
{
|
|
HAL_FLASH_Unlock();
|
|
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data) != HAL_OK)
|
|
{
|
|
HAL_FLASH_Lock();
|
|
return 0;
|
|
}
|
|
HAL_FLASH_Lock();
|
|
return 1;
|
|
}
|
|
|
|
/* 按字(32bit)编程 */
|
|
uint8_t flash_write_word(uint32_t addr, uint32_t data)
|
|
{
|
|
/* 地址合法性检查 */
|
|
if (addr < FLASH_BASE_ADDR || addr >= FLASH_BASE_ADDR + FLASH_TOTAL_SIZE)
|
|
return 0;
|
|
|
|
/* 必须字对齐 */
|
|
if (addr % 4 != 0)
|
|
return 0;
|
|
|
|
HAL_FLASH_Unlock();
|
|
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data);
|
|
HAL_FLASH_Lock();
|
|
|
|
return (status == HAL_OK) ? 1 : 0;
|
|
}
|
|
|
|
/* 写入任意长度数据(自动按字编程,不足4字节补0xFF) */
|
|
uint8_t flash_write_bytes(uint32_t addr, const uint8_t *data, uint32_t len)
|
|
{
|
|
uint32_t i = 0;
|
|
uint32_t word_data;
|
|
|
|
/* 地址合法性检查 */
|
|
if (addr < FLASH_BASE_ADDR || addr + len > FLASH_BASE_ADDR + FLASH_TOTAL_SIZE)
|
|
return 0;
|
|
|
|
/* 写入前必须确保目标区域已擦除(全0xFF) */
|
|
HAL_FLASH_Unlock();
|
|
|
|
/* 处理非字对齐的起始地址: 按半字编程到字对齐 */
|
|
/* STM32F1 Flash 只支持半字(16bit)编程,不支持字节编程 */
|
|
while ((addr + i) % 4 != 0 && i + 2 <= len)
|
|
{
|
|
uint16_t hw = *(const uint16_t *)&data[i];
|
|
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK)
|
|
{
|
|
HAL_FLASH_Lock();
|
|
return 0;
|
|
}
|
|
i += 2;
|
|
}
|
|
|
|
/* 按字(32bit)批量编程 */
|
|
while (i + 4 <= len)
|
|
{
|
|
word_data = *(const uint32_t *)&data[i];
|
|
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, word_data) != HAL_OK)
|
|
{
|
|
HAL_FLASH_Lock();
|
|
return 0;
|
|
}
|
|
i += 4;
|
|
}
|
|
|
|
/* 处理剩余不足4字节的部分: 按半字编程 */
|
|
while (i + 2 <= len)
|
|
{
|
|
uint16_t hw = *(const uint16_t *)&data[i];
|
|
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK)
|
|
{
|
|
HAL_FLASH_Lock();
|
|
return 0;
|
|
}
|
|
i += 2;
|
|
}
|
|
|
|
/* 剩余1字节(奇数长度): 补0xFF组成半字编程,保持高字节擦除状态 */
|
|
if (i < len)
|
|
{
|
|
uint16_t hw = (uint16_t)data[i] | 0xFF00U;
|
|
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK)
|
|
{
|
|
HAL_FLASH_Lock();
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
HAL_FLASH_Lock();
|
|
return 1;
|
|
}
|
|
|
|
/* ===================== 便捷接口 ===================== */
|
|
|
|
/* 从用户数据区读取(offset 相对 0x0800F000) */
|
|
uint8_t flash_read_user(uint16_t offset, uint8_t *buf, uint16_t len)
|
|
{
|
|
if (offset + len > FLASH_USER_PAGE_NUM * FLASH_PAGE_SIZE)
|
|
return 0;
|
|
|
|
flash_read_bytes(FLASH_USER_START_ADDR + offset, buf, len);
|
|
return 1;
|
|
}
|
|
|
|
/* 写入用户数据区(自动擦除所在页) */
|
|
uint8_t flash_write_user(uint16_t offset, const uint8_t *buf, uint16_t len)
|
|
{
|
|
uint32_t write_addr;
|
|
|
|
if (offset + len > FLASH_USER_PAGE_NUM * FLASH_PAGE_SIZE)
|
|
return 0;
|
|
|
|
write_addr = FLASH_USER_START_ADDR + offset;
|
|
|
|
/* 擦除涉及的页 */
|
|
uint32_t first_page = write_addr / FLASH_PAGE_SIZE;
|
|
uint32_t last_page = (write_addr + len - 1) / FLASH_PAGE_SIZE;
|
|
|
|
for (uint32_t p = first_page; p <= last_page; p++)
|
|
{
|
|
uint32_t page_addr = p * FLASH_PAGE_SIZE;
|
|
if (!flash_erase_page(page_addr))
|
|
return 0;
|
|
}
|
|
|
|
/* 写入数据 */
|
|
return flash_write_bytes(write_addr, buf, len);
|
|
}
|