第26章 FMC—擴展外部SDRAM


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

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

26.1  SDRAM控制原理

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

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

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

 

26-1 SDRAM芯片外觀

 

26-2 一種SDRAM芯片的內部結構框圖

26.1.1  SDRAM信號線

26-2虛線框外引出的是SDRAM芯片的控制引腳,其說明見 26-1。

26-1 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:12]

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”線的具體運用,需要先熟悉它內部存儲陣列的結構,見 26-3。

 

26-3 SDRAM存儲陣列模型

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

 

26-4 SDRAM內有多個Bank時的結構圖

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

262標號„中表示的就是它內部的存儲陣列結構,通訊時當RAS線為低電平,則“行地址選通器”被選通,地址線A[12: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]表示的數據無效。

1.1.6  SDRAM的命令

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

26-2 SDRAM命令表

 

 

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

1. 命令禁止

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

2. 空操作

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

3. 行有效

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

 

26-5 行有效命令時序圖

4. 列讀寫

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

 

26-6 讀取命令時序

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

5. 預充電

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

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

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

 

26-7 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”,各個地址線表示的參數見 26-8。

 

26-8 模式寄存器解析圖

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

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個時鍾周期,見 26-9。

 

26-9 CL=2CL=3的說明圖

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

Op Mode

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

WB

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

Reserved

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

26.1.7  SDRAM的初始化流程

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

 

26-10 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的讀寫流程

初始化步驟完成,開始讀寫數據,其時序流程見 26-11及 26-12。

 

26-11 CL=2時,帶AUTO PRECHARGE的讀時序

 

26-12 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簡介

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

26.3  FMC框圖剖析

STM32FMC外設內部結構見 26-13。

 

26-13 FMC控制器框圖

1. 通訊引腳

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

26-3 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(默認216MHz),控制器的時鍾輸出就是由它分頻得到。如SDRAM控制器的FMC_SDCLK引腳輸出的時鍾,是用於與SDRAM芯片進行同步通訊,它的時鍾頻率可通過FMC_SDCR1寄存器的SDCLK位配置,可以配置為HCLK1/21/3,也就是說,與SDRAM通訊的同步時鍾最高頻率為108MHz

26.4  FMC的地址映射

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

 

26-14 FMC的地址映射

圖中左側的是Cortex-M7內核的存儲空間分配,右側是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的最高同步時鍾是108MHz,代碼的執行速度會受影響。

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

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

26.5  SDRAM時序結構體

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

SDRAM時序結構體的成員見代碼清單 24-1

代碼清單 26-1 SDRAM時序結構體FMC_SDRAM_TimingTypeDef

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

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

 3 typedef struct

 4 {

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

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

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

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

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

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

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

12 } FMC_SDRAM_TimingTypeDef;

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

(1) LoadToActiveDelay

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

(2) ExitSelfRefreshDelay

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

(3)  SelfRefreshTime

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

(4)  RowCycleDelay

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

(5)  WriteRecoveryTime

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

(6)  RPDelay

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

(7)  FMC_RCDDelay

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

 

1.6  SDRAM初始化結構體

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

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

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

 2 typedef struct

 3 {

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

 5     uint32_t ColumnBitsNumber;     /*定義SDRAM的列地址 */

 6     uint32_t RowBitsNumber;         /*定義SDRAM的行地址 */

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

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

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

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

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

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

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

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

15 } FMC_SDRAM_InitTypeDef;

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

(1) Bank

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

(2) ColumnBitsNumber

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

(3)  RowBitsNumber

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

(4) MemoryDataWidth

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

(5)  InternalBankNumber

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

(6)  CASLatency

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

(7)  WriteProtection

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

(8)  ClockPeriod

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

(9)  ReadBurst

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

(10)  ReadPipeDelay

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

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

26.7  SDRAM命令結構體

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

代碼清單 263 SDRAM命令結構體

1 typedef struct

 2 {

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

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

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

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

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

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

9 } FMC_SDRAM_CommandTypeDef;

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

(1) CommandMode

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

26-4 FMC可輸出的SDRAM控制命令

命令說明

FMC_SDRAM_CMD_NORMAL_MODE

正常模式命令

FMC_SDRAM_CMD_CLK_ENABLE

使能CLK命令

FMC_SDRAM_CMD_PALL

對所有Bank預充電命令

FMC_SDRAM_CMD_AUTOREFRESH_MODE

自動刷新命令

FMC_SDRAM_CMD_LOAD_MODE

加載模式寄存器命令

FMC_SDRAM_CMD_SELFREFRESH_MODE

自我刷新命令

FMC_SDRAM_CMD_POWERDOWN_MODE

掉電命令

(2) CommandTarget

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

(3)  AutoRefreshNumber

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

(4) ModeRegisterDefinition

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

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

26.8  FMC—擴展外部SDRAM實驗

本小節以型號為“W9825G6KH”的SDRAM芯片為STM32擴展內存。它的行地址寬度為13位,列地址寬度為9位,內部含有4Bank,數據線寬度為16位,容量大小為32MB

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

26.8.1  硬件設計

 

26-15 SDRAM硬件連接圖

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

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

1.8.2  軟件設計

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

1. 編程要點

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

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

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

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

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

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

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

我們把FMC SDRAM硬件相關的配置都以宏的形式定義到 “bsp_sdram.h”文件中,見代碼清單 24-4。

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

1 /*地址信號線*/

 2 #define FMC_A0_GPIO_PORT         GPIOF

 3 #define FMC_A0_GPIO_CLK()        __GPIOF_CLK_ENABLE()

 4 #define FMC_A0_GPIO_PIN          GPIO_PIN_0

 5 /*省略一些引腳*/

 6 #define FMC_A12_GPIO_PORT        GPIOG

 7 #define FMC_A12_GPIO_CLK()       __GPIOG_CLK_ENABLE()

 8 #define FMC_A12_GPIO_PIN         GPIO_PIN_2

 9

10 /*數據信號線*/

11 #define FMC_D0_GPIO_PORT         GPIOD

12 #define FMC_D0_GPIO_CLK()        __GPIOD_CLK_ENABLE()

13 #define FMC_D0_GPIO_PIN          GPIO_PIN_14

14

15 /*省略一些引腳*/

16 #define FMC_D15_GPIO_PORT        GPIOD

17 #define FMC_D15_GPIO_CLK()       __GPIOD_CLK_ENABLE()

18 #define FMC_D15_GPIO_PIN         GPIO_PIN_10

19

20 /*控制信號線*/

21 #define FMC_CS_GPIO_PORT         GPIOH

22 #define FMC_CS_GPIO_CLK()        __GPIOH_CLK_ENABLE()

23 #define FMC_CS_GPIO_PIN          GPIO_PIN_6

24

25 #define FMC_BA0_GPIO_PORT        GPIOG

26 #define FMC_BA0_GPIO_CLK()       __GPIOG_CLK_ENABLE()

27 #define FMC_BA0_GPIO_PIN         GPIO_PIN_4

28

29 #define FMC_BA1_GPIO_PORT        GPIOG

30 #define FMC_BA1_GPIO_CLK()       __GPIOG_CLK_ENABLE()

31 #define FMC_BA1_GPIO_PIN         GPIO_PIN_5

32

33 #define FMC_WE_GPIO_PORT         GPIOC

34 #define FMC_WE_GPIO_CLK()        __GPIOC_CLK_ENABLE()

35 #define FMC_WE_GPIO_PIN          GPIO_PIN_0

36

37 #define FMC_RAS_GPIO_PORT        GPIOF

38 #define FMC_RAS_GPIO_CLK()       __GPIOF_CLK_ENABLE()

39 #define FMC_RAS_GPIO_PIN         GPIO_PIN_11

40

41 #define FMC_CAS_GPIO_PORT        GPIOG

42 #define FMC_CAS_GPIO_CLK()       __GPIOG_CLK_ENABLE()

43 #define FMC_CAS_GPIO_PIN         GPIO_PIN_15

44

45 #define FMC_CLK_GPIO_PORT        GPIOG

46 #define FMC_CLK_GPIO_CLK()       __GPIOG_CLK_ENABLE()

47 #define FMC_CLK_GPIO_PIN         GPIO_PIN_8

48

49 #define FMC_CKE_GPIO_PORT        GPIOH

50 #define FMC_CKE_GPIO_CLK()       __GPIOH_CLK_ENABLE()

51 #define FMC_CKE_GPIO_PIN         GPIO_PIN_7

52

53 /*UDQM LDQM*/

54 #define FMC_UDQM_GPIO_PORT        GPIOE

55 #define FMC_UDQM_GPIO_CLK()       __GPIOE_CLK_ENABLE()

56 #define FMC_UDQM_GPIO_PIN         GPIO_PIN_1

57

58 #define FMC_LDQM_GPIO_PORT        GPIOE

59 #define FMC_LDQM_GPIO_CLK()       __GPIOE_CLK_ENABLE()

60 #define FMC_LDQM_GPIO_PIN         GPIO_PIN_0 

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

初始化FMCGPIO 

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

代碼清單 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     FMC_A0_GPIO_CLK();FMC_A1_GPIO_CLK(); FMC_A2_GPIO_CLK();    

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

16     FMC_UDQM_GPIO_CLK();FMC_LDQM_GPIO_CLK();

17

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

19     /* 通用 GPIO 配置 */

20     GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//配置為復用功能

21     GPIO_InitStructure.Pull = GPIO_PULLUP; 

22     GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; 

23     GPIO_InitStructure.Alternate = GPIO_AF12_FMC; 

24

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

26     GPIO_InitStructure.Pin = FMC_A0_GPIO_PIN;

27     HAL_GPIO_Init(FMC_A0_GPIO_PORT, &GPIO_InitStructure);

28     

29     /*...*/

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

31     GPIO_InitStructure.Pin = FMC_D0_GPIO_PIN;

32     HAL_GPIO_Init(FMC_D0_GPIO_PORT, &GPIO_InitStructure);

33     

34     /*...*/

35     /*控制信號線*/

36     GPIO_InitStructure.Pin = FMC_CS_GPIO_PIN;

37     HAL_GPIO_Init(FMC_CS_GPIO_PORT, &GPIO_InitStructure);

38     

39     /*...*/

40 }

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

配置FMC的模式

接下來需要配置FMC SDRAM的工作模式,這個函數的主體是根據硬件連接的SDRAM特性,對時序結構體以及初始化結構體進行賦值。見錯誤!未找到引用源。

代碼清單 26-6 配置FMC的模式

1 void SDRAM_Init(void)

 2 {

 3

 4     FMC_SDRAM_TimingTypeDef SdramTiming;

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

 6     SDRAM_GPIO_Config();

 7

 8     /* 使能 FMC 時鍾 */

 9     __FMC_CLK_ENABLE();

10

11     /*執行SDRAM1的內存初始化序列 */

12     hsdram1.Instance = FMC_SDRAM_DEVICE;

13     /* hsdram1結構體初始化*/

14     hsdram1.Init.SDBank = FMC_SDRAM_BANK2;

15     hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;//SDRAM列數

16     hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;//SDRAM行數

17     hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;//總線數據寬度為16

18     hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;//4個扇區

19     hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;//列地址選通信延時

20     hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;//禁止寫保護

21     hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;//SDRAM時鍾fpclk=108M

22     hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;     //使能突發傳輸模式

23     hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; //讀通道延時

24     /* SDRAM時序 */

25     SdramTiming.LoadToActiveDelay = 2;//加載模式寄存器命令與行有效或刷新命令之間的延遲

26     SdramTiming.ExitSelfRefreshDelay = 8;//退出自我刷新到行有效命令之間的延遲

27     SdramTiming.SelfRefreshTime = 5;//行有效與預充電命令之間的延遲

28     SdramTiming.RowCycleDelay = 7;//兩個刷新命令或兩個行有效命令之間的延遲

29     SdramTiming.WriteRecoveryTime = 2;//寫入命令到預充電命令之間的延遲

30     SdramTiming.RPDelay = 2;//預充電與行有效命令之間的延遲

31     SdramTiming.RCDDelay = 2;//行有效與列讀寫命令之間的延遲

32

33     HAL_SDRAM_Init(&hsdram1, &SdramTiming);

34     /* FMC SDRAM 設備時序初始化 */

35     SDRAM_InitSequence();

36

37 } 

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

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

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

(2) 時序結構體賦值

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

26-5 SDRAM的延時參數(摘自《W9825G6KH》規格書)

時間參數

說明

最小值

單位

trc

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

60

ns

tras

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

42

ns

trp

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

15

ns

trcd

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

15

ns

twr

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

2

cycle

txsr 

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

72

ns

tmrd

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

2

cycle

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

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

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

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

q 設置存儲區域

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

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

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

q  CL長度

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

q  寫保護

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

q 同步時鍾參數

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

q  突發讀模式及讀延遲

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

q 時序參數

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

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

實現SDRAM的初始化時序

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

代碼清單 26-7 SDRAM上電初始化時序

1 static void SDRAM_InitSequence(void)

 2 {

 3     uint32_t tmpr = 0;

 4

 5     /* Step 1 ----------------------------------------------------*/

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

 7     Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;

 8     Command.CommandTarget = FMC_COMMAND_TARGET_BANK;

 9     Command.AutoRefreshNumber = 1;

10     Command.ModeRegisterDefinition = 0;

11     /* 發送配置命令 */

12     HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);

13

14     /* Step 2: 延時100us */

15     SDRAM_delay(1);

16

17     /* Step 3 ----------------------------------------------------*/

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

19     Command.CommandMode = FMC_SDRAM_CMD_PALL;

20     Command.CommandTarget = FMC_COMMAND_TARGET_BANK;

21     Command.AutoRefreshNumber = 1;

22     Command.ModeRegisterDefinition = 0;

23     /* 發送配置命令 */

24     HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);

25

26     /* Step 4 -----------------------------------------------------*/

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

28     Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;

29     Command.CommandTarget = FMC_COMMAND_TARGET_BANK;

30     Command.AutoRefreshNumber = 8;

31     Command.ModeRegisterDefinition = 0;

32     /* 發送配置命令 */

33     HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);

34

35     /* Step 5 ------------------------------------------------------*/

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

37     tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |

38            SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |

39            SDRAM_MODEREG_CAS_LATENCY_3           |

40            SDRAM_MODEREG_OPERATING_MODE_STANDARD |

41            SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

42

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

44     Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;

45     Command.CommandTarget = FMC_COMMAND_TARGET_BANK;

46     Command.AutoRefreshNumber = 1;

47     Command.ModeRegisterDefinition = tmpr;

48     /* 發送配置命令 */

49     HAL_SDRAM_SendCommand(&sdramHandle, &Command, SDRAM_TIMEOUT);

50

51     /* Step 6 -----------------------------------------------------*/

52

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

54     /* 刷新周期=64ms/8192=7.8125us */

55     /* COUNT=(7.8125 us x Freq) - 20 */

56     /* 設置自刷新速率 */

57     HAL_SDRAM_ProgramRefreshRate(&sdramHandle, 824);

58 }

SDRAM的初始化流程實際上是發送一系列控制命令,利用命令結構體FMC_SDRAM_CommandTypeDef及庫函數HAL_SDRAM_SendCommand配合即可發送各種命令。函數中按次序發送了使能CLK命令、預充電命令、2個自動刷新命令以及加載模式寄存器命令。

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

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

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的刷新周期。

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

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

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

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

TRefresh = 64ms/8192=7.8125us

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

所以:

COUNTA = TRefresh/TSDCLK=7.8125x108=844

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

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

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

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

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

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

代碼清單 26-9 使用指針的方式訪問SDRAM

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

 2 #define SDRAM_BANK_ADDR     ((uint32_t)0xD0000000)

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

 4 #define W9825G6KH_SIZE 0x2000000

 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的首地址;宏W9825G6KH_SIZE表示SDRAM的大小,所以從地址(SDRAM_BANK_ADDR)(SDRAM_BANK_ADDR+ W9825G6KH_SIZE)都表示在SDRAM的存儲空間,訪問這些地址,直接就能訪問SDRAM

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

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

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

代碼清單 26-10 直接指定變量地址的方式訪問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芯片讀寫校驗,見錯誤!未找到引用源。

代碼清單 26-11 main函數

1 int main(void)

 2 {

 3     /* 系統時鍾初始化成216 MHz */

 4     SystemClock_Config();

 5     /* LED 端口初始化 */

 6     LED_GPIO_Config();

 7

 8     /* 初始化串口 */

 9     DEBUG_USART_Config();

10

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

12

13     /*初始化SDRAM模塊*/

14     SDRAM_Init();

15

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

17     LED_BLUE;

18

19     /*使能RNG時鍾*/

20     __RNG_CLK_ENABLE();

21     /*初始化RNG模塊產生隨機數*/

22     hrng.Instance = RNG;

23     HAL_RNG_Init(&hrng);

24

25     printf("開始生成10000SDRAM測試隨機數\r\n");

26     for (count=0; count<10000; count++)

27

28     {

29         RadomBuffer[count]=HAL_RNG_GetRandomNumber(&hrng);

30

31     }

32     printf("10000SDRAM測試隨機數生成完畢\r\n");

33

34     SDRAM_Check();

35

36     while (1);

37

38

39 }

函數中初始化了系統時鍾、LED、串口,初始化隨機數發生模塊,產生10000個隨機數用於寫入SDRAM,接着調用前面定義好的SDRAM_Init函數初始化FMCSDRAM,然后調用自定義的測試函數SDRAM_Test測試整個SDRAM填滿隨機數,進行讀寫校驗是否正確,它就是使用指針的方式存取數據並校驗而已,此處不展開。

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

下載驗證

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


免責聲明!

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



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