一把兔、GPIO簡(jiǎn)介
i.MX6ULL 芯片的 GPIO 被分成 5 組,并且每組 GPIO 的數(shù)量不盡相同,例如 GPIO1 擁有 32 個(gè)引腳声畏, GPIO2 擁有 22 個(gè)引腳喝检, 其他 GPIO 分組的數(shù)量以及每個(gè) GPIO 的功能請(qǐng)參考 《i.MX 6UltraLite Applications Processor Reference Manual》 第26章General Purpose Input/Output (GPIO)(P1133)俏蛮。
通過(guò) GPIO 硬件結(jié)構(gòu)框圖,就可以從整體上深入了解 GPIO 外設(shè)及它的各種應(yīng)用模式麦撵。
1.1 IO命名
打開(kāi) i.MX6ULL 參考手冊(cè)的第 32 章“Chapter 32: IOMUX Controller(IOMUXC)”
i.MX6ULL 的 IO 分為兩類:SNVS 域的和通用的刽肠,這兩類 IO 本質(zhì)上都是一樣的。
“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”的就是 GPIO 命名免胃,命名形式就是“IOMUXC_SW_MUC_CTL_PAD_XX_XX
”音五,后面的“XX_XX
”就是 GPIO 命名,比如:GPIO1_IO01羔沙、UART1_TX_DATA躺涝、JTAG_MOD 等等。他是 根據(jù)某個(gè) IO 所擁有的功能來(lái)命名的扼雏。比如我們一看到 GPIO1_IO01 就知道這個(gè)肯定能做 GPIO坚嗜,看到 UART1_TX_DATA 肯定就知道這個(gè) IO 肯定能做為 UART1 的發(fā)送引腳夯膀。
IO 復(fù)用功能。 i.MX6ULL 除了 GPIO1_IO00~GPIO1_IO09 引腳外苍蔬,其它 IO 也是可以復(fù)用為 GPIO 功能诱建。同樣的,GPIO1_IO00~GPIO_IO09 也是可以復(fù)用為其它外設(shè)引腳碟绑。
1.2 IO復(fù)用
IOMUX 譯為 IO 復(fù)用選擇器俺猿。i.MX6ULL 的芯片每個(gè) GPIO 都通過(guò) IOMUX 支持多種功能, 例如一個(gè) IO 可用于網(wǎng)絡(luò)外設(shè) ENET 的數(shù)據(jù)接收引腳蜈敢,也可以被配置成 PWM 外設(shè)的輸出引腳辜荠, 這樣的設(shè)計(jì)大大增加了芯片的適用性,這樣可選的功能就是由 IOMUX 實(shí)現(xiàn)的抓狭。IOMUX 相當(dāng)于增加了多根內(nèi)部信號(hào)線與 IO 引腳相連伯病,
最多有 8 根,也就是說(shuō)一個(gè) IO 最多可支持 8 種可選的功能
否过。
以“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”這個(gè) IO 為例午笛,打開(kāi)參考手冊(cè)的 1568 頁(yè)。
可以看到有個(gè)名為:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器苗桂,寄存器地址為 0X020E005C药磺,這個(gè)寄存器是 32 位的,但是只用到了最低 5 位煤伟,其中 bit0~bit3(MUX_MODE) 就是設(shè)置 GPIO1_IO00 的復(fù)用功能的癌佩。GPIO1_IO00 一共可以復(fù)用為 9 種功能 IO,分別對(duì)應(yīng) ALT0~ALT8便锨,其中 ALT5 就是作為 GPIO1_IO00围辙。GPIO1_IO00 還可以作為 I2C2_SCL、GPT1_CAPTURE1放案、ANATOP_OTG1_ID 等姚建。
1.3 IO配置
IOMUX 由其左側(cè)的 IOMUXC 控制(C表示Controler),IOMUXC 提供寄存器給用戶進(jìn)行配置吱殉, 它又分成 MUX Mode(IO模式控制) 以及 Pad Settings(Pad配置) 兩個(gè)部分:
在 IOMUXC 外設(shè)中關(guān)于 MUX Mode 和 Pad Settings 寄存器命名格式如下:
IOMUXC控制類型 | 寄存器名稱 |
---|---|
MUX Mode | IOMUXC_SW_MUX_CTL_PAD_XXXX |
Pad Settings | IOMUXC_SW_PAD_CTL_PAD_XXXX |
每個(gè)引腳都包含這兩個(gè)寄存器掸冤,表中的XXXX表示引腳的名字
1.3.1 MUX Mode配置
MUX Mode 就是用來(lái)
配置引腳的復(fù)用功能
,即選擇引腳具體是用于網(wǎng)絡(luò)外設(shè) ENET 的數(shù)據(jù)接收友雳, 還是用于 PWM 外設(shè)的輸出引腳稿湿,當(dāng)然,也可以配置成普通的 IO 口押赊,僅用于控制輸出高低電平饺藤。
以 GPIO1_IO04 引腳為例對(duì) MUX 寄存器進(jìn)行說(shuō)明,該引腳相應(yīng)的 MUX 寄存器在參考手冊(cè)中的描述如下:
該寄存器主要有兩個(gè)配置域,分別是 SION 和 MUX_MODE策精。
- SION: 用于設(shè)置引腳在輸出模式下同時(shí)開(kāi)啟輸入通道舰始。
-
MUX_MODE: 使用 4 個(gè)寄存器位表示可選的 ALT0~ALT7 這 8 個(gè)模式。
- 如 ALT2 模式就是用于 USB 外設(shè)的 USB_OTG1_PWR 信號(hào)咽袜;
- 若配置為 ALT5 則引腳會(huì)用作普通的 GPIO 功能丸卷, 用于輸出高、低電平询刹。
1.3.2 Pad Settings配置
Pad Settings 用于
配置引腳的屬性
谜嫉,例如驅(qū)動(dòng)能力,是否使用上下拉電阻凹联, 是否使用保持器沐兰,是否使用開(kāi)漏模式以及使用施密特模式還是CMOS模式等。
以 GPIO1_IO04 引腳中 PAD 寄存器在參考手冊(cè)中的描述如下:
相對(duì)來(lái)說(shuō) PAD 寄存器的配置項(xiàng)就更豐富了蔽挠,而且圖中僅是該寄存器的部分說(shuō)明住闯,如 HYS 設(shè)置使用施密特模式的滯后功能,PUS 配置上下拉電阻的阻值澳淑, 其它的還包含PUE比原、PKE、ODE杠巡、SPEED量窘、DSE 及 SRE 的配置。
1.3.3 PAD(可跳過(guò)不看)
PAD 代表了一個(gè) i.MX6ULL 的 GPIO 引腳氢拥。在它的左側(cè)是一系列信號(hào)通道及控制線蚌铜,如 input_on 控制輸入開(kāi)關(guān),Dir 控制引腳的輸入輸出方向嫩海,Data_out 控制引腳輸出高低電平冬殃,Data_in 作為信號(hào)輸入,這些信號(hào)都經(jīng)過(guò)一個(gè) IOMUX 的器件連接到左側(cè)的寄存器出革。
①PAD引腳
代表一個(gè)i.MX6ULL的引腳造壮。
②輸出緩沖區(qū)
當(dāng)輸出緩沖區(qū)使能時(shí)渡讼,引腳被配置為輸出模式骂束。在輸出緩沖區(qū)中,又包含了如下的屬性配置:
-
DSE驅(qū)動(dòng)能力
當(dāng)IO用作輸出的時(shí)候用來(lái)設(shè)置IO的驅(qū)動(dòng)能力成箫。
DSE可以調(diào)整芯片內(nèi)部與引腳串聯(lián)電阻R0的大小展箱,從而改變引腳的驅(qū)動(dòng)能力。例如蹬昌,R0的初始值為260歐姆混驰,在3.3V電壓下其電流驅(qū)動(dòng)能力為12.69mA,通過(guò)DSE可以把R0的值配置為原值的1/2、1/3…1/7等栖榨。位設(shè)置 速度 000 輸出驅(qū)動(dòng)關(guān)閉 001 R0(3.3V 下 R0 是 260Ω昆汹,1.8V 下 R0 是 150Ω,接 DDR 的時(shí)候是 240Ω) 010 R0/2 011 R0/3 100 R0/4 101 R0/5 110 R0/6 111 R0/7 SRE壓擺率配置
設(shè)置壓擺率婴栽。
壓擺率是指電壓轉(zhuǎn)換速率满粗,可理解為電壓由波谷升到波峰的時(shí)間。增大壓擺率可減少輸出電壓的上升時(shí)間愚争。i.MX6ULL的引腳通過(guò)SRE支持低速和高速壓擺率這兩種配置映皆。當(dāng)此位為0的時(shí)候是低壓擺率,當(dāng)為1的時(shí)候是高壓擺率轰枝。壓擺率是大信號(hào)特性捅彻,下面的帶寬是小信號(hào)特性。-
SPEED帶寬配置
設(shè)置IO的帶寬鞍陨。
分別可設(shè)置為50MHz步淹、100MHz以及200MHz。帶寬的意思是能通過(guò)這個(gè)IO口最高的信號(hào)頻率诚撵,通俗點(diǎn)講就是方波不失真贤旷,如果超過(guò)這個(gè)頻率方波就變正弦波。但是這個(gè)帶寬要區(qū)別于IO的翻轉(zhuǎn)速率砾脑,IO的翻轉(zhuǎn)速率的信號(hào)來(lái)自于GPIO這個(gè)外設(shè)幼驶,而IO的帶寬只是限制了IO口引腳的物理特性,IO口的信號(hào)可以來(lái)自于內(nèi)部定時(shí)器輸出的PWM信號(hào)韧衣,也可以來(lái)自于GPIO翻轉(zhuǎn)輸出的信號(hào)盅藻,兩者相比之下,PWM信號(hào)的頻率是遠(yuǎn)遠(yuǎn)高于GPIO翻轉(zhuǎn)輸出的信號(hào)頻率畅铭。位設(shè)置 速度 00 低速 50M 01 中速 100M 10 中速 100M 11 最大速度 200M ODE開(kāi)漏輸出配置
設(shè)置引腳是否工作在開(kāi)漏輸出模式氏淑。
在該模式時(shí)引腳可以輸出高阻態(tài)和低電平,此位為0的時(shí)候禁止開(kāi)路輸出硕噩,當(dāng)此位為1的時(shí)候就使能開(kāi)路輸出功能假残。輸出高阻態(tài)時(shí)可由外部上拉電阻拉至高電平。開(kāi)漏輸出模式常用在一些通訊總線中炉擅,如I2C辉懒。
③輸入緩沖區(qū)
當(dāng)輸入緩沖區(qū)使能時(shí),引腳被配置為輸入模式谍失。在輸入緩沖區(qū)中眶俩,又包含了如下的屬性配置:
- HYS滯后使能
用來(lái)使能遲滯比較器。
i.MX6ULL的輸入檢測(cè)可以使用普通的CMOS檢測(cè)或施密特觸發(fā)器模式(滯后模式)快鱼。施密特觸發(fā)器具有滯后效應(yīng)颠印,對(duì)正向和負(fù)向變化的輸入信有不同的閾值電壓纲岭。如果需要對(duì)輸入波形進(jìn)行整形的話可以使能此位。此位為0的時(shí)候禁止遲滯比較器线罕,為1的時(shí)候使能遲滯比較器止潮。常被用于電子開(kāi)關(guān)、波形變換等場(chǎng)合钞楼,其轉(zhuǎn)換特性和對(duì)比如下沽翔,如檢測(cè)按鍵時(shí),使用施密特模式即可起到消抖的功能窿凤。
④Pull/Keeper上下拉仅偎、保持器
引腳的控制邏輯中還包含了上下拉、保持器的功能雳殊。芯片內(nèi)部的上拉和下拉電阻可以將不確定的信號(hào)鉗位在高橘沥、低電平,或小幅提高的電流輸出能力夯秃,上拉提供輸出電流座咆,下拉提供輸入電流。注意這些上下拉配置只是弱拉仓洼,對(duì)于類似I2C之類的總線介陶,還是必須使用外部上拉電阻。i.MX6ULL芯片的電源模塊中包含轉(zhuǎn)換器色建,當(dāng)轉(zhuǎn)換器停止工作時(shí)哺呜,保持器會(huì)保持輸入輸出電壓。
上下拉箕戳、保持器可以通過(guò)如下屬性配置:
- PUS上下拉配置
設(shè)置上下拉電阻某残。
PUS可配置項(xiàng)可選為100K歐下拉以及22K歐、47K歐及100K歐上拉陵吸。位設(shè)置 含義 00 100K 下拉 01 47K 上拉 10 100K 上拉 11 22K 上拉 - PUE上下拉玻墅、保持器選擇
上下拉功能和保持器功能是二選一的,可以通過(guò)PUE來(lái)選擇壮虫。
當(dāng)IO作為輸入的時(shí)候澳厢,這個(gè)位用來(lái)設(shè)置 IO 使用上下拉還是狀態(tài)保持器。當(dāng)為0的時(shí)候使用狀態(tài)保持器囚似,當(dāng)為1的時(shí)候使用上下拉剩拢。狀態(tài)保持器在IO作為輸入的時(shí)候才有用,顧名思義谆构,就是當(dāng)外部電路斷電以后此IO口可以保持住以前的狀態(tài)裸扶。 - PKE上下拉框都、保持器配置
用來(lái)使能或者禁止上下拉/狀態(tài)保持器功能搬素。
為0時(shí)禁止上下拉/狀態(tài)保持器呵晨,為1時(shí)使能上下拉和狀態(tài)保持器。
注意熬尺,當(dāng)引腳被配置為輸出模式時(shí)摸屠,不管上下拉、保持器是什么配置粱哼,它們都會(huì)被關(guān)閉季二。
1.4 GPIO配置
GPIO 模塊是每個(gè) IO 都具有的外設(shè),它具有 IO 控制最基本的功能揭措,如輸出高低電平胯舷、檢測(cè)電平輸入等。 它也占用 IOMUX 分配的復(fù)用信號(hào)绊含,也就是說(shuō)使用 GPIO 模塊功能時(shí)同樣需要使用 IOMUX 選中 GPIO 外設(shè)桑嘶,對(duì)其 GPIO 的功能進(jìn)行配置。
1.4.1 GDIR方向寄存器
設(shè)置某個(gè) IO 的工作方向躬充。
控制一個(gè) GPIO 引腳時(shí)逃顶,要先用 GDIR 方向寄存器配置該引腳用于輸出電平信號(hào)還是用作輸入檢測(cè)。 典型的例子是使用輸出模式可以控制LED燈的亮滅充甚,輸入模式時(shí)可以用來(lái)檢測(cè)按鍵是否按下以政。
GDIR 寄存器的每一個(gè)數(shù)據(jù)位代表一個(gè)引腳的方向,對(duì)應(yīng)的位被設(shè)置為0時(shí)該引腳為輸入模式伴找,被設(shè)置為1時(shí)該引腳為輸出模式盈蛮。
例如,對(duì) GPIO1 的 GDIR 寄存器的 bit3 位被寫(xiě)入為 1技矮,那么 GPIO1.3 引腳的模式即為輸出眉反。
1.4.2 DR數(shù)據(jù)寄存器
DR 數(shù)據(jù)寄存器直接代表了引腳的電平狀態(tài)
,它也使用 1 個(gè)數(shù)據(jù)位表示 1 個(gè)引腳的電平穆役,每位用 1 表示高電平寸五,用 0 表示低電平。
當(dāng) GDIR 方向寄存器設(shè)置引腳為輸出模式時(shí)耿币,寫(xiě)入 DR 數(shù)據(jù)寄存器對(duì)應(yīng)的位即可控制該引腳輸出的電平狀態(tài)梳杏, 如這時(shí) GPIO1 的 DR 寄存器的 bit4 被寫(xiě)入為 1,則引腳為輸出高電平淹接。
當(dāng) GDIR 方向寄存器設(shè)置引腳為輸入模式時(shí)十性,讀取 DR 數(shù)據(jù)寄存器對(duì)應(yīng)的位即可獲取該引腳當(dāng)前的輸入電平狀態(tài),例如這里讀取 GPIO1 的DR寄存器的 bit4塑悼,得到該位的值為 0劲适,表示當(dāng)前引腳的輸入狀態(tài)為低電平。
1.4.3 PSR引腳狀態(tài)寄存器
讀取相應(yīng)的位即可獲取對(duì)應(yīng)的 GPIO 的狀態(tài)
厢蒜,也就是 GPIO 的高低電平值霞势。PSR 引腳狀態(tài)寄存器相當(dāng)于 DR 寄存器的簡(jiǎn)化版烹植,它僅在 GDIR 方向寄存器設(shè)置為輸入模式時(shí)有效,它的每個(gè)位表示一個(gè)引腳當(dāng)前的輸入電平狀態(tài)愕贡。PSR 寄存器的權(quán)限是只讀的草雕,對(duì)它進(jìn)行寫(xiě)操作是無(wú)效的。
特別地固以,當(dāng)引腳被配置成輸出模式時(shí)墩虹,若 IOMUXC 中的 MUX 寄存器使能了 SION 功能(輸出通道回環(huán)至輸入), 可以通過(guò) PSR 寄存器讀取回引腳的狀態(tài)值憨琳。
二诫钓、引腳確定
我使用的是 野火_EBF6ULL S1 Pro
開(kāi)發(fā)板
從原理圖可看到 RGB 燈的三個(gè)陰極 R、G篙螟、B 連接分別連接至標(biāo)號(hào)
GPIO_4
尖坤、CSI_HSYNC
、CSI_VSYNC
闲擦, 這些標(biāo)號(hào)實(shí)際上與配套核心板上 i.MX6ULL 芯片的引腳相連慢味。由于引腳功能眾多, 繪制原理圖時(shí)不可避免地?zé)o法完全表示引腳信息的所有信息墅冷。而無(wú)論是具體的引腳名還是復(fù)用功能纯路, 我們都無(wú)法直接得知這些具體是 i.MX6ULL 芯片的哪個(gè)引腳。我們需要知道這些引腳是對(duì)應(yīng)的具體 GPIO寞忿,這樣我們才能編寫(xiě)程序進(jìn)行控制驰唬。
由于還不清楚標(biāo)號(hào) GPIO_4
、CSI_HSYNC
腔彰、CSI_VSYNC
的具體引腳名叫编,我們首先要在核心板原理圖中查看它與 i.MX6ULL 芯片的關(guān)系。打開(kāi) 《野火_EBF6ULL S1 郵票孔核心板V1.0原理圖》霹抛,在PDF閱讀器的搜索框輸入前面的 GPIO_4
搓逾、CSI_HSYNC
、CSI_VSYNC
標(biāo)號(hào)杯拐。
查找到了
GPIO_4
信號(hào)的具體引腳名為GPIO1_IO04
霞篡。 但是當(dāng)我們使用同樣的方法查找時(shí)發(fā)現(xiàn)只能找到CSI_HSYNC
、CSI_VSYNC
端逼, 并沒(méi)有我們熟悉的 GPIOx_IOx 標(biāo)注的引腳名朗兵。這兩個(gè)引腳默認(rèn)情況下不用作 GPIO,而是用作攝像頭的某一功能引腳顶滩,但是它可以復(fù)用為 GPIO余掖,我們?cè)趺凑业綄?duì)應(yīng)的 GPIO 呢?
-
方法一:
在《i.MX 6UltraLite Applications Processor Reference Manual》的第4章 External Signals and Pin Multiplexing 搜索引腳名
-
方法二:
在官方寫(xiě)好的文件 fsl_iomuxc.h(路徑:SDK文件夾/devices/MCIMX6Y2/drivers/fsl_iomuxc.h) 中搜索引腳名
經(jīng)查閱礁鲁,我們把以上連接 LED 燈的各個(gè) i.MX6ULL 芯片引腳總結(jié)出如表:
LED燈 | 原理圖的標(biāo)號(hào) | 具體引腳名 | GPIO端口及引腳編號(hào) |
---|---|---|---|
R燈 | GPIO_4 | GPIO1_IO04 | GPIO1_IO04 |
G燈 | CSI_HSYNC | CSI_HSYNC | GPIO4_IO20 |
B燈 | CSI_VSYNC | CSI_VSYNC | GPIO4_IO19 |
三盐欺、編程流程
1. 開(kāi)啟GPIO時(shí)鐘
2. 設(shè)置引腳的復(fù)用功能以及引腳屬性
3. 設(shè)置引腳方向以及輸出電平
四赁豆、編程代碼
4.1 完整代碼
/*************************第一部分*************************/
.text //代碼段
.align 2 //設(shè)置2字節(jié)對(duì)齊
.global _start //定義一個(gè)全局標(biāo)號(hào)
/*************************第二部分*************************/
_start: //程序的開(kāi)始
b reset //跳轉(zhuǎn)到reset標(biāo)號(hào)處
/*************************第三部分*************************/
reset:
mrc p15, 0, r0, c1, c0, 0 /* 將 CP15 協(xié)處理器中的寄存器數(shù)據(jù)讀到 ARM 寄存器中 */
bic r0, r0, #(0x1 << 12) /* 清除第12位(I位)禁用 I Cache */
bic r0, r0, #(0x1 << 2) /* 清除第 2位(C位)禁用 D Cache */
bic r0, r0, #0x2 /* 清除第 1位(A位)禁止嚴(yán)格對(duì)齊 */
bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支預(yù)測(cè) */
bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */
mcr p15, 0, r0, c1, c0, 0 /* 將 ARM 寄存器的數(shù)據(jù)寫(xiě)入到 CP15 協(xié)處理器寄存器中 */
/*************************第四部分*************************/
/*跳轉(zhuǎn)到light_led函數(shù)*/
bl light_led
/*進(jìn)入死循環(huán)*/
/*************************第五部分*************************/
loop:
b loop
/*************************第六部分*************************/
/*CCM_CCGR1 時(shí)鐘使能寄存器地址,默認(rèn)時(shí)鐘全部開(kāi)啟*/
#define gpio1_clock_enible_ccm_ccgr1 0x20C406C
/*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
寄存器地址找田,用于設(shè)置GPIO1_iIO04的復(fù)用功能*/
#define gpio1_io04_mux_ctl_register 0x20E006C
/*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04寄存器地址歌憨,用于設(shè)置GPIO的PAD屬性*/
#define gpio1_io04_pad_ctl_register 0x20E02F8
/*GPIO1_GDIR寄存器着憨,用于設(shè)置GPIO為輸入或者輸出*/
#define gpio1_gdir_register 0x0209C004
/*GPIO1_DR寄存器墩衙,用于設(shè)置GPIO輸出的電平狀態(tài)*/
#define gpio1_dr_register 0x0209C000
/*************************第七部分*************************/
light_led:
/*開(kāi)啟GPIO1的時(shí)鐘*/
ldr r0, =gpio1_clock_enible_ccm_ccgr1
ldr r1, =0xFFFFFFFF
str r1, [r0]
/*************************第八部分*************************/
/*將PAD引腳復(fù)用為GPIO*/
ldr r0, =gpio1_io04_mux_ctl_register
ldr r1, =0x5
str r1, [r0]
/*************************第九部分*************************/
/*設(shè)置GPIO PAD屬性*/
ldr r0, =gpio1_io04_pad_ctl_register
ldr r1, =0x1F838
str r1, [r0]
/*************************第十部分*************************/
/*將GPIO_GDIR.[4] 設(shè)置為1, gpio1_io04設(shè)置為輸出模式*/
ldr r0, =gpio1_gdir_register
ldr r1, =0x10
str r1, [r0]
/*************************第十一部分*************************/
/*將GPIO_DR 設(shè)置為0甲抖, gpio1全部輸出為低電平*/
ldr r0, =gpio1_dr_register
ldr r1, =0x0
str r1, [r0]
/*************************第十二部分*************************/
/*跳出light_led函數(shù)漆改,返回跳轉(zhuǎn)位置*/
mov pc, lr
4.2 分析代碼
在 Ubuntu 下創(chuàng)建 led.S
文件用于編寫(xiě) LED 匯編驅(qū)動(dòng)代碼。
-
第一部分
.text
定義代碼段准谚。
.align 2
設(shè)置字節(jié)對(duì)齊挫剑。
.global _start
生命全局標(biāo)號(hào)_start。
/*************************第一部分*************************/
.text //代碼段
.align 2 //設(shè)置2字節(jié)對(duì)齊
.global _start //定義一個(gè)全局標(biāo)號(hào)
-
第二部分
_start:
定義標(biāo)號(hào)_start: 柱衔,它位于匯編的最前面樊破,說(shuō)以會(huì)首先被執(zhí)行。
b reset
使用b指令將程序跳轉(zhuǎn)到reset標(biāo)號(hào)處唆铐。
/*************************第二部分*************************/
_start: //程序的開(kāi)始
b reset //跳轉(zhuǎn)到reset標(biāo)號(hào)處
-
第三部分
通過(guò)修改CP15寄存器(系統(tǒng)控制寄存器)
關(guān)閉 I Cache 哲戚、D Cache、MMU 等等艾岂。
我們暫時(shí)用不到的功能顺少,如果開(kāi)啟可能會(huì)影響我們裸機(jī)運(yùn)行,為避免不必要的麻煩暫時(shí)關(guān)閉這些功能王浴。
/*************************第三部分*************************/
reset:
mrc p15, 0, r0, c1, c0, 0 /* 將 CP15 協(xié)處理器中的寄存器數(shù)據(jù)讀到 ARM 寄存器中 */
bic r0, r0, #(0x1 << 12) /* 清除第12位(I位)禁用 I Cache */
bic r0, r0, #(0x1 << 2) /* 清除第 2位(C位)禁用 D Cache */
bic r0, r0, #0x2 /* 清除第 1位(A位)禁止嚴(yán)格對(duì)齊 */
bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支預(yù)測(cè) */
bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */
mcr p15, 0, r0, c1, c0, 0 /* 將 ARM 寄存器的數(shù)據(jù)寫(xiě)入到 CP15 協(xié)處理器寄存器中 */
-
第四部分
bl light_led
執(zhí)行跳轉(zhuǎn)指令脆炎,代碼將跳轉(zhuǎn)到函數(shù)“l(fā)ight_led”執(zhí)行。
“bl”指令是“可返回”跳轉(zhuǎn)氓辣,跳轉(zhuǎn)之前的執(zhí)行地址保存在lr(連接寄存器)中秒裕。
“l(fā)ight_led” 函數(shù)實(shí)現(xiàn)位于第六到十二部分。
/*************************第四部分*************************/
/*跳轉(zhuǎn)到light_led函數(shù)*/
bl light_led
/*進(jìn)入死循環(huán)*/
-
第五部分
light_led函數(shù)返回后就會(huì)執(zhí)行標(biāo)號(hào)loop處的代碼钞啸,而標(biāo)號(hào)loop處只有一條指令b loop
簇爆, 這個(gè)指令是代碼再次跳轉(zhuǎn)到loop標(biāo)號(hào)處,所以這是一個(gè)死循環(huán)爽撒。
/*************************第五部分*************************/
loop:
b loop
-
第六部分
定義我們用到的寄存器地址入蛆。
配置時(shí)鐘使能寄存器地址
設(shè)置時(shí)鐘控制寄存器CCM_CCGR1的地址如下:
配置MUX Mode寄存器地址
MUX Mode用于設(shè)置GPIO1_IO04的復(fù)用功能。配置成普通的IO口硕勿,僅用于控制輸出高低電平哨毁。寄存器地址如下:
配置Pad Settings寄存器地址
Pad Settings用于設(shè)置GPIO的PAD屬性。例如驅(qū)動(dòng)能力源武,是否使用上下拉電阻扼褪, 是否使用保持器想幻,是否使用開(kāi)漏模式以及使用施密特模式還是CMOS模式等。寄存器地址如下:
/*************************第六部分*************************/
/*CCM_CCGR1 時(shí)鐘使能寄存器地址话浇,默認(rèn)時(shí)鐘全部開(kāi)啟*/
#define gpio1_clock_enible_ccm_ccgr1 0x20C406C
/*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
寄存器地址脏毯,用于設(shè)置GPIO1_IO04的復(fù)用功能*/
#define gpio1_io04_mux_ctl_register 0x20E006C
/*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04寄存器地址,用于設(shè)置GPIO的PAD屬性*/
#define gpio1_io04_pad_ctl_register 0x20E02F8
/*GPIO1_GDIR寄存器幔崖,用于設(shè)置GPIO為輸入或者輸出*/
#define gpio1_gdir_register 0x0209C004
/*GPIO1_DR寄存器食店,用于設(shè)置GPIO輸出的電平狀態(tài)*/
#define gpio1_dr_register 0x0209C000
-
第七部分
開(kāi)啟GPIO1的時(shí)鐘。
設(shè)置的時(shí)鐘控制寄存器CCM_CCGR1
赏寇。
從上表中可以看出CCM_CCGR1[26:27]用于使能GPIO1的時(shí)鐘吉嫩,這里不僅僅設(shè)置時(shí)鐘的開(kāi)或者關(guān), 還可以設(shè)置在芯片在不同工作模式下的時(shí)鐘狀態(tài)如下表:
CCM_CCGR1[26:27]的值 | 時(shí)鐘狀態(tài)描述 |
---|---|
00 | 時(shí)鐘在所有模式下都是關(guān)閉的 |
01 | 時(shí)鐘在運(yùn)行模式下為開(kāi)嗅定,但在等待和停止模式下為關(guān) |
10 | 保留 |
11 | 除停止模式外自娩,時(shí)鐘一直開(kāi)啟 |
我們將CCM_CCGR1[26:27]設(shè)置為11
(二進(jìn)制)即可。仔細(xì)觀察可以發(fā)現(xiàn)發(fā)現(xiàn)CCM_CCGR1寄存器默認(rèn)全為1渠退,即默認(rèn)開(kāi)啟了時(shí)鐘忙迁。 為了程序規(guī)范我們?cè)俅问褂么a開(kāi)啟時(shí)鐘。將CCM_CCGR1寄存器設(shè)置全為1
碎乃。
ldr r0, =gpio1_clock_enible_ccm_ccgr1
從存儲(chǔ)器Rn+offset(即gpio1_clock_enible_ccm_ccgr1)的位置讀取數(shù)據(jù)存放到r0中姊扔。
str r1, [r0]
將r1中的數(shù)據(jù)寫(xiě)入到存儲(chǔ)器中的Rn+offset(即r0的指向)位置。
/*************************第七部分*************************/
light_led:
/*開(kāi)啟GPIO1的時(shí)鐘*/
ldr r0, =gpio1_clock_enible_ccm_ccgr1
ldr r1, =0xFFFFFFFF
str r1, [r0]
-
第八部分
設(shè)置引腳復(fù)用功能為GPIO荠锭。
這里設(shè)置的是GPIO1_04的引腳復(fù)用寄存器旱眯,我們直接搜索IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
可以找到如下所示的寄存器。
從上圖可知IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04[MUX_MODE]=0101(二進(jìn)制)時(shí)GPIO1_04復(fù)用功能是GPIO证九。所以在程序中我們將0x5
寫(xiě)入該寄存即可删豺。
/*************************第八部分*************************/
/*將PAD引腳復(fù)用為GPIO*/
ldr r0, =gpio1_io04_mux_ctl_register
ldr r1, =0x5
str r1, [r0]
-
第九部分
設(shè)置引腳的PAD屬性。
這里設(shè)置的是GPIO1_04的引腳PAD屬性寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04”
/*************************第九部分*************************/
/*設(shè)置GPIO PAD屬性*/
ldr r0, =gpio1_io04_pad_ctl_register
ldr r1, =0x1F838
str r1, [r0]
-
第十部分
設(shè)置GPIO為輸出模式愧怜。
/*************************第十部分*************************/
/*將GPIO_GDIR.[4] 設(shè)置為1呀页, gpio1_io04設(shè)置為輸出模式*/
ldr r0, =gpio1_gdir_register
ldr r1, =0x10
str r1, [r0]
-
第十一部分
設(shè)置GPIO輸出電平為低電平。
/*************************第十一部分*************************/
/*將GPIO_DR 設(shè)置為0拥坛, gpio1全部輸出為低電平*/
ldr r0, =gpio1_dr_register
ldr r1, =0x0
str r1, [r0]
-
第十二部分
從light_led函數(shù)返回蓬蝶。在第四部分說(shuō)到,我們使用“bl”指令跳轉(zhuǎn)到light_led函數(shù)執(zhí)行猜惋, “bl”指令是“可返回”的跳轉(zhuǎn)指令丸氛,返回地址保存在“LR”(連接寄存器)里。
mov pc, lr
將“l(fā)r”寄存器的值寫(xiě)入“pc”寄存器即可著摔。
/*************************第十二部分*************************/
/*跳出light_led函數(shù)缓窜,返回跳轉(zhuǎn)位置*/
mov pc, lr
五、編譯下載驗(yàn)證
5.1 編譯生成.bin文件
5.1.1 編譯文件
編譯出在ARM開(kāi)發(fā)板上運(yùn)行的可執(zhí)行文件,所以要使用我們安裝的交叉編譯器arm-linux-gnueabihf-gcc來(lái)編譯禾锤。 編譯出的led.o文件并不是我們可以下載到開(kāi)發(fā)板中運(yùn)行的文件私股,一個(gè)工程中所有的C文件和匯編文件都會(huì)編譯生成一個(gè)對(duì)應(yīng)的.o文件,我們需要將這.o文件鏈接起來(lái)組合成可執(zhí)行文件恩掷。
arm-none-eabi-gcc -g -c led.S -o led.o
- -g :加入GDB能夠使用的調(diào)試信息,能夠使用GDB調(diào)試倡鲸。
- -c :對(duì)源程序example.c進(jìn)行預(yù)處理、編譯黄娘、匯編操作峭状,生成example.o文件。
- led.S :要編譯的源文件寸宏。
- -o :指定輸出文件的文件名宁炫,不加“-o led.o”默認(rèn)會(huì)輸出led.o偿曙。 正常情況下執(zhí)行該命令后會(huì)在當(dāng)前文件夾下生成led.o文件氮凝。
5.1.2 鏈接文件
arm-linux-gnueabihf-ld用來(lái)將眾多的.o文件鏈接到一個(gè)指定的鏈接地址。這里我們要區(qū)分“存儲(chǔ)地址”和“運(yùn)行地址”這兩個(gè)概念望忆,“存儲(chǔ)地址”就是可執(zhí)行文件存儲(chǔ)在哪里罩阵,可執(zhí)行文件的存儲(chǔ)地址可以隨意選擇∑羯悖“運(yùn)行地址”就是代碼運(yùn)行的時(shí)候所處的地址惩猫,這個(gè)我們?cè)阪溄拥臅r(shí)候就已經(jīng)確定好了检号,代碼要運(yùn)行,那就必須處于運(yùn)行地址處,否則代碼肯定運(yùn)行出錯(cuò)纤壁。比如I.MX6ULL支持SD卡、EMMC空凸、NAND啟動(dòng)避矢,因此代碼可以存儲(chǔ)到SD卡、EMMC或者NAND中龟再,但是要運(yùn)行的話就必須將代碼從SD卡书闸、EMMC或者NAND中拷貝到其運(yùn)行地址(鏈接地址)處,“存儲(chǔ)地址”和“運(yùn)行地址”可以一樣利凑,比如STM32的存儲(chǔ)起始地址和運(yùn)行起始地址都是0X08000000浆劲。上電以后I.MX6ULL的內(nèi)部boot rom程序會(huì)將可執(zhí)行文件拷貝到鏈接地址處,這個(gè)鏈接地址可以在I.MX6UL的內(nèi)部128KB RAM中(0X900000~0X91FFFF)哀澈,也可以在外部的DDR中牌借。led.elf 文件也不是我們最終燒寫(xiě)到SD卡中的可執(zhí)行文件,我們要燒寫(xiě)的.bin文件割按,因此還需要將led.elf文件轉(zhuǎn)換為.bin文件膨报。
arm-none-eabi-ld -Ttext 0x80000000 led.o -o led.elf
-
-Ttext 0x80000000 :
設(shè)置程序代碼段的起始地址為0x80000000
。0x80000000是外部?jī)?nèi)存的起始地址。這個(gè)地址是由芯片本身決定的丙躏,我們打開(kāi) 《IMX6ULRM》手冊(cè)在Chapter 2 Memory Maps章節(jié)ARM平臺(tái)內(nèi)存映射表 介紹了這部分內(nèi)容择示,如下所示。
- -o :指定輸出的文件名晒旅。
5.1.3 格式轉(zhuǎn)換
上一步鏈接生成的.elf文件是帶有地址信息的文件栅盲,不能放在存儲(chǔ)器中執(zhí)行,要使用格式轉(zhuǎn)換命令轉(zhuǎn)化為二進(jìn)制文件废恋。
arm-none-eabi-objcopy -O binary -S -g led.elf led.bin
- -O binary :指定輸出文件格式為二進(jìn)制文件谈秫。
- -S選項(xiàng) :不從源文件中復(fù)制重定位信息和符號(hào)信息。
- -g選項(xiàng) :不從源文件中復(fù)制可調(diào)試信息鱼鼓。
5.1.4 反匯編(可跳過(guò))
大多數(shù)情況下我們都是用C語(yǔ)言寫(xiě)試驗(yàn)例程的拟烫,有時(shí)候需要查看其匯編代碼來(lái)調(diào)試代碼,因此就需要進(jìn)行反匯編迄本,一般可以將elf文件反匯編硕淑。
arm-linux-gnueabihf-objdump -D led.elf > led.dis
- -D :表示反匯編所有的段,反匯編完成以后就會(huì)在當(dāng)前目錄下出現(xiàn)一個(gè)名為led.dis文件嘉赎。
5.2 代碼燒寫(xiě)
編譯成功后會(huì)在當(dāng)前文件夾下生成.bin文件置媳,這個(gè).bin文件也不能直接放到開(kāi)發(fā)板上運(yùn)行, 這次是因?yàn)樾枰?bin文件缺少啟動(dòng)相關(guān)信息公条。
為二進(jìn)制文件添加頭部信息并燒寫(xiě)到SD卡拇囊。查看 IMX6ULL學(xué)習(xí)筆記(12)——通過(guò)SD卡啟動(dòng)官方SDK程序
進(jìn)入燒寫(xiě)工具目錄,執(zhí)行 ./mkimage.sh <燒寫(xiě)文件路徑>
命令靶橱,例如要燒寫(xiě)的 led.bin 位于 home 目錄下寥袭,則燒寫(xiě)命令為 ./mkimage.sh /home/led.bin
。
執(zhí)行上一步后會(huì)列出linux下可燒寫(xiě)的磁盤(pán)关霸,選擇你插入的SD卡即可传黄。這一步 非常危險(xiǎn)!Z怂尝江!一定要確定選擇的是你插入的SD卡!英上!炭序,如果選錯(cuò)很可能破壞你電腦磁盤(pán)內(nèi)容,造成數(shù)據(jù)損壞2匀铡2涯簟! 確定磁盤(pán)后SD卡以“sd”開(kāi)頭相恃,選擇“sd”后面的字符即可辜纲。例如要燒寫(xiě)的sd卡是“sdb”則輸入“b”即可。
5.3 實(shí)驗(yàn)現(xiàn)象
將開(kāi)發(fā)板設(shè)置為SD卡啟動(dòng),接入SD卡耕腾,開(kāi)發(fā)板上電见剩,正常情況下可以看到開(kāi)發(fā)板RGB燈紅燈亮。
? 由 Leung 寫(xiě)于 2022 年 12 月 12 日
? 參考:4. 匯編點(diǎn)亮LED燈