來源:韋東山嵌入式專欄_ARM裸機加強版維基教程
作者:韋東山
(本文字數:8142,閱讀時長:12分鐘)
第001節輔線1硬體知識_記憶體接口概念
首先來分析下操作GPIO控制器和操作UART控制器兩者的差別。
如圖是S3C2440是個片上系統,有GPIO控制器(接有GPIO管腳),有序列槽控制器 (接有TXD RXD引腳)。
配置GPIO控制器相應的寄存器,即可讓引腳輸出高低電平;配置UART控制器相應的寄存器,即可讓引腳輸出波形。前者相對簡單,類似門電路,後者相對複雜,屬于協定類接口。類似的協定類接口還有iic、iis、spi等。對于CPU是不管什麼接口的,它隻寫相應的寄存器,由控制器根據寄存器的配置去控制具體的引腳。
那麼CPU是如何通路各個不同的寄存器的呢?
CPU隻管發出一個位址,記憶體控制器根據該位址選擇不同的子產品,然後從子產品中得到資料或者發送資料到子產品中。
前面的GPIO/門電路接口、協定類接口,都不會把位址輸出到外部,接下來的記憶體類接口,會把位址輸出到外部,比如Nor Flash、網卡、SDRAM。
如圖,SDRAM、DM9000網卡、Nor Flash都接在JZ2440的資料總線和位址總線上,CPU把資料和位址發送出去,然後記憶體控制器根據片選信号選擇相應的裝置接收位址和資料信号,互不幹擾。
片選信号和位址的關系怎麼确定?
這個是由2440晶片特性決定的。
- 當選擇Nor Flash啟動時,CPU發出的指令的位址範圍處于0x0000000 - 0x06000000,記憶體控制器就會使nGCS0處于低電平(片選引腳被選中),Nor Flash被選中。
- 當CPU發出的指令的位址範圍處于0x20000000 - 0x26000000,記憶體控制器就會使nGCS4處于低電平(片選引腳被選中),網卡被選中。
-
當CPU發出的指令的位址範圍處于0x30000000 - 0x36000000,記憶體控制器就會使nGCS6處于低電平(片選引腳被選中),SDRAM被選中。
記憶體控制器根據不同的位址位址範圍,發出不同的片選引腳,隻有被片選引腳選中的晶片才能正常工作,不被選中的晶片就像不存在一樣,不工作。
GPIO/門電路接口、協定類接口、記憶體類接口都屬于CPU的統一編址。對于Nand Flash,在原理圖上它的位址線并沒有連接配接到CPU,是以它不參與CPU的統一編址。但它的資料線也接到了資料總線上,為了防止幹擾,它也有一個片選信号(CE)。當CPU通路Nand Flash時,Nand Flash控制器才會片選Nand Flash,讓其接收資料總線上的資料。
再來看下Nor Flash的空間,0x00000000 * 0x06000000,為128M,即每一個片選信号可以選擇的空間是128M=2^27,也就需要A0、A1……A26,共27根位址線。CPU發出的32位位址線,記憶體控制器根據位址範圍,片選上相應的bank,并将位址轉化為27位。
第002節輔線1硬體知識_不同位寬裝置的連接配接
參考2440晶片手冊,可以看到記憶體接口與8-bit ROM連接配接時,2440的A0與外部晶片的A0相連。
當與兩個8-bit ROM拼接成的一個16-bit ROM連接配接時,2440的A1與外部晶片的A0相連。
當與四個8-bit ROM拼接成的一個32-bit ROM連接配接時,2440的A2與外部晶片的A0相連。
當與一個16-bit ROM連接配接時,2440的A1與外部晶片的A0相連。
可以看出外接晶片的位寬有變化時,位址線的接法也會有變化。那這個變化有什麼規律呢?
假設CUP執行:
MOV R0, #3 @去位址為3的記憶體上LDRB R1, [R0] @ 從記憶體為3的位址上,讀出一個位元組
如圖有8bitROM、16bitROM、32bitROM。
8個bit組成一個位元組,位元組是計算機的最小的存儲機關,是以我們讀取資料肯定都是8bit的倍數。
* 對于8bitROM ,8bit是一次讀寫的最小機關,即0位址是第一個8bit,1位址是第二個8bit;CPU發出的指令是讀取位址為3上的資料,即A0和A1都為1,8bitROM的A0和A1收到的也都是1,于是找到了ROM上位址為3的8bit資料,包含了我們需要的資料。
* 對于16bitROM ,16bit是一次讀寫的最小機關,即0位址是第一個16bit,裡面有兩個8bit資料;CPU發出的指令是讀取位址為3上的資料,即A0和A1都為1,16bitROM的A0和A1分别收到的是1和0,于是找到了ROM上位址為1的16bit資料,包含了我們需要的資料,最後記憶體控制器再幫我們挑選出所需的8bit資料。
* 對于32bitROM ,32bit是一次讀寫的最小機關,即0位址是第一個32bit,裡面有四個8bit資料;CPU發出的指令是讀取位址為3上的資料,即A0和A1都為0,32bitROM的A0和A1收到的都是0,于是找到了ROM上位址為0的32bit資料,包含了我們需要的資料,最後記憶體控制器再幫我們挑選出所需的8bit資料。
接到晶片上的引腳用來确定讀取晶片上的哪一個單元的資料,把這個單元的資料傳回給記憶體控制器,記憶體控制器會根據沒有連接配接晶片的引腳,來确定傳回哪一個單元的資料給CPU,
再舉一個例子:假如傳遞一個32位的資料時
MOV R0, #4LDR R1, [R0] @去位址4,讀取4位元組資料
執行過程如下:
- 8bitROM: 當CPU發出位址(000100),記憶體控制器會把000100,000101,000110,000111處的位址轉發給ROM,ROM會把得到的位址000100,000101,000110,000111,上的資料傳回給記憶體控制器,記憶體控制器會把得到的4個8bit的資料組裝成一個32位的資料傳回給CPU。
- 16bitROM: 當CPU發出位址(000100),記憶體控制器會把00010,00011處的位址轉發給ROM,ROM會把得到的位址00010,00011,上的資料傳回給記憶體控制器,記憶體控制器會把得到的2個16bit的資料組裝成一個32位的資料傳回給CPU。
- 32bitROM: 當CPU發出位址(000100),記憶體控制器會把0001處的位址發送給ROM,ROM會把得到的位址0001上的資料傳回給記憶體控制器,記憶體控制器會把得到的1個32bit資料傳回給CPU。
怎樣确定晶片的通路位址:1. 根據片選信号确定基位址,2. 根據晶片所接位址線确定範圍
執行個體:Nor Flash 使用的是片選0(nGCS0),基位址為0,用到A20,A19……A1,A0共21條位址線,是以位址範圍為0x00000000 ~ 0x1FFFFF也就是2M的空間大小。網卡(Net)使用的是片選4(nGCS4),基位址為0x20000000,用到A2,A0共2根位址線,是以位址範圍為0x20000000 ~ 0x20000005。SDRAM使用的是片選6(nGCS6),基位址為0x30000000。
第003節輔線1硬體知識_時序圖分析示例
這節我們分析一下我們了解時序圖,信号之間是怎樣一起工作的,以Nor Flash 為例。
2440和Nor Flash 之間有位址線,資料線,還有各種資料線連接配接。
以Nor Flash為例,分析下如何設定它的時序。
如圖是S3C2440的Nor Flash控制器的讀時序圖,裡面很多參數都需要根據外接晶片的性能進行設定,有的晶片性能好、響應時間快,就可以把參數時間設定小一點,釋放更好的性能。
如圖是Nor Flash晶片的讀時序。
我們需要做的就是設定S3C2440的Nor Flash控制器時序去滿足Nor Flash晶片的時序。每個參數的參考範圍可以通過AC CHARACTERISTICS得到。
結合Nor Flash晶片的兩張圖,可以得到如下資訊:
- 發出位址資料(Addresses)後,要等待Taa(要求大于等于70ns)時間,位址資料才有效;
- 發出片選信号(CE#)後,要等待Tce(要求大于等于70ns)時間,片選信号才有效;
- 發出讀信号(OE#)後要等待Toe(要求大于等于30ns)時間,讀信号才有效;
為了簡單我們把位址資料(Addresses),片選信号(CE#),讀信号(OE#),同時發出,然後讓它們都等待70ns(等待信号有效)。對應S3C2440的Nor Flash控制器的讀時序圖,需要讓位址信号A[24:0]、片選信号nGCS、讀信号nOE同時發出,保持Tacc大于等于70ns。
查閱S3C2240的參考手冊,Nor Flash是接在BANKCON0上的,是以隻需要設定BANKCON0即可。
可以看到Tacc上電初始值是111,對應14個clocks。系統上電采用12MHz的晶振,HCLK=12MHz,Tacc=(1000/12*14)≈1166ns,這個值很大,幾乎可以滿足所有Nor Flash的要求。
啟動後,将HCLK設定為100MHz,T=1000/100=10ns,Tacc需要大于等于70ns,是以設定Tacc等于101,8個clocks即可。
在前面uart實驗的源碼基礎上,建立init.c和init.h兩個檔案。
在init.c裡面隻需要設定BANKCON0寄存器即可。
#include "s3c2440_soc.h"void bank0_tacc_set(int val){ BANKCON0 = val << 8;}
init.h進行函數聲明。
#ifndef _INIT_H#define _INIT_Hvoid bank0_tacc_set(int val);#endif
最後在主函數裡面,通過序列槽擷取輸入的值,傳入bank0_tacc_set()函數裡,設定Tacc,然後再讀取Nor Flash上的閃燈程式。
#include "s3c2440_soc.h"#include "uart.h"#include "init.h"int main(void){ unsigned char c; uart0_init(); puts("Enter the Tacc val: "); while(1) { c = getchar(); putchar(c); if (c >= '0' && c <= '7') { bank0_tacc_set(c - '0'); led_test(); } else { puts("Error, val should between 0~7"); puts("Enter the Tacc val: "); } } return 0;}
實驗效果:
輸入0~4,Tacc小于70ns,無法讀取Nor Flash上資料,LED不能閃爍。
輸入4~7,Tacc大于70ns,可以讀取Nor Flash上資料,LED不斷閃爍,且值越小越快(差別不明顯)。
第004節輔線1硬體知識_SDRAM的設定
本節将講解如何設定SDRAM,如果想對記憶體有更多的了解,可以在網上搜尋看下這篇文檔“高手進階_終極記憶體技術指南——完整/進階版”。
在JZ2440上接有64M的SDRAM,如果想要使用SDRAM,需要對記憶體控制器做一些設定。在前面第一節講到,CPU将資料或位址發給記憶體控制器,記憶體控制器再去通路外部的SDRAM,是以設定記憶體控制器就說本節的核心。
如圖是SDRAM存儲結構邏輯圖:
SDRAM總共有4個塊(Banks),可以認為每個塊就是一個表格,裡面的每個格子表示的是16bit資料。
- 問題1:怎樣通路裡面的某個格子呢?
- 首先發出一個片選信号,選中整個晶片;
- 發出Bank位址,選擇是哪一個Bank(塊,即表格);
- 發出行位址;
- 最後發出列位址,才能選中是個格子;
- 問題2:那麼多的信号有誰發出呢?
由記憶體控制器發出,是以我們需要設定記憶體控制器,CPU隻是簡單的執行讀寫記憶體的指令,其他的都交給記憶體控制器來處理。
例如:
LDR R0,=0x30000000LDR R1,[R0]
- CPU把0x30000000這個位址發給記憶體控制器,記憶體控制器根據這個位址,判斷屬于哪個範圍,然後發出相應的片選信号。
- 根據類型(比如SDRAM)拆分成三部分:發出Bank位址、發出行位址、發出列位址。
對于上面的三個,怎樣拆分呢?
行位址線有幾條,列位址線有幾條,都要設定記憶體控制器裡面相關寄存器。
- 讀資料
綜上所述:對SDRAM的通路可以分為如下步:
- CPU發出的片選信号nSCS6有效,它選中SDRAM晶片。
- SDRAM中有4個L-Bank,需要兩根位址信号來選中其中之一,根據原理圖,可知使用ADDR24,ADDR25作為L-Bank的選擇信号。
- 對被選中的晶片進行統一的行/列(存儲單元)尋址。
根據SDRAM晶片的列位址線數目設定CPU的相關寄存器後,CPU就會從32位的位址中自動分出L_Bank片選信号,行位址信号,列位址信号,然後先後發出行位址信号,列位址信号。L_Bank選擇信号在發出行位址信号的同時發出,并維持到列位址信号結束。
根據原理圖可知:
位址、列位址共用地線ADDR2—ADDRI4(BANK6位寬為32,ADDRO/I沒有使用),使用nSRAS、nSCAS兩個信号來區分它們。
比如本開發闆中,使用兩根位址線ADDR24、ADDR25作為L-Bank的選擇信号:SDRAM晶片K4s5m632的行位址數為13,列位址數為9,是以當nSRAS信号有時,ADDR2—ADDR14上發出是行位址信号,它對應32位位址空間的b可23m]:當nSCAS信号有效時,ADDR2—ADDR10上發出的是列位址信号,它對應32位位址空間的bit[0:2];由于BANK6以32位的寬度外接DRAM,ADDR0、ADDR1恒為0,不參與譯碼。
- 找到了存儲單元後,被選中的晶片就要進行統一的資料傳輸了。
開發闆中使用兩片16位的SDRAM晶片并聯組成32位的位寬,與CPU的32根資料線(DATA0—DATA31)相連。BANK6的起始位址為0x30000000,是以SDRAM的通路位址為0x30000000~低0x33FFFFFF,共64MB。
2440記憶體控制器設定:
記憶體控制器共有13個寄存器,
BANK0–BANK5隻需要設定BWSCON和BANKCONx(x為0~5)兩個寄存器;
BANK6、BANK7外接SDRAM時,除BWSCON和BANKCONx(x為6、7)外,還要設定REFRESH、BANKSIZE、MRSRB6、MRSRB7等4個寄存器。
下面分類說明各個寄存器的設定。
- 1.位寬和等待控制寄存器BWSCON(BUSWIDTH&WAITCONTROLREGISTER)
BWSCON中每4位控制一個BANK,最高4位對應BANK7(沒有使用)、接下來4位對應BANK6。
(1)ST6[27]: 啟動/禁止SDRAM的資料掩碼引腳,對于SDRAM,此位為0:對于SRAM此位為1。
(2)WS6[26]:是否使用存儲器的WAIT信号,通常設為0。
(3)DW6[25:24]:使用兩位來設定相應BANK的位寬,0b00對應8位,0b01對應16位,0b10對應32位(開發闆用的就是32位的),0b11表示保留。
是以BWSCON寄存器的值為:0x22000000。
-
2.BANK控制寄存器BANKCON6(BANKCONTROLREGISTER)
在8個BANK中,隻有BANK6和BANK7可以外接SRAM或SDRAM。
- (1)MT[16:15]:用于設定本BANK外接的是ROM/SRAM還是SDRAM,SRAM:0b00,SDRAM:0b11(開發闆使用的是SDRAM)。
當MT[16:15]設定為00時,此寄存器與BANKC0N0、BANKCON5類似,不再贅述。
當MT[16:15]設定為11時,此寄存器其他值如下設定。
(2)Trcd[3:2]:行位址和列位址間隔多長時間,看晶片手冊時間間隔是20ns,本來開發闆的HCLK是100MHZ,clocks為10ns,是以設定為推薦值0b00(2clocks)。
(3)SCAN[1:0]:SDRAM的列位址位數,對于本開發闆使用的SDRAMK4S561632。列位址位數為9,是以SCAN=0b010如果使用其他型号的SDRAM,需要檢視其資料手冊。來決定SCAN的取值。0b00表示8位,0b01表示9位,0b10表示10位。
綜上所述,本開發闆中BANKCON6設為0x017001。
- 3.重新整理控制寄存器REFRESH(REFRESHCONTROLREGISTER)
(1)REFEN[23]:0=禁止SDRAM的重新整理功能,1:開啟SDRAM的重新整理功能(設定開啟SDRAM的重新整理功能)。
(2)TREFMD[22]:SDRAM的重新整理模式,0=CBR/AutoRefresh,1=SelfRefresh(一般在系統休眠時使用),我們設定預設值。
(3)Trp[21:20):根據晶片手冊設為0即可。
(4)Tsrc[19:18]:根據晶片手冊設為預設值0b01即可。
(5)RefreshCounter[10:0]:即R_CNT
R_CNT可如下汁算(SDRAM時鐘頻率就是HCLK):R_CNT=2^11+1-SDRAM時鐘頻率(MZ)*SDRAM重新整理周期(us)
SDRAM的重新整理周期在SDRAM的資料手冊上有标明,在本開發闆使用的SDRAM:K4S561632的資料手冊上,可看見這麼一行“64msrefreshpenod(8KCycle)是以,重新整理周期=64ms/8192=7.8125us。Refreshcount=2^11+1-100x7.8=1269=0x4F5。
是以,本開發闆中REFRESH設為0x8404F5。
- 4.BANKSIZE寄存器REFRESH(BANKSIZEREG ISTER)
(1)BURST_EN[7]:0=ARM核禁上突發傳輸,1=ARM核支援突發傳輸(推薦);
(2)SCKEEN[5]:0=不使用SCKE信号令SDRAM進入省電模式,1=使用SCKE信号令SDRAM進入省電模式(推薦);
(3)SCLK-EN[4]:0=時刻發出SCLK信号,1=僅在通路SDRAM期間發出SCLK信号(推薦);
(4)BK76MAP[2:0]:設定BANK6的大小。本開發闆BANK6外接64MB的SDRAM,令[2:0]=b001(64M/64M),表示BANK6/7的容量都是64MB,雖然BANK7沒有使用。
是以,本開發闆中BANKSIZE設為0xB1。
- 5.SDRAM模式設定寄存器MRSRBx6(SDRAM MODE REGISTER SET REGISTER)
能修改的隻有位CL[6:4],這是SDRAM時序的一個時間參數,表示發出行、列位址後,等多久才傳回收到資料,CL可以取值為0b0l0(2 clocks)或0b011(3 clocks)。
本開發闆取最保守的值0b010,是以MRSRB6的值為0x20。
下面開始寫程式:在init.c裡面進行對記憶體控制器的寄存器依次進行設定:
void sdram_init(void){ BWSCON = 0x22000000; BANKCON6 = 0x17001; BANKCON7 = 0x17001; REFRESH = 0x8404f5; BANKSIZE = 0xb1; MRSRB6 = 0x20; MRSRB7 = 0x20;}
再寫一個測試函數,向SDRAM裡面連續寫1000個數,再讀出資料對比是否是設定的數,傳回對比結果:
int sdram_test(void){ volatile unsigned char *p = (volatile unsigned char *)0x30000000; int i; // write sdram for (i = 0; i < 1000; i++) p[i] = 0x55; // read sdram for (i = 0; i < 1000; i++) if (p[i] != 0x55) return -1; return 0;}
在主函數裡調用sdram_test()測試函數,如果測試成功,LED閃爍:
int main(void){ uart0_init(); sdram_init(); if (sdram_test() == 0) led_test(); return 0;}
實驗結果:
LED按預期閃爍,屏蔽掉sdram_init()後,LED不閃爍。
「新品首發」STM32MP157開發闆火爆預售!首批僅300套