stm32F407学习之11:flash驱动和finsh验证
STM32F407ZET6 是一款高性能的 32 位微控制器,其内部 Flash 存储器的特性如下:
Flash 存储器容量
- 容量:512KB(512K x 8),用于程序存储。
- 地址范围:Flash 存储器的地址范围为 0x08000000 至 0x08080000。
Flash 存储器特性 - 读写操作:支持代码存储和数据存储,可以通过指令总线(I-Code Bus)访问。
- 访问速度:Flash 存储器的最高访问速度为 24MHz,当以 168MHz 的系统时钟访问时,需要插入 8 个时钟周期的延迟。
- 编程特性:支持在线编程,方便程序的更新和调试。
Flash 存储器的使用 - 程序存储:Flash 存储器主要用于存储程序代码,是单片机的“硬盘”。
- 数据存储:除了存储程序代码,Flash 存储器也可以用于存储一些需要长期保存的数据。
- 读写保护:支持读写保护功能,可以防止程序被误修改或数据被误读取。
1.编写代码
bsp_flash.h
#ifndef __BSP_FLASH_H__
#define __BSP_FLASH_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f4xx_flash.h"
#include "stm32f4xx.h" // 使用标准库头文件
#ifdef STM32F40_41xxx
#define FLASH_ADDRESS_START 0x08000000 //开始地址
#define FLASH_ADDRESS_END 0x0807FFFF //结束地址
#define FLASH_PAGE_SIZE 2048 //每页长度
#define FLASH_Sector_6_ADDR 0x08060000 //
#endif
uint32_t Flash_Read(uint32_t addr, uint8_t* buffer, uint32_t size);
FLASH_Status Flash_Write(uint32_t addr, uint8_t* buffer, uint32_t size);
FLASH_Status Flash_Erase(uint32_t start_addr, uint32_t length);
FLASH_Status Flash_Erase_Sectors(uint32_t start_sector, uint32_t sector_count);
#ifdef __cplusplus
}
#endif
#endif
bsp_flash.c
#include <rtthread.h>
#include "bsp_flash.h"
#include "stm32f4xx_flash.h"
#ifdef STM32F40_41xxx
// 获取 Flash 扇区号
uint32_t GetSector(uint32_t addr) {
if (addr < 0x08004000) return FLASH_Sector_0;
else if (addr < 0x08008000) return FLASH_Sector_1;
else if (addr < 0x0800C000) return FLASH_Sector_2;
else if (addr < 0x08010000) return FLASH_Sector_3;
else if (addr < 0x08020000) return FLASH_Sector_4;
else if (addr < 0x08040000) return FLASH_Sector_5;
else if (addr < 0x08060000) return FLASH_Sector_6;
else if (addr < 0x08080000) return FLASH_Sector_7;
else if (addr < 0x080A0000) return FLASH_Sector_8;
else if (addr < 0x080C0000) return FLASH_Sector_9;
else if (addr < 0x080E0000) return FLASH_Sector_10;
else return FLASH_Sector_11;
}
// 获取扇区的起始地址
uint32_t GetSectorStartAddress(uint32_t sector) {
switch (sector) {
case FLASH_Sector_0: return 0x08000000;
case FLASH_Sector_1: return 0x08004000;
case FLASH_Sector_2: return 0x08008000;
case FLASH_Sector_3: return 0x0800C000;
case FLASH_Sector_4: return 0x08010000;
case FLASH_Sector_5: return 0x08020000;
case FLASH_Sector_6: return 0x08040000;
case FLASH_Sector_7: return 0x08060000;
case FLASH_Sector_8: return 0x08080000;
case FLASH_Sector_9: return 0x080A0000;
case FLASH_Sector_10: return 0x080C0000;
case FLASH_Sector_11: return 0x080E0000;
default: return (uint32_t)-1; // 错误的扇区号
}
}
#endif
// Flash 读接口
uint32_t Flash_Read(uint32_t addr, uint8_t* buffer, uint32_t size) {
// 直接通过指针访问 Flash 内存区域
for (uint32_t i = 0; i < size; i++) {
buffer[i] = *(uint8_t*)(addr + i);
}
return size;
}
// Flash 写接口
FLASH_Status Flash_Write(uint32_t addr, uint8_t* buffer, uint32_t size) {
uint32_t sector_start = GetSector(addr);
uint32_t sector_end = GetSector(addr + size - 1);
uint32_t i, word;
FLASH_Status status;
FLASH_Unlock(); // 解锁 Flash
// 擦除涉及的扇区
for (uint32_t sector = sector_start; sector <= sector_end; sector++) {
status = FLASH_EraseSector(sector, VoltageRange_3);
if (status != FLASH_COMPLETE) {
//rt_kprintf("Erase sector %u failed with status %u\n", sector, status);
FLASH_Lock();
return status;
}
}
// 写入数据
for ( i = 0; i < size; i += 4) {
word = buffer[i] | buffer[i + 1] << 8 | buffer[i + 2] << 16 | buffer[i + 3] << 24;
status = FLASH_ProgramWord(addr + i, word);
if (status != FLASH_COMPLETE) {
//rt_kprintf("Write word at address 0x%08X failed with status %u\n", addr + i, status);
FLASH_Lock();
return status;
}
}
FLASH_Lock(); // 锁定 Flash
return FLASH_COMPLETE;
}
//Flash 擦除接口(擦除地址对应的扇区,实测跨扇区异常)
//start_addr最好为扇区的起始地址,建议使用Flash_Erase_Sectors来代替此接口
FLASH_Status Flash_Erase(uint32_t start_addr, uint32_t length) {
// 确保地址和长度在 Flash 范围内
if (start_addr < FLASH_ADDRESS_START || start_addr + length > FLASH_ADDRESS_END) {
return FLASH_ERROR_OPERATION;
}
uint32_t sector_start = GetSector(start_addr);
uint32_t sector_end = GetSector(start_addr + length - 1);
#if CHECK_SECTOR_ADDR
//加强参数检查
uint32_t sector_start_addr = GetSectorStartAddress(sector_start);
uint32_t sector_end_addr = GetSectorStartAddress(sector_end);
// 检查 start_addr 是否是扇区的起始地址
if ((start_addr == (uint32_t)-1) || (sector_end_addr == (uint32_t)-1)) {
// start_addr 不是扇区的起始地址,直接返回错误
return FLASH_ERROR_OPERATION;
}
#endif
FLASH_Status status;
FLASH_Unlock(); // 解锁 Flash
// 擦除涉及的扇区
for (uint32_t sector = sector_start; sector <= sector_end; sector++) {
status = FLASH_EraseSector(sector, VoltageRange_3);
if (status != FLASH_COMPLETE) {
FLASH_Lock();
return status;
}
}
FLASH_Lock(); // 锁定 Flash
return FLASH_COMPLETE;
}
// Flash 擦除接口(按扇区号和扇区个数擦除)
FLASH_Status Flash_Erase_Sectors(uint32_t start_sector, uint32_t sector_count) {
// 检查起始扇区号是否有效
if (start_sector > FLASH_Sector_11) {
return FLASH_ERROR_OPERATION; // 超出扇区范围
}
// 检查扇区个数是否有效
if (start_sector + sector_count - 1 > FLASH_Sector_11) {
return FLASH_ERROR_OPERATION; // 超出扇区范围
}
FLASH_Status status;
FLASH_Unlock(); // 解锁 Flash
// 擦除指定的扇区范围
for (uint32_t sector = start_sector; sector < start_sector + sector_count; sector++) {
status = FLASH_EraseSector(sector, VoltageRange_3);
if (status != FLASH_COMPLETE) {
FLASH_Lock();
return status; // 如果某个扇区擦除失败,返回错误状态
}
}
FLASH_Lock(); // 锁定 Flash
return FLASH_COMPLETE; // 所有扇区擦除成功
}
修改test_cmd.c
新加flash的测试接口
#define FLASH_ADDR_TEST 0x08060000 //扇区6
#define FLASH_SIZE_TEST 32 //FLASH_PAGE_SIZE*1 //空间大小
static uint8_t test_buff[FLASH_SIZE_TEST];
void test_flash(int argc, char **argv)
{
if (argc < 2){
RLOG_WARN("Usage: test_flash <read|write|erase>\n");
return;
}
if (strcmp(argv[1], "read") == 0)
{
memset(test_buff, 0x00, FLASH_SIZE_TEST);
Flash_Read(FLASH_ADDR_TEST, test_buff, FLASH_SIZE_TEST);
RLOG_HEX("test_flash read\r\n", test_buff, 32);
RLOG_INFO("flash read \n");
}
else if (strcmp(argv[1], "write") == 0)
{
memset(test_buff, 0xAA, FLASH_SIZE_TEST);
Flash_Write(FLASH_ADDR_TEST, test_buff, FLASH_SIZE_TEST);
RLOG_HEX("test_flash write\r\n", test_buff, 32);
RLOG_INFO("flash write\n");
}
else if (strcmp(argv[1], "erase") == 0)
{
Flash_Erase(FLASH_ADDR_TEST, FLASH_SIZE_TEST);
RLOG_INFO("flash erase\n");
}else
{
RLOG_INFO("Invalid argument.\n");
}
}
MSH_CMD_EXPORT(test_flash, test_flash read|write|erase eg);
2. 测试效果
msh >test_flash read
[INFO] test_flash read
DataAddr: 200001B0, DataLen: 32, HexData:
FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF
[INFO] flash read
msh >
msh >test_flash write
[INFO] test_flash write
DataAddr: 200001B0, DataLen: 32, HexData:
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
[INFO] flash write
msh >
msh >test_flash read
[INFO] test_flash read
DataAddr: 200001B0, DataLen: 32, HexData:
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
[INFO] flash read
msh >
特别注意:因为写FLASH和擦除扇区都比较耗时,在操作之前先需要关闭看门狗(执行test_watchdog stop关闭)。

浙公网安备 33010602011771号