第26章 FMC—擴展外部SDRAM—零死角玩轉STM32-F429系列


第26章     FMC—擴展外部SDRAM

全套200集視頻教程和1000PDF教程請到秉火論壇下載:www.firebbs.cn

野火視頻教程優酷觀看網址:http://i.youku.com/firege

 

 

本章參考資料:《STM32F4xx 中文參考手冊2》、《STM32F4xx規格書》、庫幫助文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》。

關於SDRAM存儲器,請參考前面的"常用存儲器介紹"章節,實驗中SDRAM芯片的具體參數,請參考其規格書《IS42-45S16400J》來了解。

26.1 SDRAM控制原理

STM32控制器芯片內部有一定大小的SRAMFLASH作為內存和程序存儲空間,但當程序較大,內存和程序空間不足時,就需要在STM32芯片的外部擴展存儲器了。

STM32F429系列芯片擴展內存時可以選擇SRAMSDRAM,由於SDRAM的"容量/價格"比較高,即使用SDRAM要比SRAM要划算得多。我們以SDRAM為例講解如何為STM32擴展內存。

STM32芯片擴展內存與給PC擴展內存的原理是一樣的,只是PC上一般以內存條的形式擴展,內存條實質是由多個內存顆粒(SDRAM芯片)組成的通用標准模塊,而STM32直接與SDRAM芯片連接。見圖 262,這是一種型號為IS42-45S16400JSDRAM芯片內部結構框圖,以它為模型進行學習。

261 SDRAM芯片外觀

262 一種SDRAM芯片的內部結構框圖

26.1.1 SDRAM信號線

262虛線框外引出的是SDRAM芯片的控制引腳,其說明見表 261

261 SDRAM控制引腳說明

信號線

類型

說明

CLK

I

同步時鍾信號,所有輸入信號都在CLK為上升沿的時候被采集

CKE

I

時鍾使能信號,禁止時鍾信號時SDRAM會啟動自刷新操作

CS#

I

片選信號,低電平有效

CAS#

I

列地址選通,為低電平時地址線表示的是列地址

RAS#

I

行地址選通,為低電平時地址線表示的是行地址

WE#

I

寫入使能,低電平有效

DQM[0:1]

I

數據輸入/輸出掩碼信號,表示DQ信號線的有效部分

BA[0:1]

I

Bank地址輸入,選擇要控制的Bank

A[0:11]

I

地址輸入

DQ[0:15]

I/O

數據輸入輸出信號

除了時鍾、地址和數據線,控制SDRAM還需要很多信號配合,它們具體作用在描述時序圖時進行講解。

26.1.2 控制邏輯

SDRAM內部的"控制邏輯"指揮着整個系統的運行,外部可通過CSWECASRAS以及地址線來向控制邏輯輸入命令,命令經過"命令器譯碼器"譯碼,並將控制參數保存到"模式寄存器中",控制邏輯依此運行。

26.1.3 地址控制

SDRAM包含有"A"以及"BA"兩類地址線,A類地址線是行(Row)與列(Column)共用的地址總線,BA地址線是獨立的用於指定SDRAM內部存儲陣列號(Bank)。在命令模式下,A類地址線還用於某些命令輸入參數。

26.1.4 SDRAM的存儲陣列

要了解SDRAM的儲存單元尋址以及"A"、"BA"線的具體運用,需要先熟悉它內部存儲陣列的結構,見圖 263

263 SDRAM存儲陣列模型

SDRAM內部包含的存儲陣列,可以把它理解成一張表格,數據就填在這張表格上。和表格查找一樣,指定一個行地址和列地址,就可以精確地找到目標單元格,這是SDRAM芯片尋址的基本原理。這樣的每個單元格被稱為存儲單元,而這樣的表則被稱為存儲陣列(Bank),目前設計的SDRAM芯片基本上內部都包含有4個這樣的Bank,尋址時指定Bank號以及行地址,然后再指定列地址即可尋找到目標存儲單元。SDRAM內部具有多個Bank時的結構見圖 264

264 SDRAM內有多個Bank時的結構圖

SDRAM芯片向外部提供有獨立的BA類地址線用於Bank尋址,而行與列則共用A類地址線。

262標號„中表示的就是它內部的存儲陣列結構,通訊時當RAS線為低電平,則"行地址選通器"被選通,地址線A[11:0]表示的地址會被輸入到"行地址譯碼及鎖存器"中,作為存儲陣列中選定的行地址,同時地址線BA[1:0]表示的Bank也被鎖存,選中了要操作的Bank號;接着控制CAS線為低電平,"列地址選通器"被選通,地址線A[11:0]表示的地址會被鎖存到"列地址譯碼器"中作為列地址,完成尋址過程。

26.1.5 數據輸入輸出

若是寫SDRAM內容,尋址完成后,DQ[15:0]線表示的數據經過圖 262標號…中的輸入數據寄存器,然后傳輸到存儲器陣列中,數據被保存;數據輸出過程相反。

本型號的SDRAM存儲陣列的"數據寬度"是16(即數據線的數量),在與SDRAM進行數據通訊時,16位的數據是同步傳輸的,但實際應用中我們可能會以8位、16位的寬度存取數據,也就是說16位的數據線並不是所有時候都同時使用的,而且在傳輸低寬度數據的時候,我們不希望其它數據線表示的數據被錄入。如傳輸8位數據的時候,我們只需要DQ[7:0]表示的數據,而DQ[15:8]數據線表示的數據必須忽略,否則會修改非目標存儲空間的內容。所以數據輸入輸出時,還會使用DQM[1:0]線來配合,每根DQM線對應8位數據,如"DQM0(LDQM)"為低電平,"DQM1(HDQM)"為高電平時,數據線DQ[7:0]表示的數據有效,而DQ[15:8]表示的數據無效。

26.1.6 SDRAM的命令

控制SDRAM需要用到一系列的命令,見表 262。各種信號線狀態組合產生不同的控制命令。

262 SDRAM命令表

命令名

CS#

RAS#

CAS#

WE#

DQM

ADDR

DQ

COMMAND INHIBIT

H

X

X

X

X

X

X

NO OPERATION

L

H

H

H

X

X

X

ACTIVE

L

L

H

H

X

Bank/row

X

READ

L

H

L

H

L/H

Bank/col

X

WRITE

L

H

L

L

L/H

Bank/col

Valid

PRECHARGE

L

L

H

L

X

Code

X

AUTO REFRESH or SELF REFRESH

L

L

L

H

X

X

X

LOAD MODE REGISTER

L

L

L

L

X

Op-code

X

BURST TERMINATE

L

H

H

L

X

X

active

表中的H表示高電平,L表示低電平,X表示任意電平,High-Z表示高阻態。

1.    命令禁止

只要CS引腳為高電平,即表示"命令禁止"(COMMAND INHBIT),它用於禁止SDRAM執行新的命令,但它不能停止當前正在執行的命令。

2.    空操作

"空操作"(NO OPERATION),"命令禁止"的反操作,用於選中SDRAM,以便接下來發送命令。

3.    行有效

進行存儲單元尋址時,需要先選中要訪問的Bank和行,使它處於激活狀態。該操作通過"行有效"(ACTIVE)命令實現,見圖 265,發送行有效命令時,RAS線為低電平,同時通過BA線以及A線發送Bank地址和行地址。

265 行有效命令時序圖

4.    列讀寫

行地址通過"行有效"命令確定后,就要對列地址進行尋址了。"讀命令"(READ)和"寫命令"(WRITE)的時序很相似,見圖 266,通過共用的地址線A發送列地址,同時使用WE引腳表示讀/寫方向,WE為低電平時表示寫,高電平時表示讀。數據讀寫時,使用DQM線表示有效的DQ數據線。

266 讀取命令時序

本型號的SDRAM芯片表示列地址時僅使用A[7:0]線,而A10線用於控制是否"自動預充電",該線為高電平時使能,低電平時關閉。

5.    預充電

 SDRAM 的尋址具有獨占性,所以在進行完讀寫操作后,如果要對同一個Bank 的另一行進行尋址,就要將原來有效(ACTIVE)的行關閉,重新發送行/列地址。Bank 關閉當前工作行,准備打開新行的操作就是預充電(Precharge)。

預充電可以通過獨立的命令控制,也可以在每次發送讀寫命令的同時使用"A10"線控制自動進行預充電。實際上,預充電是一種對工作行中所有存儲陣列進行數據重寫,並對行地址進行復位,以准備新行的工作。

獨立的預充電命令時序見圖 267。該命令配合使用A10線控制,若A10為高電平時,所有Bank都預充電;A10為低電平時,使用BA線選擇要預充電的Bank

267 PRECHARGE命令時序

6.    刷新

SDRAM要不斷進行刷新(Refresh)才能保留住數據,因此它是 DRAM 最重要的操作。刷新操作與預充電中重寫的操作本質是一樣的。

但因為預充電是對一個或所有Bank 中的工作行操作,並且不定期,而刷新則是有固定的周期,依次對所有行進行操作,以保證那些久久沒被訪問的存儲單元數據正確。

刷新操作分為兩種:"自動刷新"(Auto Refresh)與"自我刷新"(Self Refresh),發送命令后CKE時鍾為有效時(低電平),使用自動刷新操作,否則使用自我刷新操作。不論是何種刷新方式,都不需要外部提供行地址信息,因為這是一個內部的自動操作。

對於"自動刷新", SDRAM 內部有一個行地址生成器(也稱刷新計數器)用來自動地依次生成行地址,每收到一次命令刷新一行。在刷新過程中,所有Bank都停止工作,而每次刷新所占用的時間為N個時鍾周期(SDRAM型號而定,通常為N=9),刷新結束之后才可進入正常的工作狀態,也就是說在這N個時鍾期間內,所有工作指令只能等待而無法執行。一次次地按行刷新,刷新完所有行后,將再次對第一行重新進行刷新操作,這個對同一行刷新操作的時間間隔,稱為SDRAM的刷新周期,通常為64ms。顯然刷新會對SDRAM的性能造成影響,但這是它的DRAM的特性決定的,也是DRAM相對於SRAM取得成本優勢的同時所付出的代價。

"自我刷新"則主要用於休眠模式低功耗狀態下的數據保存,也就是說即使外部控制器不工作了,SDRAM都能自己確保數據正常。在發出"自我刷新"命令后,將 CKE 置於無效狀態(低電平),就進入自我刷新模式,此時不再依靠外部時鍾工作,而是根據SDRAM內部的時鍾進行刷新操作。在自我刷新期間除了 CKE 之外的所有外部信號都是無效的,只有重新使 CKE 有效才能退出自我刷新模式並進入正常操作狀態。

7.    加載模式寄存器

前面提到SDRAM的控制邏輯是根據它的模式寄存器來管理整個系統的,而這個寄存器的參數就是通過"加載模式寄存器"命令(LOAD MODE REGISTER)來配置的。發送該命令時,使用地址線表示要存入模式寄存器的參數"OP-Code",各個地址線表示的參數見圖 268

268 模式寄存器解析圖

模式寄存器的各個參數介紹如下:

Burst Length

Burst Length譯為突發長度,下面簡稱BL。突發是指在同一行中相鄰的存儲單元連續進行數據傳輸的方式,連續傳輸所涉及到存儲單元(列)的數量就是突發長度。

上文講到的讀/寫操作,都是一次對一個存儲單元進行尋址,如果要連續讀/寫就還要對當前存儲單元的下一個單元進行尋址,也就是要不斷的發送列地址與讀/寫命令(行地址不變,所以不用再對行尋址)。雖然由於讀/寫延遲相同可以讓數據的傳輸在 I/O 端是連續的,但它占用了大量的內存控制資源,在數據進行連續傳輸時無法輸入新的命令,效率很低。

為此,人們開發了突發傳輸技術,只要指定起始列地址與突發長度,內存就會依次地自動對后面相應數量的存儲單元進行讀/寫操作而不再需要控制器連續地提供列地址。這樣,除了第一筆數據的傳輸需要若干個周期外,其后每個數據只需一個周期的即可獲得。其實我們在EERPOMFLASH讀寫章節講解的按頁寫入就是突發寫入,而它們的讀取過程都是突發性質的。

非突發連續讀取模式:不采用突發傳輸而是依次單獨尋址,此時可等效於 BL=1。雖然也可以讓數據連續地傳輸,但每次都要發送列地址與命令信息,控制資源占用極大。突發連續讀取模式:只要指定起始列地址與突發長度,尋址與數據的讀取自動進行,而只要控制好兩段突發讀取命令的間隔周期( BL 相同)即可做到連續的突發傳輸。BL 的數值,也是不能隨便設或在數據進行傳輸前臨時決定。在初始化SDRAM調用LOAD MODE REGISTER命令時就被固定。BL可用的選項是 1248,常見的設定是 4 8。若傳輸時實際需要數據長度小於設定的BL值,則調用"突發停止"(BURST TERMINATE)命令結束傳輸。

BT

模式寄存器中的BT位用於設置突發模式,突發模式分為順序(Sequential)與間隔(Interleaved)兩種。在順序方式中,操作按地址的順序連續執行,如果是間隔模式,則操作地址是跳躍的。跳躍訪問的方式比較亂,不太符合思維習慣,我們一般用順序模式。順序訪問模式時按照"0-1-2-3-4-5-6-7"的地址序列訪問。

CASLatency

模式寄存器中的CASLatency是指列地址選通延遲,簡稱CL。在發出讀命令(命令同時包含列地址)后,需要等待幾個時鍾周期數據線DQ才會輸出有效數據,這之間的時鍾周期就是指CLCL一般可以設置為23個時鍾周期,見圖 269

269 CL=2CL=3的說明圖

CL只是針對讀命令時的數據延時,在寫命令是不需要這個延時的,發出寫命令時可同時發送要寫入的數據。

Op Mode

OP ModeOperating ModeSDRAM的工作模式。當它被配置為"00"的時候表示工作在正常模式,其它值是測試模式或被保留的設定。實際使用時必須配置成正常模式。

WB

WB用於配置寫操作的突發特性,可選擇使用BL設置的突發長度或非突發模式。

Reserved

模式寄存器的最后三位的被保留,沒有設置參數。

26.1.7 SDRAM的初始化流程

最后我們來了解SDRAM的初始化流程。SDRAM並不是上電后立即就可以開始讀寫數據的,它需要按步驟進行初始化,對存儲矩陣進行預充電、刷新並設置模式寄存器,見圖 2610

2610 SDRAM初始化流程

該流程說明如下:

(1)    給SDRAM上電,並提供穩定的時鍾,至少100us;

(2)    發送"空操作"(NOP)命令;

(3)    發送"預充電"(PRECHARGE)命令,控制所有Bank進行預充電,並等待tRP時間, tRP表示預充電與其它命令之間的延遲;

(4)    發送至少2個"自動刷新"(AUTO REFRESH)命令,每個命令后需等待tRFC時間,tRFC表示自動刷新時間;

(5)    發送"加載模式寄存器"(LOAD MODE REGISTER)命令,配置SDRAM的工作參數,並等待tMRD時間,tMRD表示加載模式寄存器命令與行有行或刷新命令之間的延遲;

(6)    初始化流程完畢,可以開始讀寫數據。

其中tRPtRFCtMRD等時間參數跟具體的SDRAM有關,可查閱其數據手冊獲知,STM32 FMC訪問時配置需要這些參數。

26.1.8 SDRAM的讀寫流程

初始化步驟完成,開始讀寫數據,其時序流程見圖 2611及圖 2612

2611 CL=2時,帶AUTO PRECHARGE的讀時序

2612 AUTO PRECHARGE 命令的寫時序

讀時序和寫時序的命令過程很類似,下面我們統一解說:

(1)    發送"行有效"(ACTIVE)命令,發送命令的同時包含行地址和Bank地址,然后等待tRCD時間,tRCD表示行有效命令與讀/寫命令之間的延遲;

(2)    發送"讀/寫"(READ/WRITE)命令,在發送命令的同時發送列地址,完成尋址的地址輸入。對於讀命令,根據模式寄存器的CL定義,延遲CL個時鍾周期后,SDRAM的數據線DQ才輸出有效數據,而寫命令是沒有CL延遲的,主機在發送寫命令的同時就可以把要寫入的數據用DQ輸入到SDRAM中,這是讀命令與寫命令的時序最主要的區別。圖中的讀/寫命令都通過地址線A10控制自動預充電,而SDRAM接收到帶預充電要求的讀/寫命令后,並不會立即預充電,而是等待tWR時間才開始,tWR表示寫命令與預充電之間的延遲;

(3)    執行"預充電"(auto precharge)命令后,需要等待tRP時間,tRP表示預充電與其它命令之間的延遲;

(4)    圖中的標號„處的tRAS,表示自刷新周期,即在前一個"行有效"與"預充電"命令之間的時間;

(5)    發送第二次"行有效"(ACTIVE)命令准備讀寫下一個數據,在圖中的標號…處的tRC,表示兩個行有效命令或兩個刷新命令之間的延遲。

其中tRCDtWRtRPtRAS以及tRC等時間參數跟具體的SDRAM有關,可查閱其數據手冊獲知,STM32 FMC訪問時配置需要這些參數。

26.2 FMC簡介

STM32F429使用FMC外設來管理擴展的存儲器,FMCFlexible Memory Controller的縮寫,譯為可變存儲控制器。它可以用於驅動包括SRAMSDRAMNOR FLASH以及NAND FLSAH類型的存儲器。在其它系列的STM32控制器中,只有FSMC控制器(Flexible Static Memory Controller),譯為可變靜態存儲控制器,所以它們不能驅動SDRAM這樣的動態存儲器,因為驅動SDRAM時需要定時刷新,STM32F429FMC外設才支持該功能,且只支持普通的SDRAM,不支持DDR類型的SDRAM。我們只講述FMCSDRAM控制功能。

26.3

FMC框圖剖析

STM32FMC外設內部結構見圖 2613

2613 FMC控制器框圖

1.    通訊引腳

在框圖的右側是FMC外設相關的控制引腳,由於控制不同類型存儲器的時候會有一些不同的引腳,看起來有非常多,其中地址線FMC_A和數據線FMC_D是所有控制器都共用的。這些FMC引腳具體對應的GPIO端口及引腳號可在《STM32F4xx規格書》中搜索查找到,不在此列出。針對SDRAM控制器,我們是整理出以下的FMCSDRAM引腳對照表 263

263 FMC中的SDRAM控制信號線

FMC引腳名稱

對應SDRAM引腳名

說明

FMC_NBL[3:0]

DQM[3:0]

數據掩碼信號

FMC_A[12:0]

A[12:0]

/列地址線

FMC_A[15:14]

BA[1:0]

Bank地址線

FMC_D[31:0]

DQ[31:0]

數據線

FMC_SDCLK

CLK

同步時鍾信號

FMC_SDNWE

WE#

寫入使能

FMC_SDCKE[1:0]

CKE

SDCKE0SDRAM 存儲區域 1 時鍾使能

SDCKE1SDRAM 存儲區域 2 時鍾使能

FMC_SDNE[1:0]

--

SDNE0SDRAM 存儲區域 1 芯片使能

SDNE1SDRAM 存儲區域 2 芯片使能

FMC_NRAS

RAS#

行地址選通信號

FMC_NCAS

CAS#

列地址選通信號

其中比較特殊的是FMC_A[15:14]引腳用作Bank的尋址線;而FMC_SDCKE線和FMC_SDNE都各有2條,FMC_SDCKE用於控制SDRAM的時鍾使能,FMC_SDNE用於控制SDRAM芯片的片選使能。它們用於控制STM32使用不同的存儲區域驅動SDRAM,使用編號為0的信號線組會使用STM32的存儲器區域1,使用編號為1的信號線組會使用存儲器區域2。使用不同存儲區域時,STM32訪問SDRAM的地址不一樣,具體將在"FMC的地址映射"小節講解。

2.    存儲器控制器

上面不同類型的引腳是連接到FMC內部對應的存儲控制器中的。NOR/PSRAM/SRAM設備使用相同的控制器,NAND/PC卡設備使用相同的控制器,而SDRAM存儲器使用獨立的控制器。不同的控制器有專用的寄存器用於配置其工作模式。

控制SDRAM的有FMC_SDCR1/FMC_SDCR2控制寄存器、FMC_SDTR1/FMC_SDTR2時序寄存器、FMC_SDCMR命令模式寄存器以及FMC_SDRTR刷新定時器寄存器。其中控制寄存器及時序寄存器各有2個,分別對應於SDRAM存儲區域1和存儲區域2的配置。

FMC_SDCR控制寄存器可配置SDCLK的同步時鍾頻率、突發讀使能、寫保護、CAS延遲、行列地址位數以及數據總線寬度等。

FMC_SDTR時序寄存器用於配置SDRAM訪問時的各種時間延遲,如TRP行預充電延遲、TMRD加載模式寄存器激活延遲等。

FMC_SDCMR命令模式寄存器用於存儲要發送到SDRAM模式寄存器的配置,以及要向SDRAM芯片發送的命令。

FMC_SDRTR用於配置SDRAM的自動刷新周期。

3.    時鍾控制邏輯

FMC外設掛載在AHB3總線上,時鍾信號來自於HCLK(默認180MHz),控制器的時鍾輸出就是由它分頻得到。如SDRAM控制器的FMC_SDCLK引腳輸出的時鍾,是用於與SDRAM芯片進行同步通訊,它的時鍾頻率可通過FMC_SDCR1寄存器的SDCLK位配置,可以配置為HCLK1/21/3,也就是說,與SDRAM通訊的同步時鍾最高頻率為90MHz

26.4 FMC的地址映射

FMC連接好外部的存儲器並初始化后,就可以直接通過訪問地址來讀寫數據,這種地址訪問與I2C EEPROMSPI FLASH的不一樣,后兩種方式都需要控制I2CSPI總線給存儲器發送地址,然后獲取數據;在程序里,這個地址和數據都需要分開使用不同的變量存儲,並且訪問時還需要使用代碼控制發送讀寫命令。而使用FMC外接存儲器時,其存儲單元是映射到STM32的內部尋址空間的;在程序里,定義一個指向這些地址的指針,然后就可以通過指針直接修改該存儲單元的內容,FMC外設會自動完成數據訪問過程,讀寫命令之類的操作不需要程序控制。FMC的地址映射見圖 2614

2614 FMC的地址映射

圖中左側的是Cortex-M4內核的存儲空間分配,右側是STM32 FMC外設的地址映射。可以看到FMCNOR/PSRAM/SRAM/NAND FLASH以及PC卡的地址都在External RAM地址空間內,而SDRAM的地址是分配到External device區域的。正是因為存在這樣的地址映射,使得訪問FMC控制的存儲器時,就跟訪問STM32的片上外設寄存器一樣(片上外設的地址映射即圖中左側的"Peripheral"區域)

1.    SDRAM的存儲區域

FMCSDRAM的存儲區域分成了Bank1Bank2兩塊,這里的BankSDRAM芯片內部的Bank是不一樣的概念,只是FMC的地址區域划分而已。每個Bank有不一樣的起始地址,且有獨立的FMC_SDCR控制寄存器和FMC_SDTR時序寄存器,還有獨立的FMC_SDCKE時鍾使能信號線和FMC_SDCLK信號線。FMC_SDCKE0FMC_SDCLK0對應的存儲區域1的地址范圍是0xC000 0000-0xCFFF FFFF,而FMC_SDCKE1FMC_SDCLK1對應的存儲區域2的地址范圍是0xD000 0000- 0xDFFF FFFF。當程序里控制內核訪問這些地址的存儲空間時,FMC外設會即會產生對應的時序,對它外接的SDRAM芯片進行讀寫。

2.    External RAM 與External device的區別

比較遺憾的是FMCSDRAM分配的區域不在External RAM區,這個區域可以直接執行代碼,而SDRAM所在的External device區卻不支持這個功能。這里說的可直接執行代碼的特性就是在"常用存儲器"章節介紹的XIP(eXecute In Place)特性,即存儲器上若存儲了代碼,CPU可直接訪問代碼執行,無需緩存到其它設備上再運行;而且XIP特性還對存儲器的種類有要求,SRAM/SDRAMNOR Flash都支持這種特性,而NAND FLASHPC卡是不支持XIP的。結合存儲器的特性和STM32 FMC存儲器種類的地址分配,就發現它的地址規划不合理了,NAND FLASHPC卡這些不支持XIP的存儲器卻占據了External RAM的空間,而支持XIPSDRAM存儲器的空間卻被分配到了Extern device區。為了解決這個問題,通過配置"SYSCFG_MEMRMP"寄存器的"SWP_FMC"寄存器位可用於交換SDRAMNAND/PC卡的地址映射,使得存儲在SDRAM中的代碼能被執行,只是由於SDRAM的最高同步時鍾是90MHz,代碼的執行速度會受影響。

本章主要講解當STM32的片內SRAM不夠用時使用SDRAM擴展內存,但假如程序太大,它的程序空間FLASH不夠用怎么辦呢?首先是裁剪代碼,目前STM32F429系列芯片內部FLASH空間最高可達2MB,實際應用中只要我們把代碼中的圖片、字模等占據大空間的內容放到外部存儲器中,純粹的代碼很難達到2MB。如果還不夠用,非要擴展程序空間的話,一種方法是使用FMC擴展NOR FLASH,把程序存儲到NOR上,程序代碼能夠直接在NOR FLASH上執行。另一種方法是把程序存儲在其它外部存儲器,如SD卡,需要時把存儲在SD卡上的代碼加載到SRAMSDRAM上,再在RAM上執行代碼。

如果SDRAM不是用於存儲可執行代碼,只是用來保存數據的話,在External RAMExteranl device區域都沒有區別,不需要與NAND的映射地址交換。

26.5 SDRAM時序結構體

控制FMC使用SDRAM存儲器時主要是配置時序寄存器以及控制寄存器,利用ST標准庫的SDRAM時序結構體以及初始化結構體可以很方便地寫入參數。

SDRAM時序結構體的成員見代碼清單 241

代碼清單 261 SDRAM時序結構體FMC_SDRAMTimingInitTypeDef

1 /* @brief 控制SDRAM的時序參數,這些參數的單位都是"周期"

2 * 各個參數的值可設置為1-16個周期。 */

3 typedef struct

4 {

5 uint32_t FMC_LoadToActiveDelay; /*TMRD:加載模式寄存器命令后的延遲*/

6 uint32_t FMC_ExitSelfRefreshDelay; /*TXSR:自刷新命令后的延遲 */

7 uint32_t FMC_SelfRefreshTime; /*TRAS:自刷新時間*/

8 uint32_t FMC_RowCycleDelay; /*TRC:行循環延遲*/

9 uint32_t FMC_WriteRecoveryTime; /*TWR:恢復延遲 */

10 uint32_t FMC_RPDelay; /*TRP:行預充電延遲*/

11 uint32_t FMC_RCDDelay; /*TRCD:行到列延遲*/

12 } FMC_SDRAMTimingInitTypeDef;

這個結構體成員定義的都是SDRAM發送各種命令后必須的延遲,它的配置對應到FMC_SDTR中的寄存器位。所有成員參數值的單位是周期,參數值大小都可設置成"1-16"。關於這些延時時間的定義可以看"SDRAM初始化流程"和"SDRAM讀寫流程"小節的時序圖了解。具體參數值根據SDRAM芯片的手冊說明來配置。各成員介紹如下:

(1)    FMC_LoadToActiveDelay

本成員設置TMRD延遲(Load Mode Register to Active),即發送加載模式寄存器命令后要等待的時間,過了這段時間才可以發送行有效或刷新命令。

(2)    FMC_ExitSelfRefreshDelay

本成員設置退出TXSR延遲(Exit Self-refresh delay),即退出自我刷新命令后要等待的時間,過了這段時間才可以發送行有效命令。

(3)     FMC_SelfRefreshTime

本成員設置自我刷新時間TRAS,即發送行有效命令后要等待的時間,過了這段時間才執行預充電命令。

(4)     FMC_RowCycleDelay

本成員設置TRC延遲(Row cycle delay),即兩個行有效命令之間的延遲,以及兩個相鄰刷新命令之間的延遲

(5)     FMC_WriteRecoveryTime

本成員設置TWR延遲(Recovery delay),即寫命令和預充電命令之間的延遲,等待這段時間后才開始執行預充電命令。

(6)     FMC_RPDelay

本成員設置TRP延遲(Row precharge delay),即預充電命令與其它命令之間的延遲。

(7)     FMC_RCDDelay

本成員設置TRCD延遲(Row to column delay),即行有效命令到列讀寫命令之間的延遲。

 

這個SDRAMTimingInitTypeDef時序結構體配置的延時參數,將作為下一節的FMC SDRAM初始化結構體的一個成員。

26.6 SDRAM初始化結構體

FMCSDRAM初始化結構體見代碼清單 262

代碼清單 262 SDRAM初始化結構體FMC_SDRAMInitTypeDef

1 /* @brief FMC SDRAM 初始化結構體類型定義 */

2 typedef struct

3 {

4 uint32_t FMC_Bank; /*選擇FMCSDRAM存儲區域*/

5 uint32_t FMC_ColumnBitsNumber; /*定義SDRAM的列地址寬度 */

6 uint32_t FMC_RowBitsNumber; /*定義SDRAM的行地址寬度 */

7 uint32_t FMC_SDMemoryDataWidth; /*定義SDRAM的數據寬度 */

8 uint32_t FMC_InternalBankNumber; /*定義SDRAM內部的Bank數目 */

9 uint32_t FMC_CASLatency; /*定義CASLatency的時鍾個數*/

10 uint32_t FMC_WriteProtection; /*定義是否使能寫保護模式 */

11 uint32_t FMC_SDClockPeriod; /*配置同步時鍾SDCLK的參數*/

12 uint32_t FMC_ReadBurst; /*是否使能突發讀模式*/

13 uint32_t FMC_ReadPipeDelay; /*定義在CAS個延遲后再等待多

14 少個HCLK時鍾才讀取數據 */

15 FMC_SDRAMTimingInitTypeDef* FMC_SDRAMTimingStruct; /*定義SDRAM的時序參數*/

16 } FMC_SDRAMInitTypeDef;

這個結構體,除最后一個成員是上一小節講解的時序配置外,其它結構體成員的配置都對應到FMC_SDCR中的寄存器位。各個成員意義在前面的小節已有具體講解,其可選參數介紹如下,括號中的是STM32標准庫定義的宏:

(1)    FMC_Bank

本成員用於選擇FMC映射的SDRAM存儲區域,可選擇存儲區域12 (FMC_Bank1/2_SDRAM)

(2)    FMC_ColumnBitsNumber

本成員用於設置要控制的SDRAM的列地址寬度,可選擇8-11(FMC_ColumnBits_Number_8/9/10/11b)

(3)     FMC_RowBitsNumber

本成員用於設置要控制的SDRAM的行地址寬度,可選擇設置成11-13(FMC_RowBits_Number_11/12/13b)

(4)    FMC_SDMemoryDataWidth

本成員用於設置要控制的SDRAM的數據寬度,可選擇設置成81632(FMC_SDMemory_Width_8/16/32b)

(5)     FMC_InternalBankNumber

本成員用於設置要控制的SDRAM的內部Bank數目,可選擇設置成24Bank數目(FMC_InternalBank_Number_2/4),請注意區分這個結構體成員與FMC_Bank的區別。

(6)     FMC_CASLatency

本成員用於設置CASLatencyCL的時鍾數目,可選擇設置為123個時鍾周期(FMC_CAS_Latency_1/2/3)

(7)     FMC_WriteProtection

本成員用於設置是否使能寫保護模式,如果使能了寫保護則不能向SDRAM寫入數據,正常使用都是禁止寫保護的。

(8)     FMC_SDClockPeriod

本成員用於設置FMC與外部SDRAM通訊時的同步時鍾參數,可以設置成STM32HCLK時鍾頻率的1/21/3或禁止輸出時鍾(FMC_SDClock_Period_2/3FMC_SDClock_Disable)

(9)     FMC_ReadBurst

本成員用於設置是否使能突發讀取模式,禁止時等效於BL=1,使能時BL的值等於模式寄存器中的配置。

(10)     FMC_ReadPipeDelay

本成員用於配置在CASLatency個時鍾周期后,再等待多少個HCLK時鍾周期才進行數據采樣,在確保正確的前提下,這個值設置為越短越好,可選擇設置的參數值為012HCLK時鍾周期(FMC_ReadPipe_Delay_0/1/2)

(11)     FMC_SDRAMTimingStruct

這個成員就是我們上一小節講解的SDRAM時序結構體了,設置完時序結構體后再把賦值到這里即可。

 

配置完SDRAM初始化結構體后,調用FMC_SDRAMInit函數把這些配置寫入到FMCSDRAM控制寄存器及時序寄存器,實現FMC的初始化。

26.7 SDRAM命令結構體

控制SDRAM時需要各種命令,通過向FMC的命令模式寄存器FMC_SDCMR寫入控制參數,即可控制FMC對外發送命令,為了方便使用,STM32標准庫也把它封裝成了結構體,見代碼清單 263

代碼清單 263 SDRAM命令結構體

1 typedef struct

2 {

3 uint32_t FMC_CommandMode; /*要發送的命令 */

4 uint32_t FMC_CommandTarget; /*目標存儲器區域 */

5 uint32_t FMC_AutoRefreshNumber; /*若發送的是自動刷新命令,

6 此處為發送的刷新次數,其它命令時無效 */

7 uint32_t FMC_ModeRegisterDefinition; /*若發送的是加載模式寄存器命令,

8 此處為要寫入SDRAM模式寄存器的參數 */

9 } FMC_SDRAMCommandTypeDef;

命令結構體中的各個成員介紹如下:

(1)    FMC_CommandMode

本成員用於配置將要發送的命令,它可以被賦值為表 264中的宏,這些宏代表了不同命令;

264 FMC可輸出的SDRAM控制命令

命令說明

FMC_Command_Mode_normal

正常模式命令

FMC_Command_Mode_CLK_Enabled

使能CLK命令

FMC_Command_Mode_PALL

對所有Bank預充電命令

FMC_Command_Mode_AutoRefresh

自動刷新命令

FMC_Command_Mode_LoadMode

加載模式寄存器命令

FMC_Command_Mode_Selfrefresh

自我刷新命令

FMC_Command_Mode_PowerDown

掉電命令

(2)    FMC_CommandTarget

本成員用於選擇要控制的FMC存儲區域,可選擇存儲區域12(FMC_Command_Target_bank1/2);

(3)     FMC_AutoRefreshNumber

有時需要連續發送多個"自動刷新"(Auto Refresh)命令時,配置本成員即可控制它發送多少次,可輸入參數值為1-16,若發送的是其它命令,本參數值無效。如FMC_CommandMode成員被配置為宏FMC_Command_Mode_AutoRefresh,而FMC_AutoRefreshNumber被設置為2時,FMC就會控制發送2次自動刷新命令。

(4)    FMC_ModeRegisterDefinition

當向SDRAM發送加載模式寄存器命令時,這個結構體成員的值將通過地址線發送到SDRAM的模式寄存器中,這個成員值長度為13位,各個位一一對應SDRAM的模式寄存器。

配置完這些結構體成員,調用庫函數FMC_SDRAMCmdConfig即可把這些參數寫入到FMC_SDCMR寄存器中,然后FMC外設就會發送相應的命令了。

26.8 FMC—擴展外部SDRAM實驗

本小節以型號為"IS42S16400J"的SDRAM芯片為STM32擴展內存。它的行地址寬度為12位,列地址寬度為8位,內部含有4Bank,數據線寬度為16位,容量大小為8MB

學習本小節內容時,請打開配套的"FMC—讀寫SDRAM"工程配合閱讀。本實驗僅講解基本的SDRAM驅動,不涉及內存管理的內容,在本書的《MDK編譯過程及文件類型全解》章節將會講解使用更簡單的方法從SDRAM中分配變量,以及使用C語言標准庫的malloc函數來分配SDRAM的空間。

26.8.1 硬件設計

2615 SDRAM硬件連接圖

SDRAMSTM32相連的引腳非常多,主要是地址線和數據線,這些具有特定FMC功能的GPIO引腳可查詢《STM32F4xx規格書》中的說明來了解。

關於該SDRAM芯片的更多信息,請參考其規格書《IS42-45S16400J》了解。若您使用的實驗板FLASH的型號或控制引腳不一樣,可在我們工程的基礎上修改,程序的控制原理相同。

26.8.2 軟件設計

為了使工程更加有條理,我們把SDRAM初始化相關的代碼獨立分開存儲,方便以后移植。在"工程模板"之上新建"bsp_sdram.c"及"bsp_sdram.h"文件,這些文件也可根據您的喜好命名,它們不屬於STM32標准庫的內容,是由我們自己根據應用需要編寫的。

1.    編程要點

(13)    初始化通訊使用的目標引腳及端口時鍾;

(14)    使能FMC外設的時鍾;

(15)    配置FMC SDRAM的時序、工作模式;

(16)    根據SDRAM的初始化流程編寫初始化函數;

(17)    建立機制訪問外部SDRAM存儲器;

(18)    編寫測試程序,對讀寫數據進行校驗。

2.    代碼分析
FMC硬件相關宏定義

我們把FMC SDRAM硬件相關的配置都以宏的形式定義到"bsp_sdram.h"文件中,見代碼清單 242

代碼清單 264 SDRAM硬件配置相關的宏(省略了大部分數據線)

1 /*A行列地址信號線*/

2 #define FMC_A0_GPIO_PORT GPIOF

3 #define FMC_A0_GPIO_CLK RCC_AHB1Periph_GPIOF

4 #define FMC_A0_GPIO_PIN GPIO_Pin_0

5 #define FMC_A0_PINSOURCE GPIO_PinSource0

6 #define FMC_A0_AF GPIO_AF_FMC

7 /*......*/

8 /*此處省略A1-A11信號線的宏,具體可參考工程中的代碼*/

9 /*BA地址線*/

10 #define FMC_BA0_GPIO_PORT GPIOG

11 #define FMC_BA0_GPIO_CLK RCC_AHB1Periph_GPIOG

12 #define FMC_BA0_GPIO_PIN GPIO_Pin_4

13 #define FMC_BA0_PINSOURCE GPIO_PinSource4

14 #define FMC_BA0_AF GPIO_AF_FMC

15 /*......*/

16 /*此處省略BA1信號線的宏,具體可參考工程中的代碼*/

17

18 /*DQ數據信號線*/

19 #define FMC_D0_GPIO_PORT GPIOD

20 #define FMC_D0_GPIO_CLK RCC_AHB1Periph_GPIOD

21 #define FMC_D0_GPIO_PIN GPIO_Pin_14

22 #define FMC_D0_PINSOURCE GPIO_PinSource14

23 #define FMC_D0_AF GPIO_AF_FMC

24 /*......*/

25 /*此處省略D1-A15信號線的宏,具體可參考工程中的代碼*/

26

27 /*控制信號線*/

28 /*CS片選*/

29 #define FMC_CS_GPIO_PORT GPIOH

30 #define FMC_CS_GPIO_CLK RCC_AHB1Periph_GPIOH

31 #define FMC_CS_GPIO_PIN GPIO_Pin_6

32 #define FMC_CS_PINSOURCE GPIO_PinSource6

33 #define FMC_CS_AF GPIO_AF_FMC

34 /*WE寫使能*/

35 #define FMC_WE_GPIO_PORT GPIOC

36 #define FMC_WE_GPIO_CLK RCC_AHB1Periph_GPIOC

37 #define FMC_WE_GPIO_PIN GPIO_Pin_0

38 #define FMC_WE_PINSOURCE GPIO_PinSource0

39 #define FMC_WE_AF GPIO_AF_FMC

40 /*RAS行選通*/

41 #define FMC_RAS_GPIO_PORT GPIOF

42 #define FMC_RAS_GPIO_CLK RCC_AHB1Periph_GPIOF

43 #define FMC_RAS_GPIO_PIN GPIO_Pin_11

44 #define FMC_RAS_PINSOURCE GPIO_PinSource11

45 #define FMC_RAS_AF GPIO_AF_FMC

46 /*CAS列選通*/

47 #define FMC_CAS_GPIO_PORT GPIOG

48 #define FMC_CAS_GPIO_CLK RCC_AHB1Periph_GPIOG

49 #define FMC_CAS_GPIO_PIN GPIO_Pin_15

50 #define FMC_CAS_PINSOURCE GPIO_PinSource15

51 #define FMC_CAS_AF GPIO_AF_FMC

52 /*CLK同步時鍾,存儲區域2*/

53 #define FMC_CLK_GPIO_PORT GPIOG

54 #define FMC_CLK_GPIO_CLK RCC_AHB1Periph_GPIOG

55 #define FMC_CLK_GPIO_PIN GPIO_Pin_8

56 #define FMC_CLK_PINSOURCE GPIO_PinSource8

57 #define FMC_CLK_AF GPIO_AF_FMC

58 /*CKE時鍾使能,存儲區域2*/

59 #define FMC_CKE_GPIO_PORT GPIOH

60 #define FMC_CKE_GPIO_CLK RCC_AHB1Periph_GPIOH

61 #define FMC_CKE_GPIO_PIN GPIO_Pin_7

62 #define FMC_CKE_PINSOURCE GPIO_PinSource7

63 #define FMC_CKE_AF GPIO_AF_FMC

64

65 /*DQM1數據掩碼*/

66 #define FMC_UDQM_GPIO_PORT GPIOE

67 #define FMC_UDQM_GPIO_CLK RCC_AHB1Periph_GPIOE

68 #define FMC_UDQM_GPIO_PIN GPIO_Pin_1

69 #define FMC_UDQM_PINSOURCE GPIO_PinSource1

70 #define FMC_UDQM_AF GPIO_AF_FMC

71 /*DQM0數據掩碼*/

72 #define FMC_LDQM_GPIO_PORT GPIOE

73 #define FMC_LDQM_GPIO_CLK RCC_AHB1Periph_GPIOE

74 #define FMC_LDQM_GPIO_PIN GPIO_Pin_0

75 #define FMC_LDQM_PINSOURCE GPIO_PinSource0

76 #define FMC_LDQM_AF GPIO_AF_FMC

以上代碼根據硬件的連接,把與SDRAM通訊使用的引腳號、引腳源以及復用功能映射都以宏封裝起來。其中FMC_CKEFMC_CLK引腳對應的是FMC的存儲區域2,所以后面我們對SDRAM的尋址空間也是要指向存儲區域2的。

初始化FMC的 GPIO

利用上面的宏,編寫FMCGPIO引腳初始化函數,見代碼清單 243

代碼清單 265 FMCGPIO初始化函數(省略了大部分數據線)

 1 /** 

2 * @brief 初始化控制SDRAMIO

3 * @param

4 * @retval

5 */

6 static void SDRAM_GPIO_Config(void)

7 {

8 GPIO_InitTypeDef GPIO_InitStructure;

9

10 /*此處省略大量地址線、數據線以及控制信號線,

11 它們的時鍾配置都相同,具體請查看工程中的代碼*/

12 /* 使能SDRAM相關的GPIO時鍾 */

13 /*地址信號線*/

14 RCC_AHB1PeriphClockCmd(FMC_A0_GPIO_CLK | /*...*/

15 /*數據信號線*/ /*控制信號線*/

16 FMC_D0_GPIO_CLK |FMC_CS_GPIO_CLK | , ENABLE);

17

18 /*--所有GPIO的配置都相同,此處省略大量引腳初始化,具體請查看工程中的代碼*/

19 /* 通用 GPIO 配置 */

20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //配置為復用功能

21 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

22 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽輸出

23 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

24

25 /*A行列地址信號線針對引腳配置*/

26 GPIO_InitStructure.GPIO_Pin = FMC_A0_GPIO_PIN;

27 GPIO_Init(FMC_A0_GPIO_PORT, &GPIO_InitStructure);

28 GPIO_PinAFConfig(FMC_A0_GPIO_PORT, FMC_A0_PINSOURCE , FMC_A0_AF);

29 /*...*/

30 /*DQ數據信號線針對引腳配置*/

31 GPIO_InitStructure.GPIO_Pin = FMC_D0_GPIO_PIN;

32 GPIO_Init(FMC_D0_GPIO_PORT, &GPIO_InitStructure);

33 GPIO_PinAFConfig(FMC_D0_GPIO_PORT, FMC_D0_PINSOURCE , FMC_D0_AF);

34 /*...*/

35 /*控制信號線*/

36 GPIO_InitStructure.GPIO_Pin = FMC_CS_GPIO_PIN;

37 GPIO_Init(FMC_CS_GPIO_PORT, &GPIO_InitStructure);

38 GPIO_PinAFConfig(FMC_CS_GPIO_PORT, FMC_CS_PINSOURCE , FMC_CS_AF);

39 /*...*/

40 }

與所有使用到GPIO的外設一樣,都要先把使用到的GPIO引腳模式初始化,以上代碼把FMC SDRAM的所有信號線全都初始化為FMC復用功能,所有引腳配置都是一樣的。

配置FMC的模式

接下來需要配置FMC SDRAM的工作模式,這個函數的主體是根據硬件連接的SDRAM特性,對時序結構體以及初始化結構體進行賦值。見代碼清單 244

代碼清單 266 配置FMC的模式

1 /**

2 * @brief 初始化配置使用SDRAMFMCGPIO接口,

3 * 本函數在SDRAM讀寫操作前需要被調用

4 * @param None

5 * @retval None

6 */

7 void SDRAM_Init(void)

8 {

9 FMC_SDRAMInitTypeDef FMC_SDRAMInitStructure;

10 FMC_SDRAMTimingInitTypeDef FMC_SDRAMTimingInitStructure;

11

12 /* 配置FMC接口相關的 GPIO*/

13 SDRAM_GPIO_Config();

14

15 /* 使能 FMC 時鍾 */

16 RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FMC, ENABLE);

17

18 /* SDRAM時序結構體,根據SDRAM參數表配置----------------*/

19 /* SDCLK 90 Mhz (HCLK/2 :180Mhz/2) 1個時鍾周期Tsdclk =1/90MHz=11.11ns*/

20 /* TMRD: 2 Clock cycles */

21 FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2;

22 /* TXSR: min=70ns (7x11.11ns) */

23 FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7;

24 /* TRAS: min=42ns (4x11.11ns) max=120k (ns) */

25 FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;

26 /* TRC: min=70 (7x11.11ns) */

27 FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 7;

28 /* TWR: min=1+ 7ns (1+1x11.11ns) */

29 FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;

30 /* TRP: 15ns => 2x11.11ns */

31 FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2;

32 /* TRCD: 15ns => 2x11.11ns */

33 FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;

34

35 /* FMC SDRAM 控制配置 */

36 /*選擇存儲區域*/

37 FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank2_SDRAM;

38 /* 行地址線寬度: [7:0] */

39 FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b;

41 /* 列地址線寬度: [11:0] */

42 FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b;

43 /* 數據線寬度 */

44 FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;

45 /* SDRAM內部bank數量*/

46 FMC_SDRAMInitStructure.FMC_InternalBankNumber =FMC_InternalBank_Number_4;

48 /* CAS潛伏期 */

49 FMC_SDRAMInitStructure.FMC_CASLatency = FMC_CAS_Latency_2;

50 /* 禁止寫保護*/

51 FMC_SDRAMInitStructure.FMC_WriteProtection =

52 FMC_Write_Protection_Disable;

53 /* SDCLK時鍾分頻因子,SDCLK = HCLK/SDCLOCK_PERIOD*/

54 FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_2;

55 /* 突發讀模式設置*/

56 FMC_SDRAMInitStructure.FMC_ReadBurst = FMC_Read_Burst_Enable;

57 /* 讀延遲配置 */

58 FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_0;

59 /* SDRAM時序參數 */

60 FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct =&FMC_SDRAMTimingInitStructure;

62

63 /* 調用初始化函數,向寄存器寫入配置 */

64 FMC_SDRAMInit(&FMC_SDRAMInitStructure);

65

66 /* 執行FMC SDRAM的初始化流程*/

67 SDRAM_InitSequence();

68 }

這個函數的執行流程如下:

(1)    初始化GPIO引腳以及FMC時鍾

函數開頭調用了前面定義的SDRAM_GPIO_Config函數對FMC用到的GPIO進行初始化,並且使用庫函數RCC_AHB3PeriphClockCmd使能FMC外設的時鍾。

(2)    時序結構體賦值

接下來對時序結構體FMC_SDRAMTimingInitStructure賦值。在前面我們了解到時序結構體各個成員值的單位是同步時鍾SDCLK的周期數,而根據我們使用的SDRAM芯片,可查詢得它對這些時序要求,見表 265

265 SDRAM的延時參數(摘自《IS42-45S16400J》規格書)

時間參數

說明

最小值

單位

trc

兩個刷新命令或兩個行有效命令之間的延遲

63

ns

tras

行有效與預充電命令之間的延遲

42

ns

trp

預充電與行有效命令之間的延遲

15

ns

trcd

行有效與列讀寫命令之間的延遲

15

ns

twr

寫入命令到預充電命令之間的延遲

2

cycle

txsr

退出自我刷新到行有效命令之間的延遲

70

ns

tmrd

加載模式寄存器命令與行有效或刷新命令之間的延遲

2

cycle

部分時間參數以ns為單位,因此我們需要進行單位轉換,而以SDCLK時鍾周期數(cycle)為單位的時間參數,直接賦值到時序結構體成員里即可。

由於我們配置FMC輸出的SDCLK時鍾頻率為HCLK1/2(在后面的程序里配置的),即FSDCLK=90MHz,可得1SDCLK時鍾周期長度為TSDCLK=1/FSDCLK =11.11ns,然后設置各個成員的時候,只要保證時間大於以上SDRAM延時參數表的要求即可。如trc要求大於63ns,而11.11ns x 7=77.77ns,所以FMC_RowCycleDelay(TRC)成員值被設置為7個時鍾周期,依葫蘆畫瓢完成時序參數的設置。

(3)    配置FMC初始化結構體

函數接下來對FMC SDRAM的初始化結構體賦值。包括行列地址線寬度、數據線寬度、SDRAM內部Bank數量以及CL長度,這些都是根據外接的SDRAM的特性設置的,其中CL長度要與后面初始化流程中給SDRAM模式寄存器中的賦值一致。

    設置存儲區域

FMC_Bank成員設置FMCSDRAM存儲區域映射選擇為FMC_Bank2_SDRAM,這是由於我們的SDRAM硬件連接到FMC_CKE1FMC_CLK1,所以對應到存儲區域2

     行地址、列地址、數據線寬度及內部Bank數量

這些結構體成員都是根據SDRAM芯片的特性配置的,行地址寬度為8位,列地址寬度為12位,數據線寬度為16位,SDRAM內部有4Bank

     CL長度

CL的長度這里被設置為2個同步時鍾周期,它需要與后面SDRAM模式寄存器中的配置一樣;

     寫保護

FMC_WriteProtection用於設置寫保護,如果使能了這個功能是無法向SDRAM寫入數據的,所以我們關閉這個功能;

    同步時鍾參數

FMC_SDClockPeriod成員被設置為FMC_SDClock_Period_2 ,所以同步時鍾的頻率就被設置為HCLK1/2了;

     突發讀模式及讀延遲

為了加快讀取速度,我們使能突發讀功能,且讀延遲周期為0

    時序參數

最后向FMC_SDRAMTimingStruct賦值為前面的時序結構體,包含了我們設定的SDRAM時間參數。

    賦值完成后調用庫函數FMC_SDRAMInit把初始化結構體配置的各種參數寫入到FMC_SDCR控制寄存器及FMC_SDTR時序寄存器中。函數的最后調用SDRAM_InitSequence函數實現執行SDRAM的上電初始化時序。

實現SDRAM的初始化時序

在上面配置完成STM32FMC外設參數后,在讀寫SDRAM前還需要執行前面介紹的SDRAM上電初始化時序,它就是由SDRAM_InitSequence函數實現的,見代碼清單 458

代碼清單 267 SDRAM上電初始化時序

1 /**

2 * @brief SDRAM芯片進行初始化配置

3 * @param None.

4 * @retval None.

5 */

6 static void SDRAM_InitSequence(void)

7 {

8 FMC_SDRAMCommandTypeDef FMC_SDRAMCommandStructure;

9 uint32_t tmpr = 0;

10

11 /* Step 3 -----------------------------------------------*/

12 /* 配置命令:開啟提供給SDRAM的時鍾 */

13 FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled;

14 FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_COMMAND_TARGET_BANK;

15 FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;

16 FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;

17 /* 檢查SDRAM標志,等待至SDRAM空閑 */

18 while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);

19 /* 發送上述命令*/

20 FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);

21

22 /* Step 4 ---------------------------------------------*/

23 /*延時 */

24 SDRAM_delay(10);

25

26 /* Step 5 -------------------------------------------*/

27 /* 配置命令:對所有的bank預充電 */

28 FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL;

29 FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_COMMAND_TARGET_BANK;

30 FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;

31 FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;

32 /* 檢查SDRAM標志,等待至SDRAM空閑 */

33 while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);

34 /* 發送上述命令*/

35 FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);

36

37 /* Step 6 --------------------------------------------*/

38 /* 配置命令:自動刷新 */

39 FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh;

40 FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_COMMAND_TARGET_BANK;

41 FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 2;

42 FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;

43 /* 檢查SDRAM標志,等待至SDRAM空閑 */

44 while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);

45 /* 發送自動刷新命令*/

46 FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);

47

48 /* Step 7 ----------------------------------------------*/

49 /* 設置sdram寄存器配置 */

50 tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_8 |

51 SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |

52 SDRAM_MODEREG_CAS_LATENCY_2 |

53 SDRAM_MODEREG_OPERATING_MODE_STANDARD |

54 SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

55

56 /* 配置命令:設置SDRAM寄存器 */

57 FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode;

58 FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_COMMAND_TARGET_BANK;

59 FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;

60 FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr;

61 /* 檢查SDRAM標志,等待至SDRAM空閑 */

62 while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);

63

64 /* 發送上述命令*/

65 FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);

66

67 /* Step 8 --------------------------------------------*/

68

69 /* 設置刷新計數器 */

70 /*刷新速率 = (COUNT + 1) x SDRAM 頻率時鍾

71 COUNT = SDRAM 刷新周期/行數) - 20*/

72 /* 64ms/4096=15.62us (15.62 us x FSDCLK) - 20 =1386 */

73 FMC_SetRefreshCount(1386);

74 /* 發送上述命令*/

75 while (FMC_GetFlagStatus(FMC_BANK_SDRAM, FMC_FLAG_Busy) != RESET);

76 }

SDRAM的初始化流程實際上是發送一系列控制命令,利用命令結構體FMC_SDRAMCommandTypeDef及庫函數FMC_SDRAMCmdConfig配合即可發送各種命令。函數中按次序發送了使能CLK命令、預充電命令、2個自動刷新命令以及加載模式寄存器命令,每次發調用FMC_SDRAMCmdConfig發送命令后需要調用庫函數FMC_GetFlagStatus檢查BUSY標志位,等待上一個命令操作完畢。

其中發送加載模式寄存器命令時使用了一些自定義的宏,使用這些宏組合起來然后賦值到命令結構體的FMC_ModeRegisterDefinition成員中,這些宏定義見代碼清單 268

代碼清單 268 加載模式寄存器命令相關的宏

1 /**

2 * @brief FMC SDRAM 模式配置的寄存器相關定義

3 */

4 #define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)

5 #define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)

6 #define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)

7 #define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)

8 #define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)

9 #define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)

10 #define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)

11 #define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)

12 #define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)

13 #define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)

14 #define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)

這些宏是根據"SDRAM的模式寄存器"的位定義的,例如突發長度、突發模式、CL長度、SDRAM工作模式以及突發寫模式,其中的CL長度注意要與前面FMC SDRAN初始化結構體中定義的一致。

設置自動刷新周期

在上面SDRAM_InitSequence函數的最后,我們還調用了庫函數FMC_SetRefreshCount設置FMC自動刷新周期,這個函數會向刷新定時寄存器FMC_SDRTR寫入計數值,這個計數值每個SDCLK周期自動減1,減至0FMC會自動向SDRAM發出自動刷新命令,控制SDRAM刷新,SDRAM每次收到刷新命令后,刷新一行,對同一行進行刷新操作的時間間隔稱為SDRAM的刷新周期。

根據STM32F4xx 中文參考手冊的說明,COUNT值的計算公式如下:

刷新速率 = (COUNT + 1) x SDRAM 頻率時鍾

COUNT =SDRAM 刷新周期/行數) – 20

而查詢我們的SDRAM芯片規格書,可知它的SDRAM刷新周期為64ms,行數為4096,可算出它的SDRAM刷新要求:

TRefresh = 64ms/4096=15.62us

即每隔15.62us需要收到一次自動刷新命令。

所以:

COUNTA = TRefresh/TSDCLK=15.62x90=1406

但是根據要求,如果SDRAM在接受讀請求后出現內部刷新請求,則必須將刷新速率增加 20 SDRAM 時鍾周期以獲得重充足的裕量。

最后計算出:COUNT=COUNTA-20=1386

以上就是函數FMC_SetRefreshCount參數值的計算過程。

使用指針的方式訪問SDRAM存儲器

完成初始化SDRAM后,我們就可以利用它存儲數據了,由於SDRAM的存儲空間是被映射到內核的尋址區域的,我們可以通過映射的地址直接訪問SDRAM,訪問這些地址時,FMC外設自動讀寫SDRAM,程序上無需額外操作。

通過地址訪問內存,最直接的方式就是使用C語言的指針方式了,見代碼清單 269

代碼清單 269 使用指針的方式訪問SDRAM

1 /*SDRAM起始地址存儲空間2的起始地址*/

2 #define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)

3 /*SDRAM大小,8M字節*/

4 #define IS42S16400J_SIZE 0x800000

5

6 uint32_t temp;

7

8 /*SDRAM寫入8位數據*/

9 *( uint8_t*) (SDRAM_BANK_ADDR ) = (uint8_t)0xAA;

10 /*SDRAM讀取數據*/

11 temp = *( uint8_t*) (SDRAM_BANK_ADDR );

12

13 /*/ 16位數據*/

14 *( uint16_t*) (SDRAM_BANK_ADDR+10 ) = (uint16_t)0xBBBB;

15 temp = *( uint16_t*) (SDRAM_BANK_ADDR+10 );

16

17 /*/ 32位數據*/

18 *( uint32_t*) (SDRAM_BANK_ADDR+20 ) = (uint32_t)0xCCCCCCCC;

19 temp = *( uint32_t*) (SDRAM_BANK_ADDR+20 );

為方便使用,代碼中首先定義了宏SDRAM_BANK_ADDR表示SDRAM的起始地址,該地址即FMC映射的存儲區域2的首地址;宏IS42S16400J_SIZE表示SDRAM的大小,所以從地址(SDRAM_BANK_ADDR)(SDRAM_BANK_ADDR+IS42S16400J_SIZE)都表示在SDRAM的存儲空間,訪問這些地址,直接就能訪問SDRAM

配合這些宏,使用指針的強制轉換以及取指針操作即可讀寫SDRAM的數據,使用上跟普通的變量無異。

直接指定變量存儲到SDRAM空間

每次存取數據都使用指針來訪問太麻煩了,為了簡化操作,可以直接指定變量存儲到SDRAM空間,見代碼清單 2610

代碼清單 2610 直接指定變量地址的方式訪問SDRAM

1 /*SDRAM起始地址存儲空間2的起始地址*/

2 #define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)

3 /*絕對定位方式訪問SDRAM,這種方式必須定義成全局變量*/

4 uint8_t testValue __attribute__((at(SDRAM_BANK_ADDR)));

5 testValue = 0xDD;

這種方式使用關鍵字"__attribute__((at()))"來指定變量的地址,代碼中指定testValue存儲到SDRAM的起始地址,從而實現把變量存儲到SDRAM上。要注意使用這種方法定義變量時,必須在函數外把它定義成全局變量,才可以存儲到指定地址上。

更常見的是利用這種方法定義一個很大的數組,整個數組都指定到SDRAM地址上,然后就像使用malloc函數一樣,用戶自定義一些內存管理函數,動態地使用SDRAM的內存,我們在使用emWinGUI應用的時候就是這樣做的。

在本書的《MDK編譯過程及文件類型全解》章節將會講解使用更簡單的方法從SDRAM中分配變量,以及使用C語言標准庫的malloc函數來分配SDRAM的空間,更有效地進行內存管理。

3.    main函數

最后我們來編寫main函數,進行SDRAM芯片讀寫校驗,見代碼清單 2414

代碼清單 2611 main函數

1 /**

2 * @brief 主函數

3 * @param

4 * @retval

5 */

6 int main(void)

7 {

8 /* LED 端口初始化 */

9 LED_GPIO_Config();

10 /* 初始化串口 */

11 Debug_USART_Config();

12 printf("\r\n秉火STM32F429 SDRAM 讀寫測試例程\r\n");

13 /*初始化SDRAM模塊*/

14 SDRAM_Init();

15

16 /*藍燈亮,表示正在讀寫SDRAM測試*/

17 LED_BLUE;

18 /*SDRAM進行讀寫測試,檢測SDRAM是否正常*/

19 if (SDRAM_Test()==1)

20 {

21 //測試正常綠燈亮

22 LED_GREEN;

23 }

24 else

25 {

26 //測試失敗紅燈亮

27 LED_RED;

28 }

29 while (1);

30 }

函數中初始化了LED、串口,接着調用前面定義好的SDRAM_Init函數初始化FMCSDRAM,然后調用自定義的測試函數SDRAM_Test嘗試使用SDRAM存取81632位數據,並進行讀寫校驗,它就是使用指針的方式存取數據並校驗而已,此處不展開。

注意對SDRAM存儲空間的數據操作都要在SDRAM_Init初始化FMC之后,否則數據是無法正常存儲的。

下載驗證

用USB線連接開發板"USB TO UART"接口跟電腦,在電腦端打開串口調試助手,把編譯好的程序下載到開發板。在串口調試助手可看到SDRAM測試的調試信息。

26.9 每課一問

5.    如果要把SDRAM映射到FMC SDRAM的存儲區域1,需要如何修改STM32與SDRAM的硬件連接?程序上需要修改哪些內容?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM