開發環境:
MDK:Keil 5.30
開發板:GD32F207I-EVAL
MCU:GD32F207IK
1 GD32存儲結構的工作原理
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、外部設備、專用外設總線-內部、專用外設總線-外部、特定廠商等,因此使用該內核的設計者必須按照這個進行各自芯片的存儲器結構設計。
1.2 GD32存儲器結構
首先,我們對比一下Cortex-M3存儲器結構和GD32存儲器結構:
圖中可以很清晰的看到,GD32的存儲器結構和Cortex-M3的很相似,不同的是,GD32加入了很多實際的東西,如:Flash、SRAM等。只有加入了這些東西,才能成為一個擁有實際意義的、可以工作的處理芯片-GD32。
GD32的存儲器地址空間被劃分為大小相等的8塊區域,每塊區域大小為512MB。
對GD32存儲器知識的掌握,實際上就是對Flash和SRAM這兩個區域知識的掌握。
2 FLASH讀寫數據
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都依靠這些東西。
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 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