步驟1:材料
對于此項目,您將需要
Raspberry pi 3
七個LEDs
七個220歐姆電阻
一個10k歐姆電阻
一個按鈕
您還需要計算機設(shè)置程序才能以裸機方式處理raspberry pi 3。查看我以前的指導(dǎo),以了解如何為使用pi裸機設(shè)置環(huán)境。
此項目從開始到結(jié)束大概需要3-4個小時。
好,我們開始吧?。。?!
步驟2:電路

要構(gòu)建此模塊,我們需要一行6個LED分別連接到GPIO 20-25。我們還需要一個連接到GPIO 27的指示燈。該指示燈將向我們指示是否已按下按鈕。最后,我們需要連接按鈕。按鈕的一側(cè)將連接到3.3v,另一側(cè)連接到下拉引腳。我們將使用GPIO 17連接到按鈕。 GPIO 17將成為我們的輸入引腳。 GPIO 17連接到一個10k歐姆的電阻,該電阻連接到地(GND)。我們這樣做是為了確保GPIO 17始終設(shè)置為低電平。如果不是,則該引腳有可能在高點和低點之間放棄,從而給我們帶來隨機的結(jié)果。為了避免這種情況,我們可以使用電阻較大的電阻將引腳下拉至0v。
在面包板上設(shè)置電路是一個簡單的過程。請遵循上面的電路圖。將引線的短邊連接到GND,將長邊連接到220歐姆電阻。對其他5個不同的led重復(fù)此操作。這樣一來,您總共應(yīng)該連接六個LED。從一端開始,將第一個LED的正極連接到GPIO 20,然后將下一個引腳連接到GPIO 21,依此類推。..最后一個LED應(yīng)該連接到GPIO 25。
對于指示燈led連接led的短端連接到GND,長端連接到220 ohm電阻,然后將電阻連接到GPIO 17。
將按鈕連接到試驗板。我使用的按鈕上有四個連接。我們將只使用底部的兩個。將一端連接到正極軌,另一端連接到10k歐姆電阻。將GPIO 17連接到10k電阻。按下按鈕時,它將GPIO 17連接至將引腳設(shè)置為高電平的正極。
將3.3v引腳連接至正極,并將GND引腳連接至負極。
最后將正軌和負軌連接在一起,以便可以使用兩側(cè)。
步驟3:代碼:簡介

編碼部分是從上一個閃爍的項目中已經(jīng)學(xué)到的東西建立的。
該項目中的兩個新事物是,我們設(shè)置了堆??蚣艿氖褂梅绞?,以便我們可以模擬高級功能,并進行設(shè)置使用系統(tǒng)時鐘等待特定毫秒數(shù)的等待函數(shù)。
堆棧是一種常見的編程結(jié)構(gòu)。它實際上是一個地址序列,可用于臨時存儲內(nèi)容。堆棧具有兩個基本功能
您可以將項目推入堆棧頂部
,也可以將項目從堆棧頂部彈出
可以按不同的版本設(shè)置堆棧,但是對于該項目,我們將堅持默認配置。當(dāng)您將某些東西推入堆棧時,它會放在頂部。當(dāng)下一件物品被推入堆棧時,新物品將成為頂部,而舊物品將位于其下方。希望您以前曾經(jīng)使用過堆棧,如果沒有的話,可以在網(wǎng)上找到更多更深入的解釋。幸運的是,Arm有一個push和pop指令,因此使用堆棧很容易組裝。
我們將要探索的第二件事是訪問系統(tǒng)計時器,這樣我們可以將程序延遲一定的時間。
第4步:代碼:堆棧/堆??蚣?/p>

設(shè)置要使用的堆棧就像一行代碼一樣簡單。由于鏈接器的設(shè)置方式(kernel.ld),我們在編譯時將所有代碼插入0x8000之后。因此,我們需要將此值移到sp寄存器中。
mov sp,#0x8000
將其添加到代碼頂部并使用堆棧應(yīng)該沒問題。
現(xiàn)在進入堆??蚣?。堆棧框架基本上是當(dāng)我們預(yù)留堆棧的一部分以用于過程調(diào)用時。這使我們能夠?qū)崿F(xiàn)高級語言(如Java和C ++)使用的功能。這些語言使用堆棧來跟蹤函數(shù)調(diào)用。我們可以在匯編中做同樣的事情。
堆??蚣埽?/p>
要將函數(shù)調(diào)用模擬為高級語言,我們將保留寄存器我們想通過將它們放置在堆棧上來使用,然后如果需要將其用于變量,我們還可以在堆棧中預(yù)留本地內(nèi)存。在函數(shù)末尾,我們必須破壞堆棧幀并將堆棧重置為以前的狀態(tài)。
通常,如果函數(shù)需要返回任何內(nèi)容,則應(yīng)將其放在r0中。/p》
如果函數(shù)接受參數(shù),則應(yīng)將其放在函數(shù)分支的前面。
堆??蚣埽篠ETUP
我們通過設(shè)置堆??蚣荛_始該功能。
push {r7,lr}
mov r7,sp
push {}
ldr,[r7,#8] @第一個參數(shù)與r7的偏移量為#8
然后我們通過清理堆??蚣軄斫Y(jié)束堆??蚣?/p>
pop
pop {r7,lr}
。此過程使我們無需更改任何寄存器即可調(diào)用函數(shù)。它還允許在函數(shù)內(nèi)調(diào)用函數(shù),因為堆??蚣軐⑹冀K保留lr,因此允許進行多個級別的函數(shù)調(diào)用。
=======================================
stack frame visual
=======================================
0x0 | 《=sp
---------------------------------------
0x4 |
---------------------------------------
0x8 |
---------------------------------------
0x12|
---------------------------------------
0x16|
---------------------------------------
0x20|
---------------------------------------
0x24|
---------------------------------------
0x28|
======================================= Example Function with one argument.
_______________________________________ push {r1} @argument r1=0x1234
b ex_func =======================================
stack frame visual
=======================================
0x0 | 0x1234 《=sp The argument is pushed onto the stack
---------------------------------------
0x4 |
---------------------------------------
0x8 |
---------------------------------------
0x12|
---------------------------------------
0x16|
---------------------------------------
0x20|
---------------------------------------
0x24|
---------------------------------------
0x28|
======================================= ex_func:
push {r7,lr}
mov r7,sp @r7 will equal 0x8
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7 《=sp
---------------------------------------
0x12|
---------------------------------------
0x16|
---------------------------------------
0x20|
---------------------------------------
0x24|
---------------------------------------
0x28|
======================================= push {r1-r3} @using r1,r2,and r3 so we preserve them
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7
---------------------------------------
0x12| r3 saved register
---------------------------------------
0x16| r2 saved register
---------------------------------------
0x20| r1 《=sp saved register
---------------------------------------
0x24|
---------------------------------------
0x28|
=======================================
pop r1,[r7,#8] @remember r7=0x8, the old sp. To access the argument at 0x0 we need to go up by 8
@thats why we do [r7,#8] which is the same as putting the value at 0x0 (0x1234) into r1 sub sp,sp,#8 @moves sp down to create local memory
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7
---------------------------------------
0x12| r3 saved register
---------------------------------------
0x16| r2 saved register
---------------------------------------
0x20| r1 saved register
---------------------------------------
0x24| empty
---------------------------------------
0x28| empty 《=sp
======================================= To end the function we need to move the value to be returned if any into ro
步驟5:代碼:系統(tǒng)計時器

要使用系統(tǒng)計時器設(shè)置延遲,我將其編寫在一個單獨的文件中,因此也可以在以后的項目中使用。為此,我們基本上需要訪問計時器并獲取初始時間戳。一旦有了,我們將再次訪問時間戳。我們將從最初的時間戳中減去第二個時間戳,并將其與所需的值進行比較。
算法非常簡單,最困難的部分是訪問正確的寄存器。
計時器的基地址為0x3f003000
計時器的lo字的偏移量為0x4
計時器的高位字的偏移量為0x8
嘗試編寫自己的等待函數(shù)!如果需要參考,請附上我的代碼。
步驟6:代碼:獲取輸入
對于該項目,我使用了GPLEV0寄存器。本質(zhì)上,它保持引腳0-31的狀態(tài)。我們正在使用GPIO 17作為輸入。要將此引腳設(shè)置為輸入,我們必須訪問FSEL1寄存器并清除GPIO 17專用的三位。
GPLEV0偏移量0x34
FSEL1偏移量0x04
通過掩碼將GPIO 17設(shè)置為輸入0xFF1FFFFF
我們還需要將位20-27設(shè)置為輸出。
FSEL2偏移量0x08
將掩碼設(shè)置為20到27以輸出0x249249
由于我們使用的是輸出,我們還需要訪問GPSET0寄存器以打開引腳,而GPCLR0寄存器以關(guān)閉引腳
GPSET0偏移量0x1c
GPCLR0偏移量0x28
由于等待功能以微秒為單位,因此您可以使用以下幾個值
半秒0x7a120 = 500,000微秒= 1/2秒
四分之一秒0x3d090 = 250,000微秒= 1/4秒
秒的八分之一0x1e848 = 125,000微秒= 1/8秒
好,讓我們開始編碼!
mov r0, @return
上面的代碼設(shè)置了我們的代碼,因此它可以正常運行。
Then we need to undo the stack frame
下一步我設(shè)置基址的地址和我們將要使用的偏移量。
Get rid of local memory created
在這里,我設(shè)置了一些以后將要使用的有用符號。
add sp,sp,#8
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7
---------------------------------------
0x12| r3 saved register
---------------------------------------
0x16| r2 saved register
---------------------------------------
0x20| r1 《=sp saved register
---------------------------------------
0x24| empty
---------------------------------------
0x28| empty
=======================================
出于可讀性考慮,我為寄存器的目的設(shè)置了一些符號。
Replace saved registers
要將此引腳設(shè)置為輸入,首先要獲得適當(dāng)?shù)钠?,然后再加載掩碼;最后,我將掩碼寫回到寄存器中。
pop {r1-r3}
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7 《=sp
---------------------------------------
0x12| r3
---------------------------------------
0x16| r2
---------------------------------------
0x20| r1
---------------------------------------
0x24| empty
---------------------------------------
0x28| empty
=======================================
我做的和輸入一樣,只是使用了不同的偏移量和掩碼。
Replace r7 and link register
接下來,我開始主程序循環(huán)。我首先描述我要程序執(zhí)行的操作。
pop {r7,lr}
=======================================
stack frame visual
=======================================
0x0 | 0x1234 The argument is pushed onto the stack
---------------------------------------
0x4 | lr 《=sp
---------------------------------------
0x8 | r7
---------------------------------------
0x12| r3
---------------------------------------
0x16| r2
---------------------------------------
0x20| r1
---------------------------------------
0x24| empty
---------------------------------------
0x28| empty
=======================================
我以一個小的等待值開始循環(huán)。
Finally, we need to clean up after the argument
add sp,sp,#4
=======================================
stack frame visual
=======================================
0x0 | 0x1234 《=sp
---------------------------------------
0x4 | lr
---------------------------------------
0x8 | r7
---------------------------------------
0x12| r3
---------------------------------------
0x16| r2
---------------------------------------
0x20| r1
---------------------------------------
0x24| empty
---------------------------------------
0x28| empty
=======================================
mov pc,lr Return
在這里,我正在檢查引腳17的狀態(tài),該函數(shù)將返回r0中指定引腳的狀態(tài)。我的所有功能都將在末尾列出。
Notice that nothing is overwritten in the stack. We simply move the stack pointer back to it‘s original place.
我檢查返回值,然后跳轉(zhuǎn)到一個功能,該功能可以打開指示燈并循環(huán)或關(guān)閉指示燈
如果按下該按鈕,它將GPIO17連接到3.3v,因此將其設(shè)置為高電平。因此,如果按下按鈕,輸入功能將返回1,從而指示器打開并且LED的環(huán)路上升,從GPIO20到GPIO 26接通。
基本上就是這樣。接下來,我將介紹在input_loop中調(diào)用的函數(shù)。
步驟7:代碼:函數(shù)定義
首先,我們有g(shù)et輸入
b main
.section .text
main:
mov sp,#0x8000
我的函數(shù)有一個參數(shù),即GPIO引腳的編號。該功能使用引腳號創(chuàng)建一個掩碼來測試GPLEV0中的位。 tst執(zhí)行邏輯“與”并設(shè)置標志。如果and返回true,則未設(shè)置零標志。
r1:0000_1000
r2:0000_1000
tst r2,r1
結(jié)果:未設(shè)置零標志
結(jié)果返回到r0。
.equ BASE_ADDR,0x3f200000 @Base address
.equ GPFSEL0, 0x0
.equ GPFSEL1, 0x04 @FSEL1 register offset | use to select GPIO 10-19 and set input/output/alt func
.equ GPFSEL2, 0x08 @FSEL2 register offset | use to select GPIO 20-29 and set input/output/alt func
.equ GPSET0, 0x1c @GPSET0 register offset| use to set GPIO’s logic level high(3.3v)
.equ GPCLR0, 0x28 @GPCLR0 register offset| use to set GPIO‘s logic level low(0v)
.equ GPLEV0, 0x34 @GPIO level offset | use to read current level of pin(on/off)[high/low]{3.3v/0v}
接下來,我有兩個函數(shù)可以打開指示器并關(guān)閉指示燈。當(dāng)指示燈打開時,該功能將分支并通過GPIO引腳循環(huán)。當(dāng)指示燈熄滅時,該功能將通過GPIO引腳分支和向下循環(huán)。
.equ CLEAR_BITS21_23,0xFF1FFFFF @mask to clear bits 21 through 23 | use to set GPIO 17 to input
.equ SET_20_27,0x249249 @mask to set bits 20 through 27 | use to set GPIO 20-27 to output
.equ SET_BIT27,0x8000000 @mask to set bits 27 | use to set GPIO 27 to high(3.3v) or low(0v) GPIO 27 is indicator light
.equ half_second, 0x7a120 @hex value for half a second in microseconds
.equ quarter_second, 0x3d090 @hex value for quarter of a second in microseconds
.equ eighth_second,0x1e848 @hex value for eigth of a second in microseconds
前兩個功能設(shè)置向上或向下循環(huán)功能。他們只是確保在循環(huán)開始之前將計數(shù)器設(shè)置為正確的數(shù)字。 loop_up和loop_down函數(shù)在本質(zhì)上彼此相同。一個循環(huán)通過以GPIO 20開頭并以GPIO 26結(jié)尾的引腳,然后循環(huán)通過以GPIO 26開頭并以GPIO 20結(jié)尾的引腳。
循環(huán)位置i或j移入r0和然后調(diào)用turn_on函數(shù)。這會根據(jù)傳遞到r0的數(shù)字打開一個引腳。然后,循環(huán)將等待八分之一秒,然后再關(guān)閉引腳并遞增或遞減計數(shù)器。
base .req r1 @Sets symbol base to refer to r1: can use base and r1 interchangeably base《=》r1
ldr base,=BASE_ADDR @base = 0x3f20000, load base with the base address of peripheralsoffset .req r2 @Sets symbol offset to refer to r2: can use offset and r2 interchangeably offset《=》r2
mask .req r3 @Sets symbol mask to refer to r3 mask《=》r3
i .req r4 @Sets symbol i to refer to r4 i《=》r4
j .req r5 @Sets symbol j to refer to r5 j《=》r5
return .req r0 @return 《=》 r0
turn_on和turn_of函數(shù)。
步驟8:將它們放在一起。
現(xiàn)在,您已經(jīng)編寫了所有代碼文件,您需要生成一個二進制kernel.img。我設(shè)置了一個簡單的makefile,將其吐出來。只需下載它,然后將第4行的代碼變量更改為文件名即可。
如果您無法使代碼正常工作,請下載并編譯我的代碼,然后將kernel.img放到pi上。如果可以,那么如果不返回電路步驟并嘗試重建電路,則代碼可能存在問題。
-
led
+關(guān)注
關(guān)注
244文章
24654瀏覽量
691834 -
樹莓派
+關(guān)注
關(guān)注
122文章
2080瀏覽量
110626
發(fā)布評論請先 登錄
交換機上的SFP口不只是“插光纖”:從數(shù)據(jù)中心到工業(yè)現(xiàn)場的實戰(zhàn)解析
運行 debian 69 紅色電源 LED 旁邊的綠色閃爍 LED 是什么意思?
探索MAX25603:汽車組合大燈的理想LED控制器
FP8013在便攜設(shè)備LED驅(qū)動中的應(yīng)用設(shè)計:攻克低壓差條件下的亮度衰減與閃爍問題
如何組合一排LED并使它們閃爍序列
評論