溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

匯編實(shí)現(xiàn)時(shí)鐘設(shè)置代碼理解

發(fā)布時(shí)間:2020-08-03 18:48:29 來源:網(wǎng)絡(luò) 閱讀:827 作者:beka 欄目:開發(fā)技術(shù)

匯編實(shí)現(xiàn)時(shí)鐘設(shè)置代碼理解

下面的筆記是我在看《朱老師物聯(lián)網(wǎng)大講堂》(www.zhulaoshi.org)之后所做的筆記,只是大概根據(jù)自己看了視頻與朱老師上課做的筆記而有的理解記錄下來。

寫了

有代碼的,要把代碼給理解完整。

朱老師的隨堂程序是:clock.s

// 時(shí)鐘控制器基地址

#define ELFIN_CLOCK_POWER_BASE0xE0100000

 

// 時(shí)鐘相關(guān)的寄存器相對(duì)時(shí)鐘控制器基地址的偏移值

#define APLL_LOCK_OFFSET0x00

#define MPLL_LOCK_OFFSET0x08

 

#define APLL_CON0_OFFSET0x100

#define APLL_CON1_OFFSET0x104

#define MPLL_CON_OFFSET0x108

 

#define CLK_SRC0_OFFSET0x200

#define CLK_SRC1_OFFSET0x204

#define CLK_SRC2_OFFSET0x208

#define CLK_SRC3_OFFSET0x20c

#define CLK_SRC4_OFFSET0x210

#define CLK_SRC5_OFFSET0x214

#define CLK_SRC6_OFFSET0x218

#define CLK_SRC_MASK0_OFFSET    0x280

#define CLK_SRC_MASK1_OFFSET    0x284

 

#define CLK_DIV0_OFFSET0x300

#define CLK_DIV1_OFFSET0x304

#define CLK_DIV2_OFFSET0x308

#define CLK_DIV3_OFFSET0x30c

#define CLK_DIV4_OFFSET0x310

#define CLK_DIV5_OFFSET0x314

#define CLK_DIV6_OFFSET0x318

#define CLK_DIV7_OFFSET0x31c

 

#define CLK_DIV0_MASK0x7fffffff

 

// 這些M、P、S的配置值都是查數(shù)據(jù)手冊(cè)中典型時(shí)鐘配置值的推薦配置得來的。

// 這些配置值是三星推薦的,因此工作最穩(wěn)定。如果是自己隨便瞎拼湊出來的那就要

// 經(jīng)過嚴(yán)格測(cè)試,才能保證一定對(duì)。

#define APLL_MDIV       0x7d// 125

#define APLL_PDIV       0x3

#define APLL_SDIV       0x1

 

#define MPLL_MDIV0x29b// 667

#define MPLL_PDIV0xc

#define MPLL_SDIV0x1

 

#define set_pll(mdiv, pdiv, sdiv)(1<<31 | mdiv<<16 | pdiv<<8 | sdiv)

#define APLL_VALset_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)

#define MPLL_VALset_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)

 

 

.global clock_init

clock_init:

ldrr0, =ELFIN_CLOCK_POWER_BASE


// 1 設(shè)置各種時(shí)鐘開關(guān),暫時(shí)不使用PLL

ldrr1, =0x0

// 芯片手冊(cè)P378 寄存器CLK_SRCSelect clock source 0 (Main)

strr1, [r0, #CLK_SRC0_OFFSET]

 

// 2 設(shè)置鎖定時(shí)間,使用默認(rèn)值即可

// 設(shè)置PLL后,時(shí)鐘從Fin提升到目標(biāo)頻率時(shí),需要一定的時(shí)間,即鎖定時(shí)間

ldrr1,=0x0000FFFF

strr1,[r0, #APLL_LOCK_OFFSET]

str r1, [r0, #MPLL_LOCK_OFFSET] 

 

// 3 設(shè)置分頻

// bit[0~31]

ldr r1, [r0, #CLK_DIV0_OFFSET]

ldrr2, =CLK_DIV0_MASK

bicr1, r1, r2

ldrr2, =0x14131440

orrr1, r1, r2

strr1, [r0, #CLK_DIV0_OFFSET]

 

// 4 設(shè)置PLL

// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz

ldrr1, = APLL_VAL

strr1, [r0, #APLL_CON0_OFFSET]

// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz

ldrr1, = MPLL_VAL

strr1, [r0, #MPLL_CON_OFFSET]

 

// 5 設(shè)置各種時(shí)鐘開關(guān),使用PLL

ldrr1, [r0, #CLK_SRC0_OFFSET]

ldrr2, =0x10001111

orrr1, r1, r2

strr1, [r0, #CLK_SRC0_OFFSET]

 

movpc, lr

(下面就是對(duì)這一個(gè)代碼的理解)

對(duì)clock.s這一個(gè)匯編代碼的理解

    這一段代碼的第一部分:設(shè)置各種時(shí)鐘開關(guān),暫時(shí)不用PLL

    在前面的課程里面有對(duì)于時(shí)鐘框圖的解釋,像在文檔的3.2Figure3.3的左上角的部分有畫著這么樣的東西,初始時(shí)鐘從左邊過來,然后有兩條路走,一條是通過PLL另一條是直接連到一個(gè)MUX開關(guān),所以我們?cè)O(shè)置初始時(shí)鐘而且不用PLL的時(shí)候我們做的就是選MUX開關(guān)來達(dá)到只設(shè)置時(shí)鐘不通過(繞過)PLL。這樣子的話就要去設(shè)置寄存器控制選通位了。

代碼:

ldrr0, =ELFIN_CLOCK_POWER_BASE

ldrr1, =0x0

strr1, [r0, #CLK_SRC0_OFFSET]// 芯片手冊(cè)P378 寄存器CLK_SRCSelect clock source 0 (Main) 


解釋說到上面的代碼:     

上往下數(shù)第一句

這一句是把ELFIN_CLOCK_POWER_BASE這個(gè)值賦給r0寄存器,為什么要賦這一個(gè)值呢?

可以從這一段匯編函數(shù)開始的更前面看起,這一個(gè)“ELFIN_CLOCK_POWER_BASE”是一個(gè)宏定義就是相當(dāng)把ELFIN_CLOCK_POWER_BASE0xE0100000等值起來了(就是用到ELFIN_CLOCK_POWER_BASE的時(shí)候就是相當(dāng)于在用0xE0100000這個(gè)值)。

    為什么要把這個(gè)值在前面給宏定義出來呢?

從數(shù)據(jù)手冊(cè)的3.7 REGISTER DESCRIPTION看到關(guān)于這個(gè)時(shí)鐘的寄存器地址都是從0xE0100000這個(gè)地址開始,這個(gè)就是基地址,其他的地址都是相對(duì)著這一個(gè)地址偏移多少的量,通過一個(gè)計(jì)算,就是用基址地址加上偏移的量就可以找到我們想要設(shè)置的寄存器了,

如何通過上述所說的來給目標(biāo)寄存器寫東西呢? 

代碼的第三句有一個(gè)CLK_POWER_BASE,這個(gè)東西就是偏移量啦,但這是一堆字母沒看到量啊,其實(shí)就是想上面的ELFIN_CLOCK_POWER_BASE一樣,在前面已經(jīng)給宏定義一個(gè)值了這一個(gè)值是0x200,因?yàn)槲覀円獙懙募拇嫫魇蔷嚯x0xE01000000x200,我們就依據(jù)這個(gè)東西來找寄存器,并且寫入東西。寫入的值是0x0.就是這一個(gè)寄存器全部寫零。 為什么寫入0xE0100200這一個(gè)寄存器呢?

    這個(gè)地址所對(duì)應(yīng)的寄存器的名稱是:CLK_SRC0,首先去看一下這個(gè)寄存器是干嘛的,在手冊(cè)的378頁有這個(gè)寄存的介紹與如何設(shè)置(3.7.3.1 Clock Source Control Registers)。為什么要設(shè)置這個(gè)寄存器,首先就要看我們剛開始的目的是什么,我們想干嘛。我們是想“設(shè)置各種時(shí)鐘開關(guān),暫時(shí)不用PLL”,那么我們?cè)趺慈?shí)現(xiàn)呢,那就要看一下,哪一個(gè)寄存器能夠滿足我們的要求了??匆幌逻@一寄存器的功能是,數(shù)據(jù)手冊(cè)上面寫的什么Clock Source Control,就是時(shí)鐘源控制的寄存器,我想的是這一個(gè)寄存器就是控制時(shí)鐘源的來源,從圖上面看的是設(shè)置晶振與時(shí)鐘發(fā)生器產(chǎn)生的時(shí)鐘是否是要經(jīng)過PLL,這樣子就可以滿足我們的目的要求了,我們的目的就是不要PLL,晶振與時(shí)鐘產(chǎn)生多少的頻率就向右傳多 少頻率,那么我們就是要使它不使用經(jīng)過PLL的頻率好。

    為什么傳入0x0:     

    從手冊(cè)上面看到這一個(gè)寄存器的初始值(默認(rèn)值都是選擇0的)就是都是剛開始默認(rèn)不用PLL倍頻過后的程序。這個(gè)的話與上面的目標(biāo)相符。這里重點(diǎn)看這幾個(gè):               

1、VPLL_SEL

2、EPLL_SEL

    3MPLL_SEL

4、APLL_SEL 

這幾個(gè)控制位都是賦值零的。就是都選擇FINOUT,這么選,貌似是因?yàn)?/span>s5pv210這個(gè)板子上面只焊了一個(gè)時(shí)鐘發(fā)生器。這個(gè)可供其他使用了,這樣子的話都選擇了0,那么就是都不經(jīng)過PLL的了,這樣的話就是不倍頻了。

代碼最后一段: 

這個(gè)是按照變址尋址來做的,就是把r1的值寫入到r0+CLK_SRC0_OFFSET這一個(gè)地址里面去(因?yàn)槭墙y(tǒng)一編址,這個(gè)是個(gè)寄存器地址)。 

到此,設(shè)置各種時(shí)鐘開關(guān),暫時(shí)不用PLL的問題解決了。

 

第二步:設(shè)置鎖定時(shí)間,使用默認(rèn)值即可。 

為什么要設(shè)置鎖定時(shí)間,我的認(rèn)為,因?yàn)殒i相環(huán)(PLL)想要初始時(shí)鐘(24MHz)在瞬間完成倍頻到1G是不可能的,它需要低頻率在PLL里面回環(huán)轉(zhuǎn),一次次頻率的升高來達(dá)到1G這樣子的話就是是需要時(shí)間的,所以等一會(huì)兒,好了就可以了,在文檔(3.7.2 PLL CONTROL REGISTERS)上面是說當(dāng)輸入的頻率頻率改變或者分頻的值改變是得鎖定時(shí)間。鎖定時(shí)間的長(zhǎng)短基于PLL的源時(shí)鐘,用PLL_LOCK這一個(gè)寄存器去設(shè)置,

ldrr1,=0x0000FFFF

strr1,[r0, #APLL_LOCK_OFFSET]

str r1, [r0, #MPLL_LOCK_OFFSET]

為什么寫入0x0000FFFF 

這個(gè)寄存器只有低十六位可以用,最大值就是0x0000FFFF,值設(shè)置越大,鎖定的時(shí)間就越就久。默認(rèn)值是0x00000FFF,設(shè)置這一個(gè)最大值也沒事,也就是隔久一點(diǎn)而已。時(shí)間超過剛好值的話那時(shí)候已經(jīng)是好了。

有兩個(gè)寄存器,就是鎖好兩個(gè)APLLMPLL兩個(gè)倍頻器。就是這樣。

第三步:設(shè)置分頻

設(shè)置分頻系統(tǒng),由它決定給左邊的分多少倍從而得到右邊的頻率,說得清楚一些就是左邊的頻率除以一個(gè)分頻器可以接受的值,然后得到的值輸出到右邊。我們要做的就是設(shè)置分頻系數(shù),說清楚些就是設(shè)置那一個(gè)除數(shù)。設(shè)置的寄存器是CLK_DIV

如何設(shè)置呢?

視頻上面用說的方式給清楚地說出了如何去設(shè)置,在代碼中那個(gè)值是什么意思了。

    直接用代碼中的設(shè)置值來分析:

ldr r2, =0x14131440 

r2所指代的寄存器寫入0x14131440,從文檔的3.7.4.1 Clock Divider Control Register這一部分看。所寫入的數(shù)據(jù)的第一個(gè)1,是3028位的,在關(guān)于這一個(gè)寄存器的設(shè)置描述欄中寫的是公式PCLK_PSYS = HCLK_PSYS / (PCLK_PSYS_RATIO + 1),我們寫的1,那就相當(dāng)于給PCLK_PSYS_RATIO的值賦了一個(gè)1,這樣子的話下面的除數(shù)就是2.因?yàn)?/span>1+1等于2PCLK_PSYSHCLK_PSYS要從文檔上面3.4 CLOCK GENERATION的那個(gè)3.3圖中找,可以找到在右下角的PSYS域中的HCLK_PSYSPCLK_PSYS,描述欄的公式意思是PCLK_PSYS的時(shí)鐘頻率是通過HCLK_PSYS的頻率除以2得到的。

代碼的理解:

  ldr r1, [r0, #CLK_DIV0_OFFSET]

  ldrr2, =CLK_DIV0_MASK

  bicr1, r1, r2

  ldrr2, =0x14131440

  orrr1, r1, r2

  strr1, [r0, #CLK_DIV0_OFFSET] 

//忽然發(fā)現(xiàn)我理解不了方法是往哪個(gè)寄存器寫東西?為什么往那里寫,為什么寫//0x14131440?(漫長(zhǎng)) 

第四步:設(shè)置PLL                  

這個(gè)地方也是要學(xué)會(huì)看文檔。可見有會(huì)看文檔的功力也是很重要的

講的是APLLMPLL,這兩個(gè)PLL我們可以在文檔的361頁可以看到圖。 

要對(duì)它做什么?           

對(duì)他設(shè)置合適的參數(shù),然后lock,然后等待輸出。 

怎么設(shè)置參數(shù):         

找文檔,現(xiàn)在設(shè)置的是APLL,找啊找,在文檔的372,上面有個(gè)APLL_CON0,看這一頁的寄存器位的介紹,首先要看一個(gè)公式: 

 FOUT = MDIV X FIN / (PDIV × 2^(SDIV-1)) 

這個(gè)公式是與寄存位的介紹配合起來看的,想知道在公式上面的變量怎么設(shè)置看寄存器位的介紹表中的MDIV[25:16]、PDIV[13:8]、SDIV[2:0]這幾位。這幾個(gè)很重要。

公式中的MDIV值就是設(shè)置寄存器中的25:26位的值,FIN的值是左邊的頻率進(jìn)PLL的值,比如在文檔上面“3.4 CLOCK GENERATION”部分的那個(gè)圖中看左上角的APLL地方的左邊寫著有PLL這就是FIN。FOUT同理可以理解。PDIV就是我們?cè)O(shè)置的PDIV(所在寄存器的位是13:8)的值,SDIV是就是設(shè)置的SDIV所在寄存器的位是(20)的值。

例:在文檔中給定的初始值是:MDIV0xc8(對(duì)應(yīng)的10進(jìn)制是200),PDIV0x3(對(duì)應(yīng)的10進(jìn)制是3),SDIV0x1(對(duì)應(yīng)的10進(jìn)制是1),對(duì)于APLL的話FIN24MHz把這些值代入上式得FOUT=24*200/3*2^0=1600,這是文檔上面一個(gè)想不到的東西,因?yàn)槲覀円O(shè)置的是1GHz,但是按照這一個(gè)初值來設(shè)置的話就變成了1.6GHz了。在程序中的數(shù)值與上述的數(shù)值不一樣的是MDIV的值,這個(gè)值應(yīng)該設(shè)置成0x7d(對(duì)應(yīng)的10進(jìn)制是125)。關(guān)于這些經(jīng)典值可以到文檔的357頁的3.3.1RECOMMENDED PLL PMS VALUE FOR APLL可以得到三星給的推薦值。

但是在程序中計(jì)算FOUT的值有點(diǎn)讓人眼前一新(自己沒有碰到過的,在朱老師的C高級(jí)里面有講位運(yùn)算):

#define set_pll(mdiv, pdiv, sdiv)(1<<31 | mdiv<<16 | pdiv<<8 | sdiv)

#define APLL_VALset_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)

#define MPLL_VALset_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)

下面要講的是MPLL

MPLL的設(shè)置方法與APLL是一樣的,只是公式不一樣而已。

第五步:設(shè)置各種時(shí)鐘開關(guān),使用PLL

從代碼來看:

ldrr1, [r0, #CLK_SRC0_OFFSET]

ldrr2, =0x10001111

orrr1, r1, r2

strr1, [r0, #CLK_SRC0_OFFSET]

這里要理解的是0x10001111的問題,在CLK_SRC0寄存器中寫入這一個(gè)值,為什么要寫入這一個(gè)值。

這里又要配合著文檔上面的《3.7.3.1 Clock Source Control Registers》這一部分來查看開了使用了哪一個(gè)東西,之后去配合《3.4 CLOCK GENERATION》這一部分的圖后,結(jié)合之前學(xué)過的xPLLDIV的使用原理,可以算出在CLK_SRC0之后,頻率經(jīng)過什么樣的路徑,最后到達(dá)使用的部件的頻率是多少。

 

剛剛才入ARM裸機(jī)這水,還不深,其中肯定會(huì)有疏忽和錯(cuò)誤,如果看了這篇筆記發(fā)現(xiàn)了錯(cuò)誤,請(qǐng)指出,謝謝。






      

 



向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI