步驟1:材料
對于此項目,您將需要
Raspberry pi 3
七個LEDs
七個220歐姆電阻
一個10k歐姆電阻
一個按鈕
您還需要計算機(jī)設(shè)置程序才能以裸機(jī)方式處理raspberry pi 3。查看我以前的指導(dǎo),以了解如何為使用pi裸機(jī)設(shè)置環(huán)境。
此項目從開始到結(jié)束大概需要3-4個小時。
好,我們開始吧?。。。?/p>
步驟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è)置為低電平。如果不是,則該引腳有可能在高點(diǎn)和低點(diǎn)之間放棄,從而給我們帶來隨機(jī)的結(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。
將按鈕連接到試驗(yàn)板。我使用的按鈕上有四個連接。我們將只使用底部的兩個。將一端連接到正極軌,另一端連接到10k歐姆電阻。將GPIO 17連接到10k電阻。按下按鈕時,它將GPIO 17連接至將引腳設(shè)置為高電平的正極。
將3.3v引腳連接至正極,并將GND引腳連接至負(fù)極。
最后將正軌和負(fù)軌連接在一起,以便可以使用兩側(cè)。
步驟3:代碼:簡介
編碼部分是從上一個閃爍的項目中已經(jīng)學(xué)到的東西建立的。
該項目中的兩個新事物是,我們設(shè)置了堆??蚣艿氖褂梅绞?,以便我們可以模擬高級功能,并進(jìn)行設(shè)置使用系統(tǒng)時鐘等待特定毫秒數(shù)的等待函數(shù)。
堆棧是一種常見的編程結(jié)構(gòu)。它實(shí)際上是一個地址序列,可用于臨時存儲內(nèi)容。堆棧具有兩個基本功能
您可以將項目推入堆棧頂部
,也可以將項目從堆棧頂部彈出
可以按不同的版本設(shè)置堆棧,但是對于該項目,我們將堅持默認(rèn)配置。當(dāng)您將某些東西推入堆棧時,它會放在頂部。當(dāng)下一件物品被推入堆棧時,新物品將成為頂部,而舊物品將位于其下方。希望您以前曾經(jīng)使用過堆棧,如果沒有的話,可以在網(wǎng)上找到更多更深入的解釋。幸運(yùn)的是,Arm有一個push和pop指令,因此使用堆棧很容易組裝。
我們將要探索的第二件事是訪問系統(tǒng)計時器,這樣我們可以將程序延遲一定的時間。
第4步:代碼:堆棧/堆??蚣?/p>
設(shè)置要使用的堆棧就像一行代碼一樣簡單。由于鏈接器的設(shè)置方式(kernel.ld),我們在編譯時將所有代碼插入0x8000之后。因此,我們需要將此值移到sp寄存器中。
mov sp,#0x8000
將其添加到代碼頂部并使用堆棧應(yīng)該沒問題。
現(xiàn)在進(jì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
然后我們通過清理堆棧框架來結(jié)束堆??蚣?/p>
pop
pop {r7,lr}
。此過程使我們無需更改任何寄存器即可調(diào)用函數(shù)。它還允許在函數(shù)內(nèi)調(diào)用函數(shù),因?yàn)槎褩?蚣軐⑹冀K保留lr,因此允許進(jìn)行多個級別的函數(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è)置延遲,我將其編寫在一個單獨(dú)的文件中,因此也可以在以后的項目中使用。為此,我們基本上需要訪問計時器并獲取初始時間戳。一旦有了,我們將再次訪問時間戳。我們將從最初的時間戳中減去第二個時間戳,并將其與所需的值進(jìn)行比較。
算法非常簡單,最困難的部分是訪問正確的寄存器。
計時器的基地址為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è)置了我們的代碼,因此它可以正常運(yùn)行。
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è)置標(biāo)志。如果and返回true,則未設(shè)置零標(biāo)志。
r1:0000_1000
r2:0000_1000
tst r2,r1
結(jié)果:未設(shè)置零標(biāo)志
結(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)編寫了所有代碼文件,您需要生成一個二進(jìn)制kernel.img。我設(shè)置了一個簡單的makefile,將其吐出來。只需下載它,然后將第4行的代碼變量更改為文件名即可。
如果您無法使代碼正常工作,請下載并編譯我的代碼,然后將kernel.img放到pi上。如果可以,那么如果不返回電路步驟并嘗試重建電路,則代碼可能存在問題。
-
led
+關(guān)注
關(guān)注
242文章
23848瀏覽量
674276 -
樹莓派
+關(guān)注
關(guān)注
121文章
2012瀏覽量
107495
發(fā)布評論請先 登錄
5050 RGBW 四合一全彩 LED 燈珠
目前市場上主流的LLC+PFC組合IC(二合一控制器)匯總
二合一與三合一信號浪涌保護(hù)器的技術(shù)解析及行業(yè)應(yīng)用

自制DLP4710LC板子在display模式下投圖能觀察到LED閃爍的原因?
AFE4990接上電源可以亮一會,打開測試軟件LED開始不穩(wěn)定閃爍,怎么解決?
超融合一體機(jī)屬于什么設(shè)備
深妙科技 室內(nèi)外LED電源、LED屏多媒體播放器和二合一視頻處理器維修方法

LSTM神經(jīng)網(wǎng)絡(luò)在時間序列預(yù)測中的應(yīng)用
隆基Hi-MO X6防積灰組件燃動低碳造車夢
負(fù)載管的閃爍噪聲和熱噪聲的區(qū)別
繼電器組合及組合架的作用
一文全了解麥克風(fēng)陣列

硅基CCD:基礎(chǔ)知識

評論