开发环境:
MDK:Keil 5.30
开发板:GD32F207I-EVAL
MCU:GD32F207IK
存储器映射是指把芯片中或芯片外的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、外部设备、专用外设总线-内部、专用外设总线-外部、特定厂商等,因此使用该内核的设计者必须按照这个进行各自芯片的存储器结构设计。
首先,我们对比一下Cortex-M3存储器结构和GD32存储器结构:
图中可以很清晰的看到,GD32的存储器结构和Cortex-M3的很相似,不同的是,GD32加入了很多实际的东西,如:Flash、SRAM等。只有加入了这些东西,才能成为一个拥有实际意义的、可以工作的处理芯片-GD32。
GD32的存储器地址空间被划分为大小相等的8块区域,每块区域大小为512MB。
对GD32存储器知识的掌握,实际上就是对Flash和SRAM这两个区域知识的掌握。
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的存储器进行总结。
图中淡蓝色就是你需要知道的。
今后,你的编写代码、程序运行、寄存器设置、ICP、IAP都依靠这些东西。
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 function
param[in] none
param[out] none
retval none
*/
int main(void)
{
uint32_t InData = 12345678;
uint32_t OutData;
// systick init
sysTick_init();
//usart init 115200 8-N-1
com_init(COM1, 115200, 0, 1);
// led1 init
led_init(LED1);
printf("InData = %d\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n",InData);
// flash test
OutData= flash_test(FLASH_ADR, InData);
printf("OutData = %d\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n",OutData);
if(OutData == InData)
{
printf("Flash test success !\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
}
else
{
printf(