当前位置: 首页 > news >正文

GD32F303----内部Flash读写

目标:

在0x8010000处写入0x0034567 一共4Byte;

实验:

程序烧录在0x8000000处;

使用的是GD32F303VCT6;用户手册如下:

GD32F303VCT6-MCU选择器-兆易创新 GigaDevice | 官方网站

代码: 

#include "gd32f30x.h"
#include "SEGGER_RTT.h"

#define erase_ADDRESS  0x08000000  // 目标地址

#define TARGET_ADDRESS  0x08010000  // 目标地址
/* Base address of the Flash sectors */
 #define PAGE_SIZE                         (0x800)    /* 2 Kbytes */
 #define FLASH_SIZE                        (0x40000)  /* 256 KBytes */
 
 
#define EXE_APP_FLAG				((uint32_t)0x08004000)
#define APP_START_ADDRESS			((uint32_t)0x08010000)	


fmc_state_enum CAN_BOOT_ErasePage(u32 StartPageAddr, u32 EndPageAddr);
fmc_state_enum CAN_BOOT_ProgramDatatoFlash(u32 *Address, u32 *Data, u32 DataNum) ;


int main(void) {
    uint32_t my_variable = 0x00345678;
    uint32_t target_address = TARGET_ADDRESS;
    fmc_state_enum status;

    // 1. 擦除目标地址所在的页
    // 计算目标地址所在页的起始地址(按PAGE_SIZE对齐)
    uint32_t page_start = target_address & ~(PAGE_SIZE - 1);//  0x08010000 & ~0x7FF = 0x08010000
	
	
    status = CAN_BOOT_ErasePage(page_start, page_start+PAGE_SIZE-1);
    if (status != FMC_READY) {
        // 擦除失败,处理错误
		SEGGER_RTT_printf(0, "erase status != FMC_READY");
        while(1);
    }

    // 2. 写入数据到目标地址
    status = CAN_BOOT_ProgramDatatoFlash(&target_address, &my_variable, sizeof(my_variable));
    if (status != FMC_READY) {
        // 写入失败,处理错误
		SEGGER_RTT_printf(0, "write status != FMC_READY");
        while(1);
    }

    // 3. 验证写入结果
    if (*(__IO uint32_t*)TARGET_ADDRESS == my_variable) {
        // 写入成功
		SEGGER_RTT_printf(0, "write success");
    } else {
        // 写入失败
		SEGGER_RTT_printf(0, "write fault");
		
    }

    while(1);
}


/**
  * @brief  擦出指定扇区区间的Flash数据 。
  * @param  StartPage 起始扇区
  * @param  EndPage 结束扇区
  * @retval 扇区擦出状态  
  */
fmc_state_enum CAN_BOOT_ErasePage(u32 StartPageAddr, u32 EndPageAddr)
{
	u32 i;
	fmc_state_enum FLASHStatus = FMC_READY;
	
	fmc_unlock();
	//Clear All pending flags
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
	fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
	fmc_flag_clear(FMC_FLAG_BANK0_END);
	
	for(i=StartPageAddr; i<=EndPageAddr; i+=PAGE_SIZE)
	{
		FLASHStatus = fmc_page_erase(i);
		if(FLASHStatus != FMC_READY) {
			fmc_lock();
			return	FLASHStatus;	
		}
	}
	
	fmc_lock();
	return FLASHStatus;
}

/**
  * @brief  将数据烧写到指定地址的Flash中 。
  * @param  Address Flash起始地址。
  * @param  Data 数据存储区起始地址。
  * @param  DataNum 数据字节数。
  * @retval 数据烧写状态。
  */
fmc_state_enum CAN_BOOT_ProgramDatatoFlash(u32 *Address, u32 *Data, u32 DataNum) 
{
	fmc_state_enum  FLASHStatus = FMC_READY;

	u32 *p_Data=Data;
	u32 i;

	if(*Address < EXE_APP_FLAG) {
		return FMC_PGERR;
	}
	
	fmc_unlock();
	//Clear All pending flags
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
	fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
	fmc_flag_clear(FMC_FLAG_BANK0_END);
	
	for(i=0;i<(DataNum>>2);i++)
	{
		FLASHStatus = fmc_word_program(*Address, *p_Data);
		if (FLASHStatus == FMC_READY) {
			*Address += 4;
			p_Data++;
		} else { 
			fmc_lock();
			return FLASHStatus;
		}
	}
	
	fmc_lock();
	return	FLASHStatus;
}

结果: 




当时遇到一个问题:

能看到 页Page的大小为2KB (0x800)

程序中特意指定了大小: 

 #define PAGE_SIZE                         (0x800)    /* 2 Kbytes */

擦除时候,还一个起始地址问题 

1. 比如说我要擦除

0x0801 0000页; 那么他擦的就是0x0801 0000 - 0x0801 07FF;大小为2KB;

2. 比如说我要擦除

0x0801 000A地址处的页; 那么他擦的依旧是0x0801 0000 - 0x0801 07FF;大小为2KB;

这就是涉及到一个字节对其的问题:

解决方案:位操作(如 address & ~0x7FF)验证对齐性,避免依赖十进制计算的近似结果。

以下是我搜索的结果:

数据对齐的含义与重要性:

在计算机系统中,“32位数据需要4字节对齐”意味着该数据的起始内存地址必须是4的倍数(即地址的低2位为0b00)。例如:

  • 对齐地址0x0800_00000x0800_00040x0800_0008(地址末位是0x00x40x80xC)。

  • 非对齐地址0x0800_00010x0800_0003(末位是0x10x3)。

因此说:0x0801 000A地址不是四字节对其,通过 address & ~0x7FF 验证对齐性

// 1. 擦除目标地址所在的页
    // 计算目标地址所在页的起始地址(按PAGE_SIZE对齐)
    uint32_t page_start = target_address & ~(PAGE_SIZE - 1);//  0x08010000 & ~0x7FF = 0x08010000

其实也可以看到 在GD32F303手册里面 flash的划分如下: 

为什么需要对齐?

  1. 硬件要求
    • 某些处理器(如ARM Cortex-M)严格要求对齐访问。如果尝试从非对齐地址读取32位数据,会触发硬件错误(如HardFault)。

    • 例如,在STM32中,访问未对齐的32位数据可能导致异常。

  2. 性能优化
    • 对齐数据允许CPU通过单次内存操作完成读写,而非对齐数据可能需要多次访问并拼接,显著降低效率。

    • 例如,从0x0800_0001读取32位数据时,CPU可能需要先读0x0800_0000的4字节,再读0x0800_0004的4字节,然后拼接出目标数据。

  3. 原子性操作
    • 对齐数据在多线程或中断场景中更容易实现原子操作(如uint32_t的原子读写)。

如何确保对齐?

  1. 编译器自动对齐
    • 编译器默认会对变量进行对齐。例如,声明uint32_t data;时,编译器会将其分配到4字节对齐的地址。

uint32_t data; // 编译器自动对齐到4字节边界
2.手动指定对齐
  • 使用编译器扩展语法强制对齐(如ARM的__attribute__((aligned(4)))):

uint32_t data __attribute__((aligned(4))); // 强制4字节对齐
3.结构体对齐控制
  • 结构体成员默认按自然对齐方式排列。可通过#pragma pack调整对齐规则:

#pragma pack(1)    // 按1字节对齐(取消对齐)
struct {
    uint8_t  a;    // 地址+0
    uint32_t b;    // 地址+1(非对齐!)
} s;
#pragma pack()     // 恢复默认对齐
4.动态内存对齐
  • 动态分配内存时,使用对齐版本的分配函数(如aligned_alloc(4, size)):

    uint32_t* ptr = (uint32_t*)aligned_alloc(4, 1024); // 分配4字节对齐的内存

 非对齐访问的后果

硬件异常
uint32_t* p = (uint32_t*)0x08000001; // 非对齐地址
uint32_t value = *p;                 // 在Cortex-M中触发HardFault!
性能损失 



以下是deepseek的知识; 

补充知识

页(Page)与扇区(Sector)的区别和联系

页(Page)与扇区(Sector)的区别和联系
页和扇区是存储介质(如 Flash、硬盘)中管理和操作数据的基本单位,但它们的定义和应用场景有所不同。以下是详细分析:

1. 定义与典型应用

概念定义典型场景常见大小
扇区(Sector)机械硬盘(HDD)或传统存储设备的最小读写单位,操作系统通过扇区访问磁盘数据。HDD、SD卡、U盘等块设备512 Bytes 或 4 KB(现代设备)
页(Page)Flash 存储器(如 NAND/NOR Flash)的最小读写单位,但擦除需以更大的块(Block)为单位。SSD、嵌入式 Flash(如 GD32F30x 的片上 Flash)通常 4 KB(SSD)、512 B–8 KB(嵌入式 Flash)

2. 核心区别

特性扇区(Sector)页(Page)
读写单位读写的最小单位(如每次读写 1 个扇区)。读写的最小单位(如每次写 1 页)。
擦除单位无需擦除(机械硬盘直接覆盖数据)。必须按块(Block)擦除,块由多个页组成(如 64 页/块)。
物理介质机械硬盘的磁道划分单元。Flash 存储器的存储单元结构。
寿命管理无擦写次数限制(磁介质)。有擦写次数限制(Flash 的 P/E 周期)。

3. 总结

维度扇区(Sector)页(Page)
本质传统存储设备的最小读写单位。Flash 存储器的最小读写单位。
擦除特性无需擦除(直接覆盖)。必须按块擦除,无法单页擦除。
设计目标兼容传统接口(如 BIOS)。适配 Flash 物理特性(寿命、速度)。
关联性文件系统通过扇区抽象底层存储。Flash 控制器通过页管理实际数据。

关键结论

  • 在机械硬盘或通用存储协议中,扇区是核心操作单位;

  • 在 Flash 设备中,是读写单位,块是擦除单位,而术语“扇区”可能用于描述逻辑或物理划分,需结合上下文理解。

我觉得标红的地方有问题 在手册里明确写了可以进行页擦除;而且在库函数里面

有声明:

 具体定义:




内存大小:

1. Total RO Size (Code + RO Data)

  • :10040 Bytes (约 9.80KB)

  • 含义

    • RO(Read-Only) 表示只读数据,包含 代码(Code) 和 只读数据(RO Data)(如常量、字符串等)。

    • 这部分数据存储在 Flash(ROM) 中,程序运行时直接从 Flash 中读取,不可修改。

  • 作用
    衡量程序实际占用的 Flash 空间大小。若超过芯片 Flash 容量,程序将无法烧录。

2. Total RW Size (RW Data + ZI Data)

  • :6032 Bytes (约 5.89KB)

  • 含义

    • RW(Read-Write) 表示可读写数据,包含:

      • RW Data:已初始化的全局变量和静态变量(如 int a = 10;)。

      • ZI Data:未初始化的全局变量和静态变量(如 int b;),在程序启动时会被清零。

    • 这部分数据占用 RAM 空间。

  • 作用
    衡量程序运行时对 RAM 的需求。若超过芯片 RAM 容量,程序可能无法正常运行或崩溃。

3. Total ROM Size (Code + RO Data + RW Data)

  • :10052 Bytes (约 9.82KB)

  • 含义

    • ROM Size 是 代码(Code)只读数据(RO Data) 和 已初始化的 RW Data 的总和。

    • RW Data 的初始值 需要存储在 Flash 中(例如 int a = 10; 的初始值 10),程序启动时从 Flash 复制到 RAM。

  • 作用
    反映程序烧录到 Flash 中的总数据量(代码 + 常量 + RW 初始值)。必须小于芯片的 Flash 容量。

内存分配关系



相关文章:

  • Pytorch深度学习框架60天进阶学习计划 - 第39天:联邦学习系统
  • [MySQL] 表的内连和外连
  • 解决【远程主机可能不符合 glibc 和 libstdc++ Vs code 服务器的先决条件】
  • 基于Axure的动态面板旋转实现抽奖转盘
  • CAD导入arcgis中保持面积不变的方法
  • K8S-证书更新时-误删除组件-
  • OpenHarmony人才认证证书
  • SpringMVC基础三(json)
  • 学习MySQL的第七天
  • 【软考系统架构设计师】信息系统基础知识点
  • python基础14 gRPC的流式(stream)传输(二)
  • 网络互连与互联网2
  • 镜像端口及观察端口的配置
  • WebPages 对象
  • 复习防火墙(二)
  • 【KWDB 创作者计划】_二进制安装部署 KWDB 踩过的坑和经验
  • 苍穹外卖|第二篇
  • Vue学习笔记 - 插件
  • js day5
  • JAVA实战开源项目:智能无人仓库管理系统 (Vue+SpringBoot) 附源码
  • 杭州行业网站建设/seo网站推广下载
  • 如何给网站做宣传/itmc平台seo优化关键词个数
  • 实训网站开发目的/百度一下就会知道了
  • 小程序代运营/荆门网站seo
  • 网站怎么加关键词做优化/西安seo培训学校
  • 做个网站需要多久网站设计费用多少/谷歌paypal下载