#include "app_flash.h" #include "stm32f1xx_hal.h" #include /* ============================================================ * 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); }