94 .4 GPIO 時鐘 時鐘 如果使用 GPIO,我們必須要使能 GPIO 的時鐘。i.MX6 ULL 的每個外設(shè)的時鐘可以獨(dú)立的使能,我們可以關(guān)閉不使用的外設(shè)時鐘,可以達(dá)到節(jié)能的目的。如果使用某個外設(shè),我們必須要打開對應(yīng)的時鐘。《I.MX6ULL 參考手冊》的第 18 章“Clock Controller Module (CCM)”是關(guān)于 i.MX6ULL 時鐘的講解,我們可以看下該章節(jié)里面外設(shè)時鐘的使能寄存器。跟外設(shè)時鐘使能相關(guān)的寄存器有: CCM_CCGR0 CCM_CCGR1 CCM_CCGR2 CCM_CCGR3 CCM_CCGR4 CCM_CCGR5 CCM_CCGR6 一共 7 個。我們來看下 CCM_CCGR0 寄存器如何使能一個外設(shè)時鐘,改寄存器的描述如下圖所示: 我們從上圖可以看到 CCM_CCGR0 寄存器是 32 位的,每 2 位控制一個外設(shè)時鐘,比如 bit1:bit0 控制aips_tz1 的時鐘,兩位的操作方式如下: 00 //所有模式下都關(guān)閉外設(shè)時鐘 01 //只有在運(yùn)行模式下打開外設(shè)時鐘 10 //保留 11 //除了停止模式以外,其他所有模式下時鐘都打開 如果我們要打開 aips_tz1 的外設(shè)時鐘,需要設(shè)置 CCM_CCGR0 的 bit1 和 bit0 都為 1,也就是 CCM_CCGR0=3,如果關(guān)閉 aips_tz1 的外設(shè)時鐘,CCM_CCGR0 的 bit1 和 bit0 都設(shè)置為 0。CCM_CCGR0-CCM_CCGR6 這 7 個寄存器的功能都是類似的,也是每兩位控制一種外設(shè)的時鐘,為了便于開發(fā),我們在后面的例程里把所有的外設(shè)時鐘都使能了。至此關(guān)于 GPIO 的操作我們可以總結(jié)成下面的步驟: 1.使能 GPIO 對應(yīng)的時鐘 2.設(shè)置 IOMUXC_SW_MUX_CTL_PAD_XX_XX 寄存器,把對應(yīng)的 IO 設(shè)置成 GPIO 功能 3.設(shè)置 IOMUXC_SW_PAD_CTL_PAD_XX_XX 寄存器,設(shè)置 GPIO 的上拉下拉,以及驅(qū)動能力 4.設(shè)置輸入還是輸出,是否使用中斷,以及默認(rèn)的輸出電平 i.MX6ULL 終結(jié)者開發(fā)板板載了一個 LED 燈,打開底板的原理圖(光盤資料的“i.MX6UL 終結(jié)者光盤資料\03_開發(fā)板硬件資料\01_開發(fā)板原理圖\pdf\ itopmx6ul_terminator_v1_0.pdf”), 原理圖如下圖所示: 從上圖可以看到 LED2 一段接到了 3.3V 電源,另一端接到了 GPIO_3(CPU 的 GPIO1_IO03)引腳上,當(dāng)GPIO1_IO03 引腳輸出低電平(0)的時候,LED2 會被點(diǎn)亮,當(dāng) GPIO1_IO03 引腳輸出高電平(1)的時候,LED2 會滅掉。 9 96 .6 LED 匯編程序 匯編程序 按照前面的介紹,我們需要對 GPIO1_IO03 做如下的設(shè)置: 1.使能 GPIO1 的時鐘 GPIO1 的時鐘由 CCM_CCGR1 寄存器的 bit27 和 26 控制,我們把這兩位設(shè)置成 1,就會使能 GPIO1 的時鐘了。 2.配置 GPIO1_IO03 的復(fù)用功能 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器是 GPIO1_IO03 的復(fù)用寄存器,地址是 0x20e0068,把這個寄存器設(shè)置成 GPIO 功能。 3.配置 GPIO1_IO03 上下拉 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 寄存器是 GPIO1_IO03 的配置寄存器,通過該寄存器配置 GPIO1_IO03為 GPIO 的上下拉。 4.設(shè)置 GPIO1_IO03 輸出 在第二步驟我們已經(jīng)把 GPIO1_IO03 復(fù)用成 GPIO 了,然后我們需要配置 GPIO 為輸出,以及輸出時候的高低電平,在《IMX6ULL 參考手冊》的 1357 頁我們可以看到 GPIO1_IO03 對應(yīng)的 GPIO 寄存器組,如下圖所示: 通過設(shè)置寄存器 GPIO1_GDIR 的 bit3 為 1,可以設(shè)置 GPIO1_IO03 為輸出。然后通過設(shè)置 GPIO1_DR 寄存器的bit3為0,可以控制GPIO1_IO03輸出低電平,LED會亮。GPIO1_DR的bit3設(shè)置為1,可以控制GPIO1_IO03輸出高電平,LED 會滅。 本實驗的源碼在開發(fā)板光盤資料的"i.MX6UL 終結(jié)者光盤資料\04_裸機(jī)例程源碼\1_leds\1_leds"目錄下,我們可以把這個文件夾拷貝到 UBuntu 下,使用交叉編譯器編譯。 下面我們開始一步步的實現(xiàn) LED 這個實驗。首先登錄 Ubuntu 系統(tǒng),在用戶的根目錄下的 work 文件夾創(chuàng)建led 文件夾然后在 led 目錄下新建“led.s”的匯編文件,如下圖所示: 然后使用“vi led.s”命令打開 led.s 文件,在 led.s 文件輸入如下代碼: 1 .global _start /* 全局標(biāo)號 */ 2 /* 3 * _start 函數(shù),程序從此函數(shù)開始執(zhí)行此函數(shù)完成時鐘使能、 4 * GPIO 初始化、最終控制 GPIO 輸出低電平來點(diǎn)亮 LED 燈。 5 */ 6 _start: 7 /* 1、使能所有時鐘 */ 8 ldr r0, =0X020C4068 /* CCGR0 */ 9 ldr r1, =0XFFFFFFFF 10 str r1, [r0] 11 ldr r0, =0X020C406C /* CCGR1 */ 12 str r1, [r0] 13 ldr r0, =0X020C4070 /* CCGR2 */ 14 str r1, [r0] 15 ldr r0, =0X020C4074 /* CCGR3 */ 16 str r1, [r0] 17 ldr r0, =0X020C4078 /* CCGR4 */ 18 str r1, [r0] 19 ldr r0, =0X020C407C /* CCGR5 */ 20 str r1, [r0] 21 ldr r0, =0X020C4080 /* CCGR6 */ 22 str r1, [r0] 23 /* 設(shè)置 GPIO1_IO03 為 GPIO 模式 */ 24 ldr r0, =0X020E0068 /* 把寄存器 SW_MUX_GPIO1_IO03_BASE 加載到 r0 中 */ 25 ldr r1, =0X5 /* 設(shè)置寄存器 SW_MUX_GPIO1_IO03_BASE 的 MUX_MODE 為 5 */ 26 str r1,[r0] 27 /* 配置 GPIO1_IO03 的 IO 屬性 28 *bit 16:0 HYS 關(guān)閉 29 *bit [15:14]: 00 默認(rèn)下拉 30 *bit [13]: 0 kepper 功能 31 *bit [12]: 1 pull/keeper 使能 32 *bit [11]: 0 關(guān)閉開路輸出 33 *bit [7:6]: 10 速度 100Mhz 34 *bit [5:3]: 110 R0/6 驅(qū)動能力 35 *bit [0]: 0 低轉(zhuǎn)換率 36 */ 37 ldr r0, =0X020E02F4 /*寄存器 SW_PAD_GPIO1_IO03_BASE */ 38 ldr r1, =0X10B0 39 str r1,[r0] 40 /* 設(shè)置 GPIO1_IO03 為輸出 */ 41 ldr r0, =0X0209C004 /*寄存器 GPIO1_GDIR */ 42 ldr r1, =0X0000008 43 str r1,[r0] 44 /* 點(diǎn)亮 LED0 45 * 設(shè)置 GPIO1_IO03 輸出低電平 46 */ 47 ldr r0, =0X0209C000 /*寄存器 GPIO1_DR */ 48 ldr r1, =0 49 str r1,[r0] 50 /* 51 * 描述: loop 死循環(huán) 52 */ 53 loop: 54 b loop 第 2 行我們定義了全局標(biāo)號_start,程序就是從_start 標(biāo)號處開始順序往下執(zhí)行的。 第 8 行使用 ldr 向寄存器 r0 寫入 0x20c4068,這是 CCM_CCGR0 的寄存器地址。 第 9 行使用 ldr 向寄存器 r1 寫入 0xffffffff,開啟所有外設(shè)的時鐘。 第 10 行使用 str 命令把 r1 中的值寫到 r0 所保存的地址中,相當(dāng)于給 0x20c4068 這個地址寫入 0xffffffff, 也就是 CCM_CCGR0 寄存器設(shè)置成 0xffffffff,使能 CCM_CCGR0 寄存器控制的所有外設(shè)時鐘。 第 11 行到第 22 行是向 CCM_CCGR1-CCM_CCGR6 寄存器寫入 0xffffffff,使能所有的外設(shè)時鐘。 第 24 行到第 26 行是設(shè)置 GPIO1_IO03 這個 IO 為 GPIO 模式,GPIO1_IO03 的復(fù)用寄存器是 0x20e0068,把改 寄存器的 MUX_MODE 設(shè)置為 5(GPIO 模式) 第 37 行到第 39 行設(shè)置 GPIO1_IO03 的 IOMUX_SW_PAD_CTL_PAD_GPIO1_IO03 配置寄存器 第 40 行到第 43 行是設(shè)置 GPIO 為輸出功能。 第 47 到第 49 是設(shè)置 GPIO 輸出 0(低電平) 第 53 到第 54 行是死循環(huán),使用 b 跳轉(zhuǎn)指令,程序不斷的跳轉(zhuǎn)到 loop 執(zhí)行。 9.7 編譯 LED 匯編程序 匯編程序 我們在 Ubuntu 下通過 vim 編輯好 LED 的匯編程序(6.6 章節(jié)),然后保存并退出。然后我們使用 arm交叉編譯器來編譯該程序,我們在終端輸入“arm-linux-gnueabihf-gcc -g -c led.s -o led.o”命令,把 led.s 編譯成 led.o,其中的“-g”參數(shù)是產(chǎn)生調(diào)試信息,可以使用 GDB 來調(diào)試代碼。“-c”參數(shù)是編譯源文件,不鏈接。“-o”參數(shù)是指定產(chǎn)生的文件名稱,我們指定生成 led.o,運(yùn)行效果如下圖所示所示: 我們可以看到通過 arm 交叉編譯生成了 led.o 文件,這個文件相當(dāng)于中間文件,我們還需要把編譯生成的.o文件鏈接起來生成可執(zhí)行文件(我們這里只有一個 led.o,對于有的工程可能會生成多個.o 文件)。 接下來我們使用交叉編譯器的“arm-linux-gnueabihf-ld”命令來把“.o”文件鏈接起來。鏈接的目的就是把我們的程序固定到某個地址,這樣 cpu 在運(yùn)行的時候就可以通過鏈接地址找到我們的程序,并運(yùn)行它。在鏈接之前我們需要先了解下 i.MX6ULL 的啟動,i.MX6ULL 支持 SD 卡,EMMC,NAND 等方式啟動,i.MX6ULL啟動的時候首先將代碼從 SD 卡,EMMC,NAND 中拷貝到運(yùn)行地址,然后開始從運(yùn)行地址處開始運(yùn)行,i.MX6ULL 芯片內(nèi)部有 128K 的 RAM(0X900000~0X91FFFF),另外外部擴(kuò)展了 DDR,所以 i.MX6ULL 的鏈接地址可以是內(nèi)部的 RAM,也可以是外部的 DDR。我們所有的例程都是鏈接到 DDR 中,其地址為 0X87800000。i.MX6ULL終結(jié)者開發(fā)板的 DDR 有兩種:256MB 和 512MB,其實地址都是 0X80000000。256MB 的終止地址是 0X8FFFFFFF,512M 的終止地址是 0X9FFFFFFF,之所以選擇地址 0X87800000,是因為后面我們要學(xué)的 Uboot 鏈接地址也是0X87800000。所以為了學(xué)習(xí)方便,我們統(tǒng)一使用 0X87800000 地址。 下面我們開始使用 arm-linux-gnueabihf_ld 命令將前面我們生成的“led.o”文件鏈接到 0X87800000 地址處,我們在終端輸入“arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf”,其中的“-Ttext” 指定鏈接地址(0X87800000),“-o”生成鏈接文件名,運(yùn)行效果如下圖所示: 我們需要把鏈接文件轉(zhuǎn)換成“.bin”文件,然后燒寫到 EMMC,才能運(yùn)行。 接下來我們使用“arm-linux-gnueabihf-objcopy”命令將鏈接文件 led.elf 轉(zhuǎn)換成“led.bin”文件。我們在終端輸入“arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin”命令,其中“-O”指定以什么格式輸出(binary 表示二進(jìn)制輸出),“-S”表示不要賦值源文件中的重定位信息和符號信息,“-g”表示不復(fù)制源文件中的調(diào)試信息,運(yùn)行效果如下圖所示: 至此我們生成了最終的可執(zhí)行程序“led.bin”文件。 我們可以總結(jié)下,為了生成 led.bin 文件,我們分別使用了命令: arm-linux-gnueabihf-gcc -g -c led.s -o led.o arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin 為了編譯方便,我們可以使用 Makefile 來編譯我們的 led 匯編程序,首先我們在 led 工程目錄下使用“touch Makefile”命令創(chuàng)建 Makefile 文件,如下圖所示: 然后使用 vim 編輯器打開 Makefile 文件,輸入下面的命令: led.bin:led.s arm-linux-gnueabihf-gcc -g -c led.s -o led.o arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin clean: rm -rf *.o led.bin led.elf 添加完上面的命令,保存并退出,然后在 led 工程目錄下執(zhí)行“make”命令編譯 led.S,過程如下圖所示: 如果要清除工程,我們在終端執(zhí)行“make clean”即可。 至此關(guān)于 arm 交叉編譯器的使用,我們就先介紹到這里。編譯生成了 led.bin 執(zhí)行文件,下一步我們還需要給 led.bim 文件添加一些數(shù)據(jù)頭才能執(zhí)行。我們?yōu)橛脩籼峁┝颂砑訑?shù)據(jù)頭的工具“create_imx”(光盤資料的“i.MX6UL 終結(jié)者光盤資料\01_開發(fā)及燒寫工具\2.裸機(jī)鏡像制作工具”目錄下),我們通過 ssh工具拷貝改文件到 led 工程目錄下,然后在終端輸入命令“./create_imx led.bin”,生成 bare.imx 文件,如下圖所示: 現(xiàn)在我們開始使用 MFG 燒寫工具來燒寫(光盤資料的“i.MX6UL 終結(jié)者光盤資料\01_開發(fā)及燒寫工具\3.mfgtools_for_6ULL”文件夾),首先我們進(jìn)到該文件夾,我們修改“cfg.ini”文件,如果您的板子是EMMC 版本(8G flash 容量),按照下圖的方式修改: 如果您的板子是 NAND 版本(512MB flash 容量)(NAND 版本的裸機(jī)驗證我們需要使用一張 TF 卡),按照下圖所示修改: 修改完“ cfg.ini”配置文件,然后我們在 Ubuntu 系統(tǒng)生成的“bare.imx”文件通過 ssh 工具拷貝到 MFG燒寫工具的“Profiles\Linux\OS Firmware\files\linux\”目錄下,如下圖所示: 然后我們鼠標(biāo)雙擊打開 MFG 燒寫工具,如下圖所示: MFG 燒寫工具打開以后,我們使用開發(fā)板配帶的 USB 數(shù)據(jù)線,連接開發(fā)板的 OTG 接口和 PC 的 USB 接口,使用開發(fā)板配帶的電源連接到開發(fā)板的電源接口,然后開發(fā)板的撥碼開關(guān)設(shè)置成 USB 啟動,如下圖所示:( 如果我們使用的是 D NAND 版本的開發(fā)板,我們需要先拔掉 F TF 卡)然后按下開發(fā)板的電源開關(guān),使開發(fā)板上電,此時我們會看到 MFG 燒寫工具識別到開發(fā)板,如下圖所示: ( 如果我們使用的是 D NAND 版本的開發(fā)板, , 我們需要插入 F TF 卡),然后我們點(diǎn)擊 MFG 燒寫工具的“Start”按鈕,開始燒寫鏡像,如下圖所示: 等到進(jìn)度條顯示綠色,燒寫完成,如下圖所示: 然后我們在按下開發(fā)板的電源按鍵給開發(fā)板斷電,然后修改撥碼開關(guān)設(shè)置正常啟動。 如果您的板子是 EMMC 版本(8GB Flash 存儲),撥碼開關(guān)如下圖所示: 如果您的開發(fā)板是 NAND 版本(512M Flash 存儲),撥碼開關(guān)如下圖所示(設(shè)置成 TF 卡啟動模式): 最后我們在按下開發(fā)板的電源開關(guān),給開發(fā)板上電,此時我們會看到開發(fā)板的 LED2 被點(diǎn)亮了,如下圖 所示: 本節(jié)我們詳細(xì)的介紹了如何編譯代碼,并且如何使用 MFG 燒寫生成
|