一、GPIO簡介
i.MX6ULL 芯片的 GPIO 被分成 5 組,并且每組 GPIO 的數(shù)量不盡相同,例如 GPIO1 擁有 32 個引腳趴久, GPIO2 擁有 22 個引腳, 其他 GPIO 分組的數(shù)量以及每個 GPIO 的功能請參考 《i.MX 6UltraLite Applications Processor Reference Manual》 第26章General Purpose Input/Output (GPIO)(P1133)。
通過 GPIO 硬件結(jié)構(gòu)框圖改含,就可以從整體上深入了解 GPIO 外設(shè)及它的各種應(yīng)用模式。
1.1 IO命名
打開 i.MX6ULL 參考手冊的第 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ù)某個 IO 所擁有的功能來命名的。比如我們一看到 GPIO1_IO01 就知道這個肯定能做 GPIO沉填,看到 UART1_TX_DATA 肯定就知道這個 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 的芯片每個 GPIO 都通過 IOMUX 支持多種功能颠锉, 例如一個 IO 可用于網(wǎng)絡(luò)外設(shè) ENET 的數(shù)據(jù)接收引腳,也可以被配置成 PWM 外設(shè)的輸出引腳史汗, 這樣的設(shè)計大大增加了芯片的適用性琼掠,這樣可選的功能就是由 IOMUX 實(shí)現(xiàn)的。IOMUX 相當(dāng)于增加了多根內(nèi)部信號線與 IO 引腳相連停撞,
最多有 8 根瓷蛙,也就是說一個 IO 最多可支持 8 種可選的功能
。
以“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”這個 IO 為例戈毒,打開參考手冊的 1568 頁艰猬。
可以看到有個名為:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器,寄存器地址為 0X020E005C埋市,這個寄存器是 32 位的冠桃,但是只用到了最低 5 位,其中 bit0~bit3(MUX_MODE) 就是設(shè)置 GPIO1_IO00 的復(fù)用功能的道宅。GPIO1_IO00 一共可以復(fù)用為 9 種功能 IO食听,分別對應(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配置) 兩個部分:
在 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 |
每個引腳都包含這兩個寄存器零蓉,表中的XXXX表示引腳的名字
1.3.1 MUX Mode配置
MUX Mode 就是用來
配置引腳的復(fù)用功能
笤受,即選擇引腳具體是用于網(wǎng)絡(luò)外設(shè) ENET 的數(shù)據(jù)接收, 還是用于 PWM 外設(shè)的輸出引腳敌蜂,當(dāng)然箩兽,也可以配置成普通的 IO 口,僅用于控制輸出高低電平章喉。
以 GPIO1_IO04 引腳為例對 MUX 寄存器進(jìn)行說明汗贫,該引腳相應(yīng)的 MUX 寄存器在參考手冊中的描述如下:
該寄存器主要有兩個配置域,分別是 SION 和 MUX_MODE秸脱。
- SION: 用于設(shè)置引腳在輸出模式下同時開啟輸入通道落包。
-
MUX_MODE: 使用 4 個寄存器位表示可選的 ALT0~ALT7 這 8 個模式。
- 如 ALT2 模式就是用于 USB 外設(shè)的 USB_OTG1_PWR 信號摊唇;
- 若配置為 ALT5 則引腳會用作普通的 GPIO 功能咐蝇, 用于輸出高、低電平巷查。
1.3.2 Pad Settings配置
Pad Settings 用于
配置引腳的屬性
有序,例如驅(qū)動能力抹腿,是否使用上下拉電阻, 是否使用保持器旭寿,是否使用開漏模式以及使用施密特模式還是CMOS模式等警绩。
以 GPIO1_IO04 引腳中 PAD 寄存器在參考手冊中的描述如下:
相對來說 PAD 寄存器的配置項(xiàng)就更豐富了,而且圖中僅是該寄存器的部分說明盅称,如 HYS 設(shè)置使用施密特模式的滯后功能肩祥,PUS 配置上下拉電阻的阻值, 其它的還包含PUE缩膝、PKE混狠、ODE、SPEED逞盆、DSE 及 SRE 的配置檀蹋。
1.3.3 PAD(可跳過不看)
PAD 代表了一個 i.MX6ULL 的 GPIO 引腳松申。在它的左側(cè)是一系列信號通道及控制線云芦,如 input_on 控制輸入開關(guān),Dir 控制引腳的輸入輸出方向贸桶,Data_out 控制引腳輸出高低電平舅逸,Data_in 作為信號輸入,這些信號都經(jīng)過一個 IOMUX 的器件連接到左側(cè)的寄存器皇筛。
①PAD引腳
代表一個i.MX6ULL的引腳琉历。
②輸出緩沖區(qū)
當(dāng)輸出緩沖區(qū)使能時,引腳被配置為輸出模式水醋。在輸出緩沖區(qū)中旗笔,又包含了如下的屬性配置:
-
DSE驅(qū)動能力
當(dāng)IO用作輸出的時候用來設(shè)置IO的驅(qū)動能力。
DSE可以調(diào)整芯片內(nèi)部與引腳串聯(lián)電阻R0的大小拄踪,從而改變引腳的驅(qū)動能力蝇恶。例如,R0的初始值為260歐姆惶桐,在3.3V電壓下其電流驅(qū)動能力為12.69mA撮弧,通過DSE可以把R0的值配置為原值的1/2、1/3…1/7等姚糊。位設(shè)置 速度 000 輸出驅(qū)動關(guān)閉 001 R0(3.3V 下 R0 是 260Ω贿衍,1.8V 下 R0 是 150Ω,接 DDR 的時候是 240Ω) 010 R0/2 011 R0/3 100 R0/4 101 R0/5 110 R0/6 111 R0/7 SRE壓擺率配置
設(shè)置壓擺率救恨。
壓擺率是指電壓轉(zhuǎn)換速率贸辈,可理解為電壓由波谷升到波峰的時間。增大壓擺率可減少輸出電壓的上升時間肠槽。i.MX6ULL的引腳通過SRE支持低速和高速壓擺率這兩種配置擎淤。當(dāng)此位為0的時候是低壓擺率躏哩,當(dāng)為1的時候是高壓擺率。壓擺率是大信號特性揉燃,下面的帶寬是小信號特性扫尺。-
SPEED帶寬配置
設(shè)置IO的帶寬。
分別可設(shè)置為50MHz炊汤、100MHz以及200MHz正驻。帶寬的意思是能通過這個IO口最高的信號頻率,通俗點(diǎn)講就是方波不失真抢腐,如果超過這個頻率方波就變正弦波姑曙。但是這個帶寬要區(qū)別于IO的翻轉(zhuǎn)速率,IO的翻轉(zhuǎn)速率的信號來自于GPIO這個外設(shè)迈倍,而IO的帶寬只是限制了IO口引腳的物理特性伤靠,IO口的信號可以來自于內(nèi)部定時器輸出的PWM信號,也可以來自于GPIO翻轉(zhuǎn)輸出的信號啼染,兩者相比之下宴合,PWM信號的頻率是遠(yuǎn)遠(yuǎn)高于GPIO翻轉(zhuǎn)輸出的信號頻率。位設(shè)置 速度 00 低速 50M 01 中速 100M 10 中速 100M 11 最大速度 200M ODE開漏輸出配置
設(shè)置引腳是否工作在開漏輸出模式迹鹅。
在該模式時引腳可以輸出高阻態(tài)和低電平卦洽,此位為0的時候禁止開路輸出,當(dāng)此位為1的時候就使能開路輸出功能斜棚。輸出高阻態(tài)時可由外部上拉電阻拉至高電平阀蒂。開漏輸出模式常用在一些通訊總線中,如I2C弟蚀。
③輸入緩沖區(qū)
當(dāng)輸入緩沖區(qū)使能時蚤霞,引腳被配置為輸入模式。在輸入緩沖區(qū)中义钉,又包含了如下的屬性配置:
- HYS滯后使能
用來使能遲滯比較器昧绣。
i.MX6ULL的輸入檢測可以使用普通的CMOS檢測或施密特觸發(fā)器模式(滯后模式)。施密特觸發(fā)器具有滯后效應(yīng)断医,對正向和負(fù)向變化的輸入信有不同的閾值電壓滞乙。如果需要對輸入波形進(jìn)行整形的話可以使能此位。此位為0的時候禁止遲滯比較器鉴嗤,為1的時候使能遲滯比較器斩启。常被用于電子開關(guān)、波形變換等場合醉锅,其轉(zhuǎn)換特性和對比如下兔簇,如檢測按鍵時,使用施密特模式即可起到消抖的功能。
④Pull/Keeper上下拉垄琐、保持器
引腳的控制邏輯中還包含了上下拉边酒、保持器的功能。芯片內(nèi)部的上拉和下拉電阻可以將不確定的信號鉗位在高狸窘、低電平墩朦,或小幅提高的電流輸出能力,上拉提供輸出電流翻擒,下拉提供輸入電流氓涣。注意這些上下拉配置只是弱拉,對于類似I2C之類的總線陋气,還是必須使用外部上拉電阻劳吠。i.MX6ULL芯片的電源模塊中包含轉(zhuǎn)換器,當(dāng)轉(zhuǎn)換器停止工作時巩趁,保持器會保持輸入輸出電壓痒玩。
上下拉、保持器可以通過如下屬性配置:
- PUS上下拉配置
設(shè)置上下拉電阻议慰。
PUS可配置項(xiàng)可選為100K歐下拉以及22K歐蠢古、47K歐及100K歐上拉。位設(shè)置 含義 00 100K 下拉 01 47K 上拉 10 100K 上拉 11 22K 上拉 - PUE上下拉褒脯、保持器選擇
上下拉功能和保持器功能是二選一的便瑟,可以通過PUE來選擇缆毁。
當(dāng)IO作為輸入的時候番川,這個位用來設(shè)置 IO 使用上下拉還是狀態(tài)保持器。當(dāng)為0的時候使用狀態(tài)保持器脊框,當(dāng)為1的時候使用上下拉颁督。狀態(tài)保持器在IO作為輸入的時候才有用,顧名思義浇雹,就是當(dāng)外部電路斷電以后此IO口可以保持住以前的狀態(tài)沉御。 - PKE上下拉、保持器配置
用來使能或者禁止上下拉/狀態(tài)保持器功能昭灵。
為0時禁止上下拉/狀態(tài)保持器吠裆,為1時使能上下拉和狀態(tài)保持器。
注意烂完,當(dāng)引腳被配置為輸出模式時试疙,不管上下拉、保持器是什么配置抠蚣,它們都會被關(guān)閉祝旷。
1.4 GPIO配置
GPIO 模塊是每個 IO 都具有的外設(shè),它具有 IO 控制最基本的功能,如輸出高低電平怀跛、檢測電平輸入等距贷。 它也占用 IOMUX 分配的復(fù)用信號,也就是說使用 GPIO 模塊功能時同樣需要使用 IOMUX 選中 GPIO 外設(shè)吻谋,對其 GPIO 的功能進(jìn)行配置忠蝗。
1.4.1 GDIR方向寄存器
設(shè)置某個 IO 的工作方向。
控制一個 GPIO 引腳時漓拾,要先用 GDIR 方向寄存器配置該引腳用于輸出電平信號還是用作輸入檢測什湘。 典型的例子是使用輸出模式可以控制LED燈的亮滅,輸入模式時可以用來檢測按鍵是否按下晦攒。
GDIR 寄存器的每一個數(shù)據(jù)位代表一個引腳的方向闽撤,對應(yīng)的位被設(shè)置為0時該引腳為輸入模式,被設(shè)置為1時該引腳為輸出模式脯颜。
例如哟旗,對 GPIO1 的 GDIR 寄存器的 bit3 位被寫入為 1,那么 GPIO1.3 引腳的模式即為輸出栋操。
1.4.2 DR數(shù)據(jù)寄存器
DR 數(shù)據(jù)寄存器直接代表了引腳的電平狀態(tài)
闸餐,它也使用 1 個數(shù)據(jù)位表示 1 個引腳的電平,每位用 1 表示高電平矾芙,用 0 表示低電平舍沙。
當(dāng) GDIR 方向寄存器設(shè)置引腳為輸出模式時,寫入 DR 數(shù)據(jù)寄存器對應(yīng)的位即可控制該引腳輸出的電平狀態(tài)剔宪, 如這時 GPIO1 的 DR 寄存器的 bit4 被寫入為 1拂铡,則引腳為輸出高電平。
當(dāng) GDIR 方向寄存器設(shè)置引腳為輸入模式時葱绒,讀取 DR 數(shù)據(jù)寄存器對應(yīng)的位即可獲取該引腳當(dāng)前的輸入電平狀態(tài)感帅,例如這里讀取 GPIO1 的DR寄存器的 bit4,得到該位的值為 0地淀,表示當(dāng)前引腳的輸入狀態(tài)為低電平失球。
1.4.3 PSR引腳狀態(tài)寄存器
讀取相應(yīng)的位即可獲取對應(yīng)的 GPIO 的狀態(tài)
,也就是 GPIO 的高低電平值帮毁。PSR 引腳狀態(tài)寄存器相當(dāng)于 DR 寄存器的簡化版实苞,它僅在 GDIR 方向寄存器設(shè)置為輸入模式時有效,它的每個位表示一個引腳當(dāng)前的輸入電平狀態(tài)烈疚。PSR 寄存器的權(quán)限是只讀的黔牵,對它進(jìn)行寫操作是無效的。
特別地胞得,當(dāng)引腳被配置成輸出模式時荧止,若 IOMUXC 中的 MUX 寄存器使能了 SION 功能(輸出通道回環(huán)至輸入)屹电, 可以通過 PSR 寄存器讀取回引腳的狀態(tài)值。
二跃巡、引腳確定
我使用的是 野火_EBF6ULL S1 Pro
開發(fā)板
KEY按鍵連接至
SNVS_TAMPER1
引腳危号,用作普通的按鍵。
板上4個按鍵的信息及相應(yīng)GPIO端口引腳號的總結(jié)具體如下:
按鍵 | 絲印編號 | GPIO功能 | 按鍵按下時的電平 | 其它功能 |
---|---|---|---|---|
RST復(fù)位按鍵 | SW1 | 不支持 | 低電平 | 復(fù)位芯片 |
ON/OFF按鍵 | SW3 | 不支持 | 低電平 | 從低功耗喚醒 |
MODE按鍵 | SW4 | 支持 | BOOT_MODE[0]與BOOT_MODE[1]相反 | 選擇芯片啟動方式 |
KEY按鍵 | SW2 | 支持 | 高電平 | 無 |
三素邪、編程流程
1. 移植官方寄存器定義文件
2. 移植官方SDK引腳復(fù)用和引腳屬性定義文件
3. 移植野火PAD屬性配置文件
4. 編寫啟動文件
5. 編寫鏈接文件
6. 編寫makefile文件
7. 編寫C語言代碼
(1) 開啟GPIO時鐘
(2) 設(shè)置引腳的復(fù)用功能以及引腳屬性
(3) 設(shè)置引腳方向以及輸出電平
(4) 查詢按鍵輸入控制LED
四外莲、移植官方SDK寄存器定義文件
使用匯編語言和C語言實(shí)現(xiàn)點(diǎn)亮LED燈。需要自己查找兔朦、定義那么多寄存器偷线。這樣做的缺點(diǎn)很明顯,易錯沽甥、費(fèi)時声邦、代碼可讀性差。NXP官方SDK中已經(jīng)將所有的寄存器以及所有可用引腳的復(fù)用功能定義好了摆舟。
添加官方SDK寄存器定義文件 MCIMX6Y2.h
亥曹,位于 SDK_2.2_MCIM6ULL_EBF6ULL/devices/MCIMX6Y2
目錄下。
在官方SDK頭文件
MCIMX6Y2.h
文件多達(dá)4萬多行恨诱,包含了i.MX6U芯片幾乎所有的寄存器定義以及中斷編號的定義媳瞪。
這里只列 GPIO1相關(guān)寄存器 的部分代碼。其他寄存器定義與此類似照宝。 添加這些定義之后我們就可以 直接使用 “GPIO1->DR”
語句操作GPIO1的DR寄存器蛇受。操作方法與STM32非常相似。
typedef struct {
__IO uint32_t DR; /**< GPIO data register, offset: 0x0 */
__IO uint32_t GDIR; /**< GPIO direction register, offset: 0x4 */
__I uint32_t PSR; /**< GPIO pad status register, offset: 0x8 */
__IO uint32_t ICR1; /**< GPIO interrupt configuration register1,*/
__IO uint32_t ICR2; /**< GPIO interrupt configuration register2, */
__IO uint32_t IMR; /**< GPIO interrupt mask register, offset: 0x14 */
__IO uint32_t ISR; /**< GPIO interrupt status register, offset: 0x18 */
__IO uint32_t EDGE_SEL;/**< GPIO edge select register, offset: 0x1C */
} GPIO_Type;
/*********************以下代碼省略***************************8*/
/** Peripheral GPIO1 base address */
#define GPIO1_BASE (0x209C000u)
/** Peripheral GPIO1 base pointer */
#define GPIO1 ((GPIO_Type *)GPIO1_BASE)
五厕鹃、移植官方SDK引腳復(fù)用和引腳屬性定義文件
添加官方SDK引腳復(fù)用和引腳屬性定義文件 fsl_iomuxc.h
兢仰,位于 SDK_2.2_MCIM6ULL_EBF6ULL/devices/MCIMX6Y2/drivers
目錄下。
使用每一個引腳之前我們都要選擇引腳的復(fù)用功能以及引腳的pad屬性熊响。在官方SDK的頭文件
fsl_iomuxc.h
中定義了所有可用引腳以及這些引腳的所有復(fù)用功能旨别,我們需要哪種復(fù)用功能只需要選擇即可,并且官方SDK中提供了初始化函數(shù)汗茄。
-
定義引腳的復(fù)用功能
這里只列出了“GPIO1_IO00”引腳的復(fù)用功能,其他引腳類似铭若。每個引腳對應(yīng)多個宏定義代表引腳的不同的復(fù)用功能洪碳,以宏“IOMUXC_GPIO1_IO00_I2C2_SCL”為例,它表示“GPIO1_IO00”引腳復(fù)用為“I2C2”的“SCL”引腳叼屠。這些宏定義將會用作某些函數(shù)的入口參數(shù)瞳腌。
#define IOMUXC_GPIO1_IO00_I2C2_SCL \
0x020E005CU, 0x0U, 0x020E05ACU, 0x1U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_GPT1_CAPTURE1L \
0x020E005CU, 0x1U, 0x020E058CU, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ANATOP_OTG1_IDL \
0x020E005CU, 0x2U, 0x020E04B8U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ENET1_REF_CLK1L \
0x020E005CU, 0x3U, 0x020E0574U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_MQS_RIGHTL \
0x020E005CU, 0x4U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_GPIO1_IO00L \
0x020E005CU, 0x5U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ENET1_1588_EVENT0_INL \
0x020E005CU, 0x6U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_SRC_SYSTEM_RESETL \
0x020E005CU, 0x7U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_WDOG3_WDOG_BL \
0x020E005CU, 0x8U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO01_I2C2_SDAL \
0x020E0060U, 0x0U, 0x020E05B0U, 0x1U, 0x020E02ECU
#define IOMUXC_GPIO1_IO01_GPT1_COMPARE1L \
0x020E0060U, 0x1U, 0x00000000U, 0x0U, 0x020E02ECU
#define IOMUXC_GPIO1_IO01_USB_OTG1_OCL \
0x020E0060U, 0x2U, 0x020E0664U, 0x0U, 0x020E02ECU
-
引腳復(fù)用功能設(shè)置函數(shù)
IOMUXC_SetPinMux()
擁有6個入口參數(shù), 但是前五個是通過上面的宏定義自動完成設(shè)置的镜雨。而第6個入口參數(shù)“inputOnfiled”用于設(shè)置是否開啟讀回引腳電平功能嫂侍。
static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
uint32_t muxMode,
uint32_t inputRegister,
uint32_t inputDaisy,
uint32_t configRegister,
uint32_t inputOnfield)
{
*((volatile uint32_t *)muxRegister) =
IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) |\
IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);
if (inputRegister)
{
*((volatile uint32_t *)inputRegister) = \
IOMUXC_SELECT_INPUT_DAISY(inputDaisy);
}
}
-
引腳PAD屬性設(shè)置函數(shù)
IOMUXC_SetPinConfig()
函數(shù)共有6個入口參數(shù),其中前五個是通過上面的宏定義自動完成設(shè)置的。而第6個參數(shù)用于設(shè)置PAD屬性挑宠,根據(jù)每個引腳擁有一個32位PAD屬性寄存器菲盾。第六個參數(shù)就是設(shè)置要填入PAD屬性寄存器的值。
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
uint32_t muxMode,
uint32_t inputRegister,
uint32_t inputDaisy,
uint32_t configRegister,
uint32_t configValue)
{
if (configRegister)
{
*((volatile uint32_t *)configRegister) = configValue;
}
}
代碼屏蔽 #include "fsl_common.h"
六各淀、移植野火PAD屬性配置文件
添加 pad_config.h
懒鉴。
通常情況下一個引腳要設(shè)置8種PAD屬性,而這些屬性只能通過數(shù)字指定碎浇。為簡化PAD屬性設(shè)置野火編寫了一個PAD屬性配置文件 pad_config.h
(embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/pad_config.h)【源碼下載:https://gitee.com/Embedfire/embed_linux_driver_tutorial_imx6_code.git】临谱,這里使用宏定義了引腳可選的PAD屬性值,并且通過宏定義的名字很容易知道宏代表的屬性值:
/* SPEED 帶寬配置 */
#define SPEED_0_LOW_50MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(0)
#define SPEED_1_MEDIUM_100MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(1)
#define SPEED_2_MEDIUM_100MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(2)
#define SPEED_3_MAX_200MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(3)
/* PUE 選擇使用保持器還是上下拉 */
#define PUE_0_KEEPER_SELECTED IOMUXC_SW_PAD_CTL_PAD_PUE(0)
#define PUE_1_PULL_SELECTED IOMUXC_SW_PAD_CTL_PAD_PUE(1)
/* PUS 上下拉配置 */
#define PUS_0_100K_OHM_PULL_DOWN IOMUXC_SW_PAD_CTL_PAD_PUS(0)
#define PUS_1_47K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(1)
#define PUS_2_100K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(2)
#define PUS_3_22K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(3)
完整的代碼請閱讀源文件奴璃,這里只列出了文件“pad_config.h”部分代碼(embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/pad_config.h)【源碼下載:https://gitee.com/Embedfire/embed_linux_driver_tutorial_imx6_code.git】悉默。
七、編寫啟動文件
在 Ubuntu 下創(chuàng)建 start.S
文件用于編寫啟動文件苟穆。
在匯編文件中設(shè)置“棧地址”
并執(zhí)行跳轉(zhuǎn)命令跳轉(zhuǎn)到main函數(shù)
執(zhí)行C代碼麦牺。
7.1 完整代碼
/***********************第一部分*********************/
.text //代碼段
.align 2 //設(shè)置2字節(jié)對齊
.global _start //定義一個全局標(biāo)號
/*************************第二部分*************************/
_start: //程序的開始
b reset //跳轉(zhuǎn)到reset標(biā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)格對齊 */
bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支預(yù)測 */
bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */
mcr p15, 0, r0, c1, c0, 0 /* 將 ARM 寄存器的數(shù)據(jù)寫入到 CP15 協(xié)處理器寄存器中 */
/***********************第四部分*********************/
ldr sp, =0x84000000 //設(shè)置棧地址64M
b main //跳轉(zhuǎn)到main函數(shù)
/***********************第五部分*******************/
/*進(jìn)入死循環(huán)*/
loop:
b loop
7.2 分析代碼
-
第一部分
.text
定義代碼段。
.align 2
設(shè)置字節(jié)對齊鞭缭。
.global _start
生命全局標(biāo)號_start剖膳。
/*************************第一部分*************************/
.text //代碼段
.align 2 //設(shè)置2字節(jié)對齊
.global _start //定義一個全局標(biāo)號
-
第二部分
_start:
定義標(biāo)號_start: ,它位于匯編的最前面岭辣,說以會首先被執(zhí)行吱晒。
b reset
使用b指令將程序跳轉(zhuǎn)到reset標(biāo)號處。
/*************************第二部分*************************/
_start: //程序的開始
b reset //跳轉(zhuǎn)到reset標(biāo)號處
-
第三部分
通過修改CP15寄存器(系統(tǒng)控制寄存器)
關(guān)閉 I Cache 沦童、D Cache仑濒、MMU 等等。
我們暫時用不到的功能偷遗,如果開啟可能會影響我們裸機(jī)運(yùn)行墩瞳,為避免不必要的麻煩暫時關(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)格對齊 */
bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支預(yù)測 */
bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */
mcr p15, 0, r0, c1, c0, 0 /* 將 ARM 寄存器的數(shù)據(jù)寫入到 CP15 協(xié)處理器寄存器中 */
-
第四部分
ldr sp, =0x84000000
用于設(shè)置棧指針氏豌。野火i.MX6ULL開發(fā)板標(biāo)配512M的DDR內(nèi)存喉酌,裸機(jī)開發(fā)用不了這么多。程序中我們將棧地址設(shè)置到DDR的64M地址處泵喘。 這個值也可以根據(jù)需要自行定義泪电。
b main
只用跳轉(zhuǎn)指令跳轉(zhuǎn)到main函數(shù)中執(zhí)行。
/***********************第四部分*********************/
ldr sp, =0x84000000 //設(shè)置棧地址64M
b main //跳轉(zhuǎn)到main函數(shù)
-
第五部分
b loop
是“無返回”的跳轉(zhuǎn)指令纪铺。正常情況下相速,不會執(zhí)行第五部分代碼。
/***********************第五部分*******************/
/*進(jìn)入死循環(huán)*/
loop:
b loop
八鲜锚、編寫鏈接腳本
寫好的代碼(無論是匯編還是C語言)都要經(jīng)過編譯突诬、匯編苫拍、鏈接等步驟生成二進(jìn)制文件或者可供下載的文件。在編譯階編譯器會對每個源文件進(jìn)行語法檢查并生成對應(yīng)的匯編語言旺隙,匯編是將匯編文件轉(zhuǎn)化為機(jī)器碼绒极。
使用
arm-none-eabi-gcc -g -c led.S -o led.o
命令完成源碼的編譯、匯編工作催束,生成了.o
文件集峦。編譯和匯編是針對單個源文件,也就編譯完成后一個源文件(.c
抠刺,.S
或.s
)對應(yīng)一個.o
文件塔淤。程序鏈接階段就會將這些.o
鏈接成一個文件。鏈接腳本的作用就是告訴編譯器怎么鏈接這些文件速妖,比如那個文件放在最前面高蜂,程序的代碼段、數(shù)據(jù)段罕容、bss段分別放在什么位置等等备恤。
在 Ubuntu 下創(chuàng)建 button.lds
鏈接腳本。
8.1 完整代碼
ENTRY(_start)
SECTIONS {
. = 0x80000000;
. = ALIGN(4);
.text :
{
start.o (.text)
*(.text)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
.bss :
{
*(.bss)
}
}
8.2 分析代碼
-
指定程序的入口
ENTRY(_start)
用于指定程序的入口锦秒,ENTRY()
是設(shè)置入口地址的命令露泊,“_start”
是程序的入口,led程序的入口地址位于start.S
的“_start”
標(biāo)號處旅择。
ENTRY(_start)
-
定義SECTIONS
SECTIONS
可以理解為是一塊區(qū)域惭笑,我們在這塊區(qū)域排布我們的代碼,鏈接時鏈接器就會按照這里的指示鏈接我們的代碼生真。
SECTIONS {
···
···
}
-
定義鏈接起始地址
“.”
運(yùn)算符代表當(dāng)前位置沉噩。 我們在SECTION的最開始使用“.= 0x80000000”
就是將鏈接起始地址設(shè)置為0x80000000。
. = 0x80000000;
設(shè)置字節(jié)對齊
“. = ALIGN(4);”
它表示從當(dāng)前位置開始執(zhí)行四字節(jié)對齊柱蟀。假設(shè)當(dāng)前位置為0x80000001川蒙,執(zhí)行該命令后當(dāng)前地址將會空出三個字節(jié)轉(zhuǎn)到0x80000004地址處。設(shè)置代碼段
“.text :”
用于定義代碼段长已,固定的語法要求畜眨,我們按照要求寫即可。在“{}”中指定那些內(nèi)容放在代碼段痰哨。
將start.o
中的代碼放到代碼段的最前面胶果。start.S
是啟動代碼應(yīng)當(dāng)首先被執(zhí)行,所以通常情況下要把它放到代碼段的最前面斤斧,其他源文件的代碼按照系統(tǒng)默認(rèn)的排放順序即可,通配符“*”
在這里表示其他剩余所有的.o
文件霎烙。
. = ALIGN(4);
.text :
{
start.o (.text)
*(.text)
}
-
設(shè)置數(shù)據(jù)段
同設(shè)置代碼段類似撬讽,首先設(shè)置字節(jié)對齊蕊连,然后定義代碼段。在數(shù)據(jù)段里使用“*”
通配符游昼, 將所有源文件中的代碼添加到這個數(shù)據(jù)段中甘苍。
. = ALIGN(4);
.data :
{
*(.data)
}
-
設(shè)置BSS段
設(shè)置方法與設(shè)置數(shù)據(jù)段完全相同。
. = ALIGN(4);
.bss :
{
*(.bss)
}
九烘豌、編寫makefile文件
程序編寫完成后需要依次輸入編譯载庭、鏈接、格式轉(zhuǎn)換命令才能最終生成二進(jìn)制文件廊佩。這種編譯方式效率低囚聚、容易出錯。
使用
makefile
只需要在所在文件夾下執(zhí)行make
命令标锄,makefile工具便會自動完成程序的編譯顽铸、鏈接、格式轉(zhuǎn)換等工作料皇。正常情況下我們可以在當(dāng)前目錄看到生成的一些中間文件以及我們期待的.bin
文件谓松。
在 Ubuntu 下創(chuàng)建 makefile
文件。
9.1 完整代碼
all: start.o button.o
arm-none-eabi-ld -Tbutton.lds $^ -o button.elf
arm-none-eabi-objcopy -O binary -S -g button.elf button.bin
%.o : %.S
arm-none-eabi-gcc -g -c $^ -o start.o
%.o : %.c
arm-none-eabi-gcc -g -c $^ -o button.o
.PHONY: clean
clean:
rm *.o *.elf *.bin
9.2 分析代碼
- 添加最終目標(biāo)以及依賴文件
all: start.o button.o
-
添加鏈接命令
“-Tled.lds”
表示使用led.lds鏈接腳本鏈接程序践剂。
“$^”
代表所有的依賴文件鬼譬。
“-o”
指定輸出文件名。
arm-none-eabi-ld -Tbutton.lds $^ -o button.elf
-
添加格式轉(zhuǎn)換命令
“-O binary”
指定輸出二進(jìn)制文件逊脯。
“-S”
不從源文件中復(fù)制重定位信息和符號信息优质。
“-g”
不從源文件中復(fù)制可調(diào)試信息。
arm-none-eabi-objcopy -O binary -S -g button.elf button.bin
-
添加匯編文件編譯命令
“$^”
替代要編譯的源文件男窟。
%.o : %.S
arm-none-eabi-gcc -g -c $^ -o start.o
-
添加編譯C文件的命令
“$^”
替代要編譯的源文件盆赤。
%.o : %.c
arm-none-eabi-gcc -g -c $^ -o button.o
-
添加清理命令
“.PHONY”
定義了偽目標(biāo)“clean”。偽目標(biāo)一般沒有依賴歉眷,并且“clean”
偽目標(biāo)一般放在Makefile文件的末尾牺六。
“clean”
為目標(biāo)用于刪除make生成的文件。
.PHONY: clean
clean:
rm *.o *.elf *.bin
十汗捡、編寫C語言代碼
在 Ubuntu 下創(chuàng)建 button.c
文件用于查詢按鍵控制 LED淑际。
10.1 完整代碼
/*************************第一部分************************/
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "pad_config.h"
/*************************第二部分************************/
/*LED GPIO端口、引腳號及IOMUXC復(fù)用宏定義*/
#define RGB_RED_LED_GPIO GPIO1
#define RGB_RED_LED_GPIO_PIN (4U)
#define RGB_RED_LED_IOMUXC IOMUXC_GPIO1_IO04_GPIO1_IO04
#define RGB_GREEN_LED_GPIO GPIO4
#define RGB_GREEN_LED_GPIO_PIN (20U)
#define RGB_GREEN_LED_IOMUXC IOMUXC_CSI_HSYNC_GPIO4_IO20
#define RGB_BLUE_LED_GPIO GPIO4
#define RGB_BLUE_LED_GPIO_PIN (19U)
#define RGB_BLUE_LED_IOMUXC IOMUXC_CSI_VSYNC_GPIO4_IO19
/*按鍵2 GPIO端口扇住、引腳號及IOMUXC復(fù)用宏定義*/
#define button2_GPIO GPIO5
#define button2_GPIO_PIN (1U)
#define button2_IOMUXC IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01
/*************************第三部分************************/
/* 按鍵PAD配置 */
#define button_PAD_CONFIG_DATA (SRE_0_SLOW_SLEW_RATE| \
DSE_6_R0_6| \
SPEED_2_MEDIUM_100MHz| \
ODE_0_OPEN_DRAIN_DISABLED| \
PKE_0_PULL_KEEPER_DISABLED| \
PUE_0_KEEPER_SELECTED| \
PUS_0_100K_OHM_PULL_DOWN| \
HYS_1_HYSTERESIS_ENABLED)
/* 配置說明 : */
/* 轉(zhuǎn)換速率: 轉(zhuǎn)換速率慢
驅(qū)動強(qiáng)度: R0/6
帶寬配置 : medium(100MHz)
開漏配置: 關(guān)閉
拉/保持器配置: 關(guān)閉
拉/保持器選擇: 保持器(上面已關(guān)閉春缕,配置無效)
上拉/下拉選擇: 100K歐姆下拉(上面已關(guān)閉,配置無效)
滯回器配置: 開啟 */
/* 所有引腳均使用同樣的PAD配置 */
#define LED_PAD_CONFIG_DATA (SRE_0_SLOW_SLEW_RATE| \
DSE_6_R0_6| \
SPEED_2_MEDIUM_100MHz| \
ODE_0_OPEN_DRAIN_DISABLED| \
PKE_0_PULL_KEEPER_DISABLED| \
PUE_0_KEEPER_SELECTED| \
PUS_0_100K_OHM_PULL_DOWN| \
HYS_0_HYSTERESIS_DISABLED)
/* 配置說明 : */
/* 轉(zhuǎn)換速率: 轉(zhuǎn)換速率慢
驅(qū)動強(qiáng)度: R0/6
帶寬配置 : medium(100MHz)
開漏配置: 關(guān)閉
拉/保持器配置: 關(guān)閉
拉/保持器選擇: 保持器(上面已關(guān)閉艘蹋,配置無效)
上拉/下拉選擇: 100K歐姆下拉(上面已關(guān)閉轴合,配置無效)
滯回器配置: 關(guān)閉 */
/*簡單延時函數(shù)*/
void delay(uint32_t count)
{
volatile uint32_t i = 0;
for (i = 0; i < count; ++i)
{
__asm("NOP"); /* 調(diào)用nop空指令 */
}
}
int main()
{
/*led初始化*/
CCM_CCGR1_CG13(0x3);//開啟GPIO1的時鐘
CCM_CCGR3_CG6(0x3); //開啟GPIO4的時鐘
/*設(shè)置 紅燈 引腳的復(fù)用功能以及PAD屬性*/
IOMUXC_SetPinMux(RGB_RED_LED_IOMUXC,0);
IOMUXC_SetPinConfig(RGB_RED_LED_IOMUXC, LED_PAD_CONFIG_DATA);
/*設(shè)置 綠燈 引腳的復(fù)用功能以及PAD屬性*/
IOMUXC_SetPinMux(RGB_GREEN_LED_IOMUXC,0);
IOMUXC_SetPinConfig(RGB_GREEN_LED_IOMUXC, LED_PAD_CONFIG_DATA);
/*設(shè)置 藍(lán)燈 引腳的復(fù)用功能以及PAD屬性*/
IOMUXC_SetPinMux(button2_IOMUXC,0);
IOMUXC_SetPinConfig(button2_IOMUXC, LED_PAD_CONFIG_DATA);
GPIO1->GDIR |= (1<<4); //設(shè)置GPIO1_04為輸出模式
GPIO1->DR |= (1<<4); //設(shè)置GPIO1_04輸出電平為高電平
GPIO4->GDIR |= (1<<20); //設(shè)置GPIO4_20為輸出模式
GPIO4->DR |= (1<<20); //設(shè)置GPIO4_20輸出電平為高電平
GPIO4->GDIR |= (1<<19); //設(shè)置GPIO4_19為輸出模式
GPIO4->DR |= (1<<19); //設(shè)置GPIO4_19輸出電平為高電平
/*************************第四部分************************/
/*按鍵初始化*/
CCM_CCGR1_CG15(0x3); //開啟GPIO5的時鐘
/*************************第五部分************************/
/*設(shè)置 綠燈 引腳的復(fù)用功能以及PAD屬性*/
IOMUXC_SetPinMux(RGB_GREEN_LED_IOMUXC,0);
IOMUXC_SetPinConfig(RGB_GREEN_LED_IOMUXC, button_PAD_CONFIG_DATA);
/*************************第六部分************************/
GPIO5->GDIR &= ~(1<<1); //設(shè)置GPIO5_01為輸入模式
/*************************第七部分************************/
while(1)
{
if((GPIO5->DR)&(1<<1))
{
delay(0xFF);
if((GPIO5->DR)&(1<<1))
{
/*有按鍵按下地消,執(zhí)行綠色led燈翻轉(zhuǎn)*/
if((GPIO4->DR)&(1<<20))
{
GPIO4->DR &= ~(1<<20); //綠燈亮
while((GPIO5->DR)&(1<<1));//等待按鍵松開
}
else
{
GPIO4->DR |= (1<<20); //綠燈滅
while((GPIO5->DR)&(1<<1));//等待按鍵松開
}
}
}
}
return 0;
}
10.2 分析代碼
-
第一部分:添加頭文件
文件“MCIMX6Y2.h”
和“fsl_iomuxc.h”
來自SDK。 文件“pad_config.h”
是野火編寫的文件裆针,在其他工程中可直接使用。
/*************************第一部分************************/
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "pad_config.h"
- 第二部分:定義按鍵使用到的引腳
/*************************第二部分************************/
#define button2_GPIO GPIO5
#define button2_GPIO_PIN (1U)
#define button2_IOMUXC IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01
-
第三部分:設(shè)置按鍵引腳的PAD屬性
PAD屬性宏定義保存在“pad_config.h”
文件中,這里使用“|”
運(yùn)算符將所有屬性設(shè)置“合并”在一起,后面將作為函數(shù)參數(shù)。與LED燈引腳PAD屬性差別是這里開啟了滯回器的功能惹盼。
/*************************第三部分************************/
/* 按鍵PAD配置 */
#define button_PAD_CONFIG_DATA (SRE_0_SLOW_SLEW_RATE| \
DSE_6_R0_6| \
SPEED_2_MEDIUM_100MHz| \
ODE_0_OPEN_DRAIN_DISABLED| \
PKE_0_PULL_KEEPER_DISABLED| \
PUE_0_KEEPER_SELECTED| \
PUS_0_100K_OHM_PULL_DOWN| \
HYS_1_HYSTERESIS_ENABLED)
/* 配置說明 : */
/* 轉(zhuǎn)換速率: 轉(zhuǎn)換速率慢
驅(qū)動強(qiáng)度: R0/6
帶寬配置 : medium(100MHz)
開漏配置: 關(guān)閉
拉/保持器配置: 關(guān)閉
拉/保持器選擇: 保持器(上面已關(guān)閉,配置無效)
上拉/下拉選擇: 100K歐姆下拉(上面已關(guān)閉惫确,配置無效)
滯回器配置: 開啟 */
按鍵機(jī)械觸點(diǎn)斷開手报、閉合時,由于觸點(diǎn)的彈性作用改化,按鍵開關(guān)不會馬上穩(wěn)定接通或一下子斷開掩蛤,使用按鍵時會產(chǎn)生下圖中的帶波紋信號,需要用軟件消抖處理濾波所袁,不方便輸入檢測盏档。
不過i.MX6U的GPIO引腳帶有施密特觸發(fā)器功能,使用該功能可以對信號實(shí)現(xiàn)硬件消抖處理燥爷, 從而簡化了軟件的工作蜈亩,軟件只需要直接檢測引腳的電平即可。
- 第四部分:開啟GPIO時鐘
/*************************第四部分************************/
/*按鍵初始化*/
CCM_CCGR1_CG15(0x3); //開啟GPIO5的時鐘
- 第五部分:設(shè)置引腳的復(fù)用功能以及引腳PAD屬性
/*************************第五部分************************/
IOMUXC_SetPinMux(RGB_GREEN_LED_IOMUXC,0);
IOMUXC_SetPinConfig(RGB_GREEN_LED_IOMUXC, button_PAD_CONFIG_DATA);
- 第六部分:設(shè)置GPIO為輸入
/*************************第六部分************************/
GPIO5->GDIR &= ~(1<<1); //設(shè)置GPIO5_01為輸入模式
-
第七部分:在while(1)死循環(huán)中檢測按鍵的狀態(tài)
如果按鍵按下則翻轉(zhuǎn)綠燈狀態(tài)并等待按鍵松開前翎。
/*************************第七部分************************/
while(1)
{
if((GPIO5->DR)&(1<<1))
{
delay(0xFF);
if((GPIO5->DR)&(1<<1))
{
/*有按鍵按下稚配,執(zhí)行綠色led燈翻轉(zhuǎn)*/
if((GPIO4->DR)&(1<<20))
{
GPIO4->DR &= ~(1<<20); //綠燈亮
while((GPIO5->DR)&(1<<1));//等待按鍵松開
}
else
{
GPIO4->DR |= (1<<20); //綠燈滅
while((GPIO5->DR)&(1<<1));//等待按鍵松開
}
}
}
}
十一、編譯下載驗(yàn)證
11.1 編譯代碼
make
執(zhí)行make命令港华,生成button.bin文件道川。
11.2 代碼燒寫
編譯成功后會在當(dāng)前文件夾下生成.bin文件,這個.bin文件也不能直接放到開發(fā)板上運(yùn)行立宜, 這次是因?yàn)樾枰?bin文件缺少啟動相關(guān)信息冒萄。
為二進(jìn)制文件添加頭部信息并燒寫到SD卡。查看 IMX6ULL學(xué)習(xí)筆記(12)——通過SD卡啟動官方SDK程序
進(jìn)入燒寫工具目錄橙数,執(zhí)行 ./mkimage.sh <燒寫文件路徑>
命令尊流,例如要燒寫的 button.bin 位于 home 目錄下,則燒寫命令為 ./mkimage.sh /home/button.bin
灯帮。
執(zhí)行上一步后會列出linux下可燒寫的磁盤崖技,選擇你插入的SD卡即可。這一步 非常危險V痈纭S住!一定要確定選擇的是你插入的SD卡D宸 吁恍!,如果選錯很可能破壞你電腦磁盤內(nèi)容,造成數(shù)據(jù)損壞<巍Q徊伞宾巍! 確定磁盤后SD卡以“sd”開頭咕幻,選擇“sd”后面的字符即可。例如要燒寫的sd卡是“sdb”則輸入“b”即可顶霞。
11.3 實(shí)驗(yàn)現(xiàn)象
將開發(fā)板設(shè)置為SD卡啟動肄程,接入SD卡,開發(fā)板上電选浑,按下KEY按鍵蓝厌,LED綠燈亮,再按則綠燈滅古徒。
? 由 Leung 寫于 2022 年 12 月 26 日
? 參考:8. GPIO輸入—按鍵查詢檢測