步驟1:材料
對于此項目,您將需要
Raspberry pi 3
七個LEDs
七個220歐姆電阻
一個10k歐姆電阻
一個按鈕
您還需要計算機設置程序才能以裸機方式處理raspberry pi 3。查看我以前的指導,以了解如何為使用pi裸機設置環境。
此項目從開始到結束大概需要3-4個小時。
好,我們開始吧!!!!
步驟2:電路
要構建此模塊,我們需要一行6個LED分別連接到GPIO 20-25。我們還需要一個連接到GPIO 27的指示燈。該指示燈將向我們指示是否已按下按鈕。最后,我們需要連接按鈕。按鈕的一側將連接到3.3v,另一側連接到下拉引腳。我們將使用GPIO 17連接到按鈕。 GPIO 17將成為我們的輸入引腳。 GPIO 17連接到一個10k歐姆的電阻,該電阻連接到地(GND)。我們這樣做是為了確保GPIO 17始終設置為低電平。如果不是,則該引腳有可能在高點和低點之間放棄,從而給我們帶來隨機的結果。為了避免這種情況,我們可以使用電阻較大的電阻將引腳下拉至0v。
在面包板上設置電路是一個簡單的過程。請遵循上面的電路圖。將引線的短邊連接到GND,將長邊連接到220歐姆電阻。對其他5個不同的led重復此操作。這樣一來,您總共應該連接六個LED。從一端開始,將第一個LED的正極連接到GPIO 20,然后將下一個引腳連接到GPIO 21,依此類推。..最后一個LED應該連接到GPIO 25。
對于指示燈led連接led的短端連接到GND,長端連接到220 ohm電阻,然后將電阻連接到GPIO 17。
將按鈕連接到試驗板。我使用的按鈕上有四個連接。我們將只使用底部的兩個。將一端連接到正極軌,另一端連接到10k歐姆電阻。將GPIO 17連接到10k電阻。按下按鈕時,它將GPIO 17連接至將引腳設置為高電平的正極。
將3.3v引腳連接至正極,并將GND引腳連接至負極。
最后將正軌和負軌連接在一起,以便可以使用兩側。
步驟3:代碼:簡介
編碼部分是從上一個閃爍的項目中已經學到的東西建立的。
該項目中的兩個新事物是,我們設置了堆棧框架的使用方式,以便我們可以模擬高級功能,并進行設置使用系統時鐘等待特定毫秒數的等待函數。
堆棧是一種常見的編程結構。它實際上是一個地址序列,可用于臨時存儲內容。堆棧具有兩個基本功能
您可以將項目推入堆棧頂部
,也可以將項目從堆棧頂部彈出
可以按不同的版本設置堆棧,但是對于該項目,我們將堅持默認配置。當您將某些東西推入堆棧時,它會放在頂部。當下一件物品被推入堆棧時,新物品將成為頂部,而舊物品將位于其下方。希望您以前曾經使用過堆棧,如果沒有的話,可以在網上找到更多更深入的解釋。幸運的是,Arm有一個push和pop指令,因此使用堆棧很容易組裝。
我們將要探索的第二件事是訪問系統計時器,這樣我們可以將程序延遲一定的時間。
第4步:代碼:堆棧/堆棧框架
設置要使用的堆棧就像一行代碼一樣簡單。由于鏈接器的設置方式(kernel.ld),我們在編譯時將所有代碼插入0x8000之后。因此,我們需要將此值移到sp寄存器中。
mov sp,#0x8000
將其添加到代碼頂部并使用堆棧應該沒問題。
現在進入堆棧框架。堆棧框架基本上是當我們預留堆棧的一部分以用于過程調用時。這使我們能夠實現高級語言(如Java和C ++)使用的功能。這些語言使用堆棧來跟蹤函數調用。我們可以在匯編中做同樣的事情。
堆棧框架:
要將函數調用模擬為高級語言,我們將保留寄存器我們想通過將它們放置在堆棧上來使用,然后如果需要將其用于變量,我們還可以在堆棧中預留本地內存。在函數末尾,我們必須破壞堆棧幀并將堆棧重置為以前的狀態。
通常,如果函數需要返回任何內容,則應將其放在r0中。/p》
如果函數接受參數,則應將其放在函數分支的前面。
堆棧框架:SETUP
我們通過設置堆棧框架開始該功能。
push {r7,lr}
mov r7,sp
push {}
ldr,[r7,#8] @第一個參數與r7的偏移量為#8
然后我們通過清理堆棧框架來結束堆棧框架
pop
pop {r7,lr}
。此過程使我們無需更改任何寄存器即可調用函數。它還允許在函數內調用函數,因為堆棧框架將始終保留lr,因此允許進行多個級別的函數調用。
=======================================
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:代碼:系統計時器
要使用系統計時器設置延遲,我將其編寫在一個單獨的文件中,因此也可以在以后的項目中使用。為此,我們基本上需要訪問計時器并獲取初始時間戳。一旦有了,我們將再次訪問時間戳。我們將從最初的時間戳中減去第二個時間戳,并將其與所需的值進行比較。
算法非常簡單,最困難的部分是訪問正確的寄存器。
計時器的基地址為0x3f003000
計時器的lo字的偏移量為0x4
計時器的高位字的偏移量為0x8
嘗試編寫自己的等待函數!如果需要參考,請附上我的代碼。
步驟6:代碼:獲取輸入
對于該項目,我使用了GPLEV0寄存器。本質上,它保持引腳0-31的狀態。我們正在使用GPIO 17作為輸入。要將此引腳設置為輸入,我們必須訪問FSEL1寄存器并清除GPIO 17專用的三位。
GPLEV0偏移量0x34
FSEL1偏移量0x04
通過掩碼將GPIO 17設置為輸入0xFF1FFFFF
我們還需要將位20-27設置為輸出。
FSEL2偏移量0x08
將掩碼設置為20到27以輸出0x249249
由于我們使用的是輸出,我們還需要訪問GPSET0寄存器以打開引腳,而GPCLR0寄存器以關閉引腳
GPSET0偏移量0x1c
GPCLR0偏移量0x28
由于等待功能以微秒為單位,因此您可以使用以下幾個值
半秒0x7a120 = 500,000微秒= 1/2秒
四分之一秒0x3d090 = 250,000微秒= 1/4秒
秒的八分之一0x1e848 = 125,000微秒= 1/8秒
好,讓我們開始編碼!
mov r0, @return
上面的代碼設置了我們的代碼,因此它可以正常運行。
Then we need to undo the stack frame
下一步我設置基址的地址和我們將要使用的偏移量。
Get rid of local memory created
在這里,我設置了一些以后將要使用的有用符號。
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
=======================================
出于可讀性考慮,我為寄存器的目的設置了一些符號。
Replace saved registers
要將此引腳設置為輸入,首先要獲得適當的偏移,然后再加載掩碼;最后,我將掩碼寫回到寄存器中。
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
接下來,我開始主程序循環。我首先描述我要程序執行的操作。
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
=======================================
我以一個小的等待值開始循環。
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的狀態,該函數將返回r0中指定引腳的狀態。我的所有功能都將在末尾列出。
Notice that nothing is overwritten in the stack. We simply move the stack pointer back to it‘s original place.
我檢查返回值,然后跳轉到一個功能,該功能可以打開指示燈并循環或關閉指示燈
如果按下該按鈕,它將GPIO17連接到3.3v,因此將其設置為高電平。因此,如果按下按鈕,輸入功能將返回1,從而指示器打開并且LED的環路上升,從GPIO20到GPIO 26接通。
基本上就是這樣。接下來,我將介紹在input_loop中調用的函數。
步驟7:代碼:函數定義
首先,我們有get輸入
b main
.section .text
main:
mov sp,#0x8000
我的函數有一個參數,即GPIO引腳的編號。該功能使用引腳號創建一個掩碼來測試GPLEV0中的位。 tst執行邏輯“與”并設置標志。如果and返回true,則未設置零標志。
r1:0000_1000
r2:0000_1000
tst r2,r1
結果:未設置零標志
結果返回到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}
接下來,我有兩個函數可以打開指示器并關閉指示燈。當指示燈打開時,該功能將分支并通過GPIO引腳循環。當指示燈熄滅時,該功能將通過GPIO引腳分支和向下循環。
.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
前兩個功能設置向上或向下循環功能。他們只是確保在循環開始之前將計數器設置為正確的數字。 loop_up和loop_down函數在本質上彼此相同。一個循環通過以GPIO 20開頭并以GPIO 26結尾的引腳,然后循環通過以GPIO 26開頭并以GPIO 20結尾的引腳。
循環位置i或j移入r0和然后調用turn_on函數。這會根據傳遞到r0的數字打開一個引腳。然后,循環將等待八分之一秒,然后再關閉引腳并遞增或遞減計數器。
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函數。
步驟8:將它們放在一起。
現在,您已經編寫了所有代碼文件,您需要生成一個二進制kernel.img。我設置了一個簡單的makefile,將其吐出來。只需下載它,然后將第4行的代碼變量更改為文件名即可。
如果您無法使代碼正常工作,請下載并編譯我的代碼,然后將kernel.img放到pi上。如果可以,那么如果不返回電路步驟并嘗試重建電路,則代碼可能存在問題。
-
led
+關注
關注
242文章
23843瀏覽量
674012 -
樹莓派
+關注
關注
121文章
2007瀏覽量
107456
發布評論請先 登錄
5050 RGBW 四合一全彩 LED 燈珠
目前市場上主流的LLC+PFC組合IC(二合一控制器)匯總
二合一與三合一信號浪涌保護器的技術解析及行業應用

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

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

硅基CCD:基礎知識

評論