test/freetest/Core/Src/app_flash.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);
}