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关闭)。

posted @ 2025-07-25 14:05  cupid8505  阅读(239)  评论(0)    收藏  举报