开发环境:
MDK:Keil 5.30
开发板:GD32F207I-EVAL
MCU:GD32F207IK

20.1 GD32存储结构的工作原理

20.1.1 Cortex-M内核的存储器映射

存储器映射是指把芯片中或芯片外的FLASH,RAM,外设,BOOTBLOCK等进行统一编址。即用地址来表示对象。这个地址绝大多数是由厂家规定好的,用户只能用而不能改。用户只能在挂外部RAM或FLASH的情况下可进行自定义。

如下图,是Cortex-M3存储器映射结构图。

Cortex-M3是32位的内核,因此其PC指针可以指向2^32=4G的地址空间,也就是0x0000_0000 - 0xFFFF_FFFF这一大块空间。根据图中描述,Cortex-M3内核将0x0000_0000 - 0xFFFF_FFFF这块4G大小的空间分成8大块:代码、SRAM、外设、外部RAM、外部设备、专用外设总线-内部、专用外设总线-外部、特定厂商等,因此使用该内核的设计者必须按照这个进行各自芯片的存储器结构设计。

20.1.2 GD32存储器结构

首先,我们对比一下Cortex-M3存储器结构和GD32存储器结构:

图中可以很清晰的看到,GD32的存储器结构和Cortex-M3的很相似,不同的是,GD32加入了很多实际的东西,如:Flash、SRAM等。只有加入了这些东西,才能成为一个拥有实际意义的、可以工作的处理芯片-GD32。

GD32的存储器地址空间被划分为大小相等的8块区域,每块区域大小为512MB。

对GD32存储器知识的掌握,实际上就是对Flash和SRAM这两个区域知识的掌握。

20.2 FLASH读写数据

20.2.1 GD32的Flash

GD32的Flash,严格说,应该是Flash模块。该Flash模块包括:Flash主存储区(Main memory)、Flash信息区(Information block),以及Flash存储接口寄存器区(Flash memory interface)。三个组成部分分别在0x0000 0000 - 0xFFFF FFFF不同的区域,GD32F2的Flash结构如下表所示。

【注】信息块存储了boot loader,不能被用户编程或擦除。

GD32的闪存模块由:主存储闪存块、信息块和选项字节块3部分组成

主存储器,该部分用来存放代码和数据常数(如加const类型的数据)。对于主存储闪存容量不多于512KB的GD32F20x_CL,闪存页大小为2KB。对于主存储闪存容量不少于768KB的GD32F20x_CL,使用了两片闪存;前512KB容量在第一片闪存(bank0)中,后续的容量在第二片闪存(bank1)中。其中bank0的闪存页大小为2KB, bank1的闪存页大小为4KB。主存储闪存的每页都可以单独擦除。

信息块,是用来存储GD自带的启动程序,用于串口下载,当B0接3.3V,B1接GND时,运行的就这部分代码,用户选择字节,则一般用于配置保护等功能。

选项字节块,该部分用于控制闪存储器读取等,是整个闪存储器的控制机构。

对于主存储器和信息块的写入有内嵌的闪存编程管理;编程与擦除的高压由内部产生。

在执行闪存写操作时,任何对闪存的读操作都会锁定总线,在写完成后才能正确进行,在进行读取或擦除操作时,不能进行代码或者数据的读取操作。

下面对GD32的存储器进行总结。

图中淡蓝色就是你需要知道的。

 Peripherals:外设的存储器映射,对该区域操作,就是对相应的外设进行操作;
 SRAM:运行时临时存放代码的地方;
 Flash:存放代码的地方;
 System Memory:GD32出厂时自带的你只能使用,不能写或擦除;
 Option Bytes:可以按照用户的需要进行配置(如配置看门狗为硬件实现还是软件实现);

今后,你的编写代码、程序运行、寄存器设置、ICP、IAP都依靠这些东西。

20.2.2 FLASH读写实现

GD32F20x 系列产品的片上 Flash 起始地址时 0x0800 0000,最大容量可达 3072 KB。读操作为 0 等待,可支持字节、半字(16 bits)和字(32 bits)访问。 Flash 编程以半字(16 bits)或字(32bits)为单位。擦除可以以页(page)为单位,也可以进行全片擦除(information blocks 除外)。

Flash的寄存器有很多,当时GD的工程师已经封装好了,直接用就可以,我这里就不在贴出来了。

Flash操作很简单,我们将数据写入到Flash中,再将其读出来。主要有以下步骤:

1.Flash解锁操作
2.清除Flash标志
3.页擦除
4.读写操作
5.锁定

核心代码如下:

#define FLASH_ADR 0x0807F800/*** @brief  flash test* @param  WriteAddr, InData* @retval OutData*/
uint32_t flash_test(uint32_t WriteAddr, uint32_t InData)
{uint32_t OutData = 0;//解锁fmc_unlock();//清除标志位fmc_flag_clear(FMC_FLAG_BANK0_PGERR | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_END | FMC_FLAG_BANK1_PGERR | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_END);//要擦出页的起始地址fmc_page_erase(WriteAddr);//写数据fmc_word_program(WriteAddr, InData);//锁定fmc_lock();OutData=(*(__IO uint32_t*)(WriteAddr));return OutData;
}

程序就不讲了,这里需要注意一个C语言的知识点。

OutData=(*(__IO uint32_t*)(WriteAddr));

这一句很多新手很懵逼,也就是从一个地址中读取数据,多看看指针相关的知识就好理解了。

主函数如下:

/*brief      main functionparam[in]  noneparam[out] noneretval     none
*/
int main(void)
{uint32_t InData = 12345678;uint32_t OutData;// systick initsysTick_init();// UART1 initcom_init(COM1);// led1 initled_init(LED1);printf("InData = %d\r\n",InData);// flash testOutData= flash_test(FLASH_ADR, InData);printf("OutData = %d\r\n",OutData);if(OutData == InData){printf("Flash test success !\r\n");}else{printf("Flash test fail !\r\n");}while(1){led_on(LED1);            // 亮delay_ms(1000);led_off(LED1);       // 灭 delay_ms(1000);      }
}

20.2.3实验结果

将程序边看一完成后下载到板子中,通过串口助手,按下板子的复位按键可以看到如下现象。

20.3 SRAM启动

20.3.1 GD32的启动模式

首先要回顾一下GD32的启动模式,因为启动模式决定了向量表的位置,GD32有三种启动模式:

1)主闪存存储器(Main Flash)启动:从GD32内置的Flash启动(0x0800 0000-0x0807 FFFF),一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。以0x08000000 对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x08000000 操作,且都是操作的同一块内存。

2)系统存储器(System Memory)启动:从系统存储器启动(0x1FFFF000 - 0x1FFF F7FF),这种模式启动的程序功能是由厂家设置的。一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的ISP程序中,提供了串口下载程序的固件,可以通过这个ISP程序将用户程序下载到系统的Flash中。以0x1FFFFFF0对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x1FFFFFF0操作,且都是操作的同一块内存。

3)片上SRAM启动:从内置SRAM启动(0x2000 0000-0x3FFFFFFF),既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。SRAM 只能通过0x20000000进行操作,与上述两者不同。从SRAM 启动时,需要在应用程序初始化代码中重新设置向量表的位置。

用户可以通过设置BOOT0和BOOT1的引脚电平状态,来选择复位后的启动模式。如下图所示:

启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。

值得注意的是GD32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。

20.3.2 GD32的SRAM

不同类型的Cortex-M单片机的SRAM大小是不一样的,但是他们的起始地址都是0x2000 0000,终止地址都是0x2000 0000+其固定的容量大小。

SRAM的理解比较简单,其作用是用来存取各种动态的输入输出数据、中间计算结果以及与外部存储器交换的数据和暂存数据。设备断电后,SRAM中存储的数据就会丢失。

GD32F20x 系列产品的片上 SRAM 起始地址是 0x2000 0000,最大容量可达 384KB,可支持字节、半字(16bits)和字(32bits)访问。 片上 SRAM 被分为 SRAM0、 SRAM1 和 SRAM2 等三个模块,且每个模块都有一个与 AHB 总线矩阵连接的专用接口,这意味着它们可以被同时访问。

模块 容量 地址范围
SRAM1 112KB 0x2000 0000 ~ 0x2001 BFFF
SRAM2 16KB 0x2001 C000 ~ 0x2001 FFFF
SRAM3 256KB 0x2002 0000 ~ 0x2005 FFFF

20.3.3片上SRAM启动实现

在使用片上SRAM调试之前,需要了解为何要使用片上SARM来启动程序,因此GD32的片上Flash的擦写次数有限,若超过最大擦除次数则会损坏内部Flash,因此在平时的程序调试阶段,最好使用SRAM启动。

总的来说,SRAM启动程序有如下用途:

1.调试阶段,需要频繁更新程序,可以SRAM启动,加快调试,减少flash擦写损耗
2.程序SWD/JTAG接口已经配置为普通端口,程序启动后无法程序更新,可在SRAM中启动后,再更新flash程序
3.程序已经开启了读保护,可在SRAM启动后,进行读保护关闭

片上SRAM启动实现的方法很简单,这里以GDF207+Keil5举例,实现方法如下:

1.修改内存分配

修改了内存分配,也就是修改分散加载文件xxx.sct文件。

修改后再次生成工程,分散文件也会修改。

下面谈谈SRAM参数的分配,首先确定MCU的RAM大小,笔者使用的MCU是GD32F207,因此SRAM大小是256KB,然后还需要根据程序的编译大小来分配SRAM空间。

如果想方便点可以直接看MAP文件。

FLASH和RAM的大小分别如下:
Flash = Code + RO Data + RW Data = 3.88KB(3968)
RAM = RW-data + ZI-data=8.07KB(8264)

因此笔者这里分配空间如下:

IROM1地址为0x2000 0000, 大小是0x10000=65536=64kB
IRAM1地址为0x2000 3000, 大小是0x20000=131072=128kB

当然啦,SRAM空间分配需要根据自己的 MCU来决定。

2.修改debug配置
新建RAM.ini文件,配置如下:

/*----------------------------------------------------------------------------* Name:    RAM.ini* Purpose: RAM Debug Initialization File* Note(s):*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------Setup()  configure PC & SP for RAM Debug*----------------------------------------------------------------------------*/
FUNC void Setup (void) {SP = _RDWORD(0x20000000);          // Setup Stack PointerPC = _RDWORD(0x20000004);          // Setup Program Counter_WDWORD(0xE000ED08, 0x20000000);   // Setup Vector Table Offset Register
}FUNC void OnResetExec (void)  {      // executes upon software RESETSetup();                           // Setup for Running
}load %L incrementalSetup();                             // Setup for Runningg, main

然后加载进来。

RAM.ini文件主要是初始化SP和PC指针。Cortex-M3复位时会从0x00000000和0x00000004的相对位置取出MSP初值以及将复位向量地址赋给PC,在SRAM中的绝对位置就是0x20000000和0x20000004。GD32F207需要手动配置。

3.修改下载配置
需要把程序下载到SRAM,修改相应的下载地址。

4.修改中断向量表指针
由启动文件可知,程序启动首先执行Reset_Handler函数,SRAM启动硬件强制将PC指针赋值为0x200001E0,PC指针加1开始执行启动文件,故Reset_Handler函数的运行地址需为0x200001E1才行,否则程序就会跑飞。

因此需要在中断向量表的末尾添加SPACE 0x96。

同时在在main函数中添加设置中断向量表以及系统初化函数。

nvic_vector_table_set(NVIC_VECTTAB_RAM,0x00);
SystemInit();

5.修改启动模式
这里选择SRAM启动,因此BOOT0->1 BOOT1->1。

最后编译下载程序,然后就可以运行程序了。


欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎


资源获取方式

1.关注公众号[嵌入式实验楼]
2.在公众号回复关键词[GD32开发实战指南]获取资料提取码

《嵌入式 – GD32开发实战指南》第20章 GD32的存储结构相关推荐

  1. 《嵌入式 – GD32开发实战指南》第3章 GPIO流水灯的前世今生

    开发环境: MDK:Keil 5.30 MCU:GD32F207IK 上一章通过控制GPIO的高低电平实现了流水灯,但只是告诉了大家怎么做,如何实现流水灯,本文将深入剖析的GPIO流水灯的前生今世,深 ...

  2. 《嵌入式 – GD32开发实战指南》第12章 ADC

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 12.1 ADC工作原理 GD32F2系列有 3 个逐次逼近型的ADC,精度为 12 位,有 ...

  3. 《嵌入式 – GD32开发实战指南》第18章 CRC

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 18.1 CRC的校验原理 循环冗余校验(CRC)计算单元是根据固定的生成多项式得到任一32 ...

  4. 《嵌入式 – GD32开发实战指南》第17章 看门狗

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK GD32 有两个看门狗,一个是独立看门狗,另外一个是窗口看门狗,独立看门狗号称宠物狗,窗口看 ...

  5. 《嵌入式 – GD32开发实战指南》第19章 程序加密

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 19.1程序加密工作原理 GD32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭.96位 ...

  6. 《嵌入式 – GD32开发实战指南》第14章 内部温度传感器

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 14.1内部温度传感器工作原理 GD32 有一个内部的温度传感器,可以用来测量 CPU 及周 ...

  7. 《嵌入式 – GD32开发实战指南》第2章 初识GPIO流水灯

    开发环境: MDK:Keil 5.30 MCU:GD32F207IK 2.1 GPIO工作原理 熟悉单片机的朋友都知道,学习的第一个例程就是流水灯,要想实现流水灯,首先必须了解GPIO的工作原理.GP ...

  8. 《嵌入式 – GD32开发实战指南》第6章 按键

    开发环境: MDK:Keil 5.30 MCU:GD32F207IK 6.1普通方式 6.1.1普通方式工作原理 按键 GPIO 端口有两个方案可以选择,一是采用上拉输入模式,因为按键在没按下的时候, ...

  9. 《嵌入式 – GD32开发实战指南》第21章 I2C

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 21.1 I2C工作原理 21.1.1 I2C串行总线概述 I2C总线是PHLIPS公司推出 ...

最新文章

  1. 为何我的BLOG不能DIY?
  2. 数独高阶技巧入门之四:简单异数链
  3. 查询GC得到森林里主域和子域的帐号
  4. 庄导就业指导2020.4.6
  5. 3Animation动画的创建,CCSpeed,CCFollow
  6. DataTable的Merge\COPY\AcceptChange使用说明
  7. 隐藏界面没有必要应用场景
  8. 《云计算:概念、技术与架构》一1.1 本书目标
  9. Mac os更新系统后安装scrapy报错error: command ‘xcrun‘ failed with exit status 1
  10. java9 多版本兼容jar_Java 9 多版本兼容 jar 包
  11. 从大哥大到iPhone13,谈谈移动网络发展
  12. centos 7 yum php swoole_自动化部署技能—搭建企业级YUM仓库
  13. VSCODE 改变Python版本到3.10
  14. 对称密钥和非对称密钥
  15. js定时器和延时调用的使用
  16. 修复硬盘数据的时候用!
  17. js设置元素垂直居中
  18. mfp 服务器控制中心,云平台管理中心
  19. 使用腾讯企业邮箱为什么强烈建议启用安全登录?怎么启用?
  20. java jsf_eclipse搭建JSF简单示例的教程(亲测)

热门文章

  1. 案例 | 客户关系管理数字化升级,侨鑫集团如何做到“心中有数”?
  2. AI开发基本流程介绍
  3. 12-24K/月,京东招聘研发项目经理(地点:北京)
  4. 使用VS2010来进行MFC4.2的开发
  5. java netty教程_Java NIO框架Netty教程(一) – Hello Netty
  6. 彩虹易支付新版更换用户中心
  7. 超实用的中国风配色 这些颜色你熟悉吗
  8. 区块链快速入门(三)——CFT(非拜占庭容错)共识算法
  9. 购物网站系统设计与实现
  10. 阿拉伯数字 和汉字的相互转换