1. 程式人生 > >x210 一步步點亮LED

x210 一步步點亮LED

看朱老師的視訊,一點點記錄


1 LED硬體工作原理及原理圖查閱(硬體有關)

1.1 原理圖是在底板x210bv3中查詢


查閱原理圖,發現開發板上一共有5顆LED。其中一顆D26的接法是:正極接5V,負極接地。因此這顆LED只要上電就會常亮。因此我們分析這顆LED是電源指示燈。

剩下4顆LED的接法是:正極接3.3V,負極接了SoC上的一個引腳(GPIO),具體詳細接法是:

D22:GPJ0_3

D23:GPJ0_4

D24:GPJ0_5

D25:PWMTOUT1(GPD0_1)    [PWMTOUT1接在了核心板x210cv3,利用好pdf 閱讀器中的搜尋功能

]

1.2 分析如何點亮及熄滅LED(GPIO)

分析:LED點亮的要求是:正極和負極之間有正向電壓差。

思考:在開發板上如何為LED製造這個電壓差讓它點亮呢?

解答:因為正極已經定了(3.3V),而負極接在了SoC的引腳上,可以通過   SoC中程式設計來控制負極的電壓值,因此我們可以通過程式控制負極輸 出低電平(0V),這樣在正負極上就有了壓差,LED即可點亮。

資料手冊查閱及相關暫存器瀏覽

2.1 GPIO概念的引入

GPIO:general purpose input output 通用輸入輸出

GPIO就是晶片的引腳(晶片上的引腳有些不是GPIO,只有一部分是),作為GPIO的這類引腳,他的功能和特點是可以被程式設計控制它的工作模式,也可以程式設計控制他的電壓高低等。

通過之前的分析我們知道,我們設計電路時就把LED接在了一個GPIO上,這樣我們就可以通過程式設計控制GPIO的模式和輸入輸出值來操控LED亮還是滅;如果你當時設計電路時把LED接在非GPIO上那就不可能了。

 

2.2 閱讀資料手冊中有關部分(S5PV210_UM_REV1.1)

當我們想要通過程式設計操控GPIO來操作LED時,我們首先需要通讀一下S5PV210的資料手冊中有關於GPIO的部分,這部分在資料手冊的Section2.2中。


GPJ0CON[0] 就是GPJ0CON_0

[3:0]  —— bit3 ~ bit0   4位

 bit3 ~ bit0,設定的不同分別有7種模式

2.3 GPIO相關的暫存器介紹

回憶下之前說過的,軟體操作硬體的介面是:暫存器。

我們當前要操作的硬體是LED,但是LED實際是通過GPIO來間接控制的,所以當前我們實際要操作的裝置其實是SoC的GPIO。要操作這些GPIO,必須通過設定他們的暫存器。

查閱資料手冊(S5PV210_UM_REV1.1)可知,GPJ0相關的暫存器有以下:

GPJ0CON, (GPJ0 control)GPJ0控制暫存器,用來配置各引腳的工作模式

GPJ0DAT, (GPJ0 data)當引腳配置為input/output模式時,暫存器的相 應位和引腳的電平高低相對應。

GPJ0PUD, (pull up down)控制引腳內部弱上拉、下拉

GPJ0DRV, (driver)配置GPIO引腳的驅動能力

GPJ0CONPDN,(記得是低功耗模式下的控制暫存器)

GPJ0PUDPDN  (記得是低功耗模式下的上下拉暫存器)

注:在驅動LED點亮時,應該將GPIO配置為output模式。

實際上真正操控LED的硬體,主要的有:GPJ0CON, GPJ0DAT 這麼2個。

如何點亮LED,程式設計的步驟是:

1、操控GPJ0CON暫存器中,選中output模式

2、操控GPJ0DAT暫存器,相應的位設定為0

3.從零開始手寫彙編點亮LED

3.1 GPxCON、GPxDAT暫存器分析

GPJ0埠一共有8個引腳,分別記住:GPJ0_0 ~ GPJ0_7,相關重要暫存器就是GPJ0CON和GPJ0DAT

GPJ0CON暫存器中設定8個引腳的工作模式(32/8=4,每個引腳可以分到4位,譬如GPJ0_0對應的bit位為bit0~bit3,GPJ0_3對應的位為bit12~bit15。工作方法是:給相應的暫存器位寫入相應的值,該引腳硬體就會按照相應的模式去工作。譬如給bit12~bit15寫入0b0001,GPJ0_3引腳就成為輸出模式了)

 

3.2 從零開始寫程式碼操作暫存器

需要哪些先決條件才能寫呢?

1. 硬體接法和引腳:GPJ0_3 GPJ0_4 GPJ0_5 低電平亮/高電平滅;

2. GPJ0CON(0xE0200240)暫存器和GPJ0DAT(0xE0200244)暫存器;

3. 工程管理:Makefile等。

根據以上分析,我們就知道程式碼的寫法了,程式碼所要完成的動作就是:

把相應的配置資料寫入相應的暫存器即可。

3.3 怎麼寫?

先將朱老師寫好的led檔案(1.leds_s)中的 三個檔案(Makefile、mkv210_image、write2sd)複製到自己的資料夾(2.leds_s)裡,然後新建一個.S檔案(led.S),在這個檔案裡寫程式。具體的程式怎麼寫、為什麼這麼寫,在這個檔案後面都有註釋。

3.4 編譯、下載、執行看結果

編譯時用我們的工程管理,直接make編譯得到led.bin和210.bin

下載執行可以用usb啟動dnw下載;也可以用sd卡燒錄下載,根據自己的情況用一般都用usb下載,因為方便。usb啟動dnw下載,則執行led.bin檔案

注意:開發板上按下電源鍵之後4顆LED預設都是半亮的,當我們下載程式後其中3顆變的很亮,這說明我們的程式已經運行了。

 

3.5 總結和回顧(軟體控制硬體思想、暫存器意義、原理圖資料手冊的作用)

軟體到底是怎麼控制硬體的?為什麼程式一執行硬體就能跟著動?

軟體程式設計控制硬體的介面就是:暫存器

 

4.問題及解決

問題1 字尾名沒改

應該建立檔名為2.leds_s.S ,而我建立為2.leds_s.S.txt。

問題2 不細心

程式設計的時候把r0寫成了ro 。

問題3 編譯得到led.bin和210.bin,執行哪一個?

usb啟動dnw下載,則執行led.bin檔案;

sd卡燒錄下載,則執行210.bin檔案。

 

5 程式分析

目的:3個LED(GPJ0_3GPJ0_4、GPJ0_5)全亮2.leds_s)

 

 

 

第一步:把0x11111111寫入0xE0200240(GPJ0CON)位置。

原因:  應該將GPJ0_3、GPJ0_4、GPJ0_5這3個引腳設定為輸出模式,其餘5個引腳無所謂,故乾脆把8個引腳(GPJ0CON_0~GPJ0CON_7)全設定為輸出模式。

 

第二步:把0x0寫入0xE0200244(GPJ0DAT)位置。

原因:  實際上只需要將GPJ0DAT的8個bit中的bit3—bit5設定為0,其餘  bit位0是1無所謂,所以乾脆都設定為0,即00000000,即0x0。

 

6.問題提出:如何只點亮中間1顆(兩邊是熄滅的)LED3.leds_s)

  分析:  程式其實就是寫了GPJ0CON和GPJ0DAT這2個暫存器而已,功能更  改也要從這裡下手。GPJ0CON暫存器不需要修改,GPJ0DAT中設定  相應的輸出值即可。

  直接解法:  GPJ0DAT = 0x28    程式碼見<3.led_s>

  目的:只讓中間的LED(GPJ0_4)亮,另外兩個(GPJ0_3、GPJ0_5)熄。

  程式分析:

第一步:把0x11111111寫入0xE0200240(GPJ0CON)位置。把8個引腳全設定 為輸出模式,程式碼不變

 

第二步:把0x28寫入0xE0200244(GPJ0DAT)位置。

原因:  GPJ0DAT的8個bit中的bit4設定為0,bit3和bit5為1,其餘幾  位是0是1無所謂(因為我只需要用bit3,bit4,bit5),那我就將  其餘幾位都設定為0,即00101000,即0x28。

小結: 1.這樣寫可以完成任務。

     2.這樣寫有缺陷。缺陷就是需要人為的去計算這個特定的設定值,而   且看程式碼的也不容易看懂。

解決方案:在寫程式碼時用位運算去讓編譯器幫我們計算這個特定值。

 

7.使用位運算實現功能

7.1常用位運算:位與(&)  位或(|)  位非(取反 ~) 移位(左移<<  右移>>)

7.2 目的:  中間亮兩邊滅

直接解法:  GPJ0DAT = 0x28

位運算:  ldr r0, = ((1<<3) | (1<<5))

分析:  1左移3位為1000,1左移5位為100000,1000和100000進行位或  運算,得到0b00101000,即0x28

0b00101000  // 00101000前面加個0b,代表00101000是二進位制

 

還可以這樣,ldr r0, = ((1<<3) | (0<<4) | (1<<5))

別人一看就知道,哪個亮哪個滅,bit3和bit5滅,bit4亮

 

7.3 擴充套件一下:如何只熄滅中間1顆而點亮旁邊2顆

ldr r0, = ((0<<3) | (1<<4) | (0<<5))

 

8 彙編編寫延時函式並實現LED閃爍效果

8.1閃爍效果原理分析

閃爍 = 亮 + 延時 + 滅 + 延時 + 亮 + 延時 ······

8.2 彙編編寫延時函式

彙編編寫延時函式的原理,用一個暫存器存放一個數字,然後在迴圈中每個迴圈裡給數字減1,然後再判斷這個數字的值是否為0.如果為0則停止迴圈,如果不為0則繼續迴圈。

 

// 延時函式:函式名:delay

delay:

ldr r2, =9000000

ldr r3, =0x0//這兩句是初始化

delay_loop:

sub r2, r2, #1 //r2 = r2 - 1

cmp r2, r3 //cmp會影響Z標誌位,如果r2等於r3則Z=1,    

//下一句中eq就會成立

bne delay_loop //這三句是函式體

mov pc, lr //函式呼叫返回

 

8.3 用匯編寫死迴圈也很簡單

flash:

-延時-滅-延時... //迴圈體

b flash

 

8.4 彙編編寫及呼叫函式的方式

彙編中整個彙編的主程式是一個死迴圈,這個死迴圈是我們彙編程式的主體,類似於C中的main函式。其他函式必須寫在這個主死迴圈程式的後面(死迴圈外),不然會出錯。

彙編編寫delay延時函式時,要注意函式的初始化和函式體的位置,不能把初始化寫在了迴圈體內。

彙編中呼叫函式用bl指令,子函式中最後用mov pc, lr來返回。

 

程式設計操控一個硬體的步驟:

1 分析硬體工作原理

2 分析原理圖

3 分析資料手冊

4 找到相關的SFR

5 寫程式碼設定暫存器得到想要的效果

9.自己做的。板子上有4顆LED的(還有個在GPD0_1),四個燈依次點亮

1.分析硬體工作原理(底板x210bv3)

1.觀察第四個燈D25:PWMTOUT1

所以要查核心板x210cv3,[PWMTOUT1接在了核心板x210cv3,利用好pdf閱讀器中的搜尋功能],發現PWMTOUT1對應的就是GPD0_1

2.分析資料手冊

在S5PV210_UM_REV1.1中查詢GPIO對應的SFR——GPJ0、GPD0

4.找到相關的SFR

然後相關的SFR為GPJ0_CON、GPJ0_DAT、GPD0_CON、GPD0_DAT

5 寫程式碼設定暫存器得到想要的效果  檔名<9.leds_s>和<11.leds_swang>

1.觀察第四個燈D25:PWMTOUT1

所以要查核心板x210cv3,[PWMTOUT1接在了核心板x210cv3,利用好pdf閱讀器中的搜尋功能],發現PWMTOUT1對應的就是GPD0_1

2.分析資料手冊

在S5PV210_UM_REV1.1中查詢GPIO對應的SFR——GPJ0、GPD0

4.找到相關的SFR

然後相關的SFR為GPJ0_CON、GPJ0_DAT、GPD0_CON、GPD0_DAT

5 寫程式碼設定暫存器得到想要的效果  檔名<9.leds_s>和<11.leds_swang>


1.觀察第四個燈D25:PWMTOUT1

所以要查核心板x210cv3,[PWMTOUT1接在了核心板x210cv3,利用好pdf閱讀器中的搜尋功能],發現PWMTOUT1對應的就是GPD0_1

2.分析資料手冊

在S5PV210_UM_REV1.1中查詢GPIO對應的SFR——GPJ0、GPD0

4.找到相關的SFR

然後相關的SFR為GPJ0_CON、GPJ0_DAT、GPD0_CON、GPD0_DAT

5 寫程式碼設定暫存器得到想要的效果  檔名<9.leds_s>和<11.leds_swang>

/*
 *檔名: 9.leds_s
 *作者:
 *描述: 流水燈-四個燈依次點亮
*/


#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
#define GPD0CON 0xE02000A0
#define GPD0DAT 0xE02000A4


.global _start //用.global把_start連結屬性改為外部,這樣其他檔案就可以看見_start了
_start:
//第一步:把0x11111111寫入0xE0200240(GPJ0CON)位置。意思是把8個引腳全設定為輸出模式,程式碼不變。
ldr r0, =0x11111111 //從後面的=可以看出用的是ldr偽指令,因為需要編譯器來判斷這個數
ldr r1, =GPJ0CON //是合法立即數還是非法立即數。一般寫程式碼都用ldr偽指令
str r0, [r1] //暫存器間接定址功能是把r0中的數寫入到r1中的數為地址的記憶體中去

//第二步:把0x1111寫入0xE02100A0(GPD0CON)位置。
ldr r0, =10000 //ldr r0, =(1<<4)
ldr r1, =GPD0CON
str r0, [r1]

flash:
//第1步:第一顆燈亮
//ldr r0, = ((0<<3) | (1<<4) | (1<<5))
ldr r0,= GPD0DAT
ldr r1,= 1111 //ldr r1,= (1<<1)
str r1,[r0]
ldr r0, = ~(1<<3) //1000  位取反   為 0111
ldr r1, =GPJ0DAT
str r0,[r1] //把0寫入到GPJ0DAT暫存器中,引腳即輸出低電平,LED點亮
//然後:延時
bl delay 

//第2步:第二顆燈亮
ldr r0,= GPD0DAT
ldr r1,= 1111
str r1,[r0]
ldr r0, = ~(1<<4)
ldr r1, =GPJ0DAT
str r0,[r1] //把0寫入到GPJ0DAT暫存器中,引腳即輸出低電平,LED點亮
//然後:延時
bl delay 

//第3步:第三顆燈亮
ldr r0,= GPD0DAT
ldr r1,= 1111
str r1,[r0]

ldr r0, = ~(1<<5)
ldr r1, =GPJ0DAT
str r0, [r1] //把0寫入到GPJ0DAT暫存器中,引腳即輸出低電平,LED點亮
//然後:延時
bl delay 

//第4步:第四課燈亮
ldr r0, =1101 //ldr r0, =(0<<1)
ldr r1, =GPD0DAT
str r0, [r1]
ldr r0,= GPJ0DAT
ldr r1,= (1<<3)|(1<<4)|(1<<5)
str r1,[r0]
//然後:延時
bl delay 

b flash

// 延時函式:函式名:delay
delay:
ldr r2, =9000000
ldr r3, =0x0
delay_loop:
sub r2, r2, #1 //r2 = r2 - 1
cmp r2, r3 //cmp會影響Z標誌位,如果r2等於r3則Z=1,下一句中eq就會成立
bne delay_loop
mov pc, lr //函式呼叫返回