第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芯片外觀ui

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

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的硬件鏈接?程序上須要修改哪些內容?

相關文章
相關標籤/搜索