系列文章目錄
I.MX6ULL的官方 SDK 移植實驗
I.MX6ULL的官方 SDK 移植實驗
- 系列文章目錄
- 一、前言
- 二、I.MX6ULL 官方 SDK 包簡介
- 三、硬件原理圖
- 四、試驗程序編寫
- 4.1 SDK 文件移植
- 4.2 創建 cc.h 文件
- 4.3 編寫實驗代碼
- 五、編譯下載驗證
- 5.1編寫 Makefile 和鏈接腳本
- 5.2編譯下載
一、前言
在I.MX6ULL模仿 STM32 驅動開發格式實驗,我們參考 ST 官方給 STM32 編寫的 stm32f10x.h 來自行編寫 I.MX6ULL的寄存器定義文件。自己編寫這些寄存器定義不僅費時費力,沒有任何意義,而且很容易寫錯,**NXP 官方為 I.MX6ULL 編寫了 SDK 包,在 SDK 包里面 NXP 已經編寫好了寄存器定義文件,所以我們可以直接移植 SDK 包里面的文件來用。**雖然 NXP 是為 I.MX6ULL 編寫的 SDK 包,但是 I.MX6UL 也是可以使用的!
二、I.MX6ULL 官方 SDK 包簡介
NXP 針對 I.MX6ULL 編寫了一個 SDK 包,這個 SDK 包就類似于 STM32 的 STD 庫或者
HAL 庫,這個 SDK 包提供了 Windows 和 Linux 兩種版本,分別針對主機系統是 Windows 和Linux。因為我們是在 Windows 下使用 Source Insight 來編寫代碼的,因此我們使用的是 Windows版本的。Windows 版本 SDK 里面的例程提供了 IAR 版本,肯定有人會問既然 NXP 提供了 IAR版本的 SDK,那我們為什么不用 IAR 來完成裸機試驗,偏偏要用復雜的 GCC?因為我們要從簡單的裸機開始掌握 Linux 下的 GCC 開發方法,包括 Ubuntu 操作系統的使用、Makefile 的編寫、shell 等等。如果為了偷懶而使用 IAR 開發裸機的話,那么后續學習 Uboot 移植、Linux 移植和 Linux 驅動開發就會很難上手,因為開發環境都不熟悉!再者,不是所有的半導體廠商都會為 Cortex-A 架構的芯片編寫裸機 SDK 包,我使用過那么多的 Cotex-A 系列芯片,也就發現了 NXP 給 I.MX6ULL 編寫了裸機 SDK 包。而且去 NXP 官網看一下,會發現只有 I.MX6ULL這一款 Cotex-A 內核的芯片有裸機 SDK 包,NXP 的其它 Cotex-A 芯片都沒有。
所以,使用 Cortex-A 內核芯片的時候不要想著有類似 STM32 庫一樣的東西,I.MX6ULL 是一個特例,基本所有的 Cortex-A 內核的芯片都不會提供裸機 SDK 包。因此在使用 STM32 的時候那些用起來很順手的庫文件,在 Cotex-A 芯片下基本都需要我們自行編寫,比如.s 啟動文件、寄存器定義等等。
nxp官網:www.nxp.com
下載圖中的 WIN 版本 SDK,也就是“SDK2.2_iMX6ULL_WIN”
雙擊 SDK_2.2_MCIM6ULL_RFP_Win.exe 安裝 SDK 包,安裝的時候需要設置好安裝位置,安裝完成以后的 SDK 包如圖所示:
重點是需要 SDK 包里面與寄存器定義相關的文件,一共需要如下三個文件:
fsl_common.h:位置為SDK_2.2_MCIM6ULL\devices\MCIMX6Y2\drivers\fsl_common.h。
fsl_iomuxc.h: 位置為 SDK_2.2_MCIM6ULL\devices\MCIMX6Y2\drivers\fsl_iomuxc.h。
MCIMX6Y2.h: 位置為 SDK_2.2_MCIM6ULL\devices\MCIMX6Y2\MCIMX6YH2.h。
整個 SDK 包我們就需要上面這三個文件,把這三個文件準備好,我們后面移植要用。
三、硬件原理圖
I.MX6UALPHA 開發板上有一個 LED 燈,原理圖如下圖所示;
從圖可以看出,LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,當 GPIO1_IO03輸出低電平(0)的時候發光二極管 LED0 就會導通點亮,當 GPIO1_IO03 輸出高電平(1)的時候發光二極管 LED0 不會導通,因此 LED0 也就不會點亮。所以 LED0 的亮滅取決于 GPIO1_IO03的輸出電平,輸出 0 就亮,輸出 1 就滅。
四、試驗程序編寫
4.1 SDK 文件移植
使用 VSCode 新建工程,將 fsl_common.h、fsl_iomuxc.h 和 MCIMX6Y2.h 這三個文件拷貝到工程中,這三個文件直接編譯的話肯定會出錯的!需要對其做刪減,根據make報錯的刪掉對應不需要的函數以及裁剪
4.2 創建 cc.h 文件
新建一個名為 cc.h 的頭文件,cc.h 里面存放一些 SDK 庫文件需要使用到的數據類型,在cc.h 里面輸入如下代碼:
#ifndef __CC_H
#define __CC_H
/*
* 自定義一些數據類型供庫文件使用
*/
#define __I volatile
#define __O volatile
#define __IO volatile
#define ON 1
#define OFF 0
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char s8;
typedef signed short int s16;
typedef signed int s32;
typedef signed long long int s64;
typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned int u32;
typedef unsigned long long int u64;
#endif
在 cc.h 文件中我們定義了很多的數據類型,因為有些第三方庫會用到這些變量類型。
4.3 編寫實驗代碼
新建 start.S 和 main.c 這兩個文件,start.S 文件的內容和I.MX6ULL模仿 STM32 驅動開發格式實驗一樣,直接復制過來就可以,創建完成以后工程目錄如圖所示:
在 main.c 中輸入如下所示代碼:
1 #include "fsl_common.h"
2 #include "fsl_iomuxc.h"
3 #include "MCIMX6Y2.h"
4
5 /*
6 * @description : 使能 I.MX6U 所有外設時鐘
7 * @param : 無
8 * @return : 無
9 */
10 void clk_enable(void)
11 {
12 CCM->CCGR0 = 0XFFFFFFFF;
13 CCM->CCGR1 = 0XFFFFFFFF;
14
15 CCM->CCGR2 = 0XFFFFFFFF;
16 CCM->CCGR3 = 0XFFFFFFFF;
17 CCM->CCGR4 = 0XFFFFFFFF;
18 CCM->CCGR5 = 0XFFFFFFFF;
19 CCM->CCGR6 = 0XFFFFFFFF;
2021 }
22
23 /*
24 * @description : 初始化 LED 對應的 GPIO
25 * @param : 無
26 * @return : 無
27 */
28 void led_init(void)
29 {
30 /* 1、初始化 IO 復用 */
31 IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);
32
33 /* 2、、配置 GPIO1_IO03 的 IO 屬性
34 *bit 16:0 HYS 關閉
35 *bit [15:14]: 00 默認下拉
36 *bit [13]: 0 kepper 功能
37 *bit [12]: 1 pull/keeper 使能
38 *bit [11]: 0 關閉開路輸出
39 *bit [7:6]: 10 速度 100Mhz
40 *bit [5:3]: 110 R0/6 驅動能力
41 *bit [0]: 0 低轉換率
42 */
43 IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0);
44
45 /* 3、初始化 GPIO,設置 GPIO1_IO03 設置為輸出 */
46 GPIO1->GDIR |= (1 << 3);
47
48 /* 4、設置 GPIO1_IO03 輸出低電平,打開 LED0 */
49 GPIO1->DR &= ~(1 << 3);
50 }
51
52 /*
53 * @description : 打開 LED 燈
54 * @param : 無
55 * @return : 無
56 */
57 void led_on(void)
58 {
59 /* 將 GPIO1_DR 的 bit3 清零 */
60 GPIO1->DR &= ~(1<<3);
61 }
62
63 /*
64 * @description : 關閉 LED 燈
65 * @param : 無
66 * @return : 無
67 */
68 void led_off(void)
69 {
70 /* 將 GPIO1_DR 的 bit3 置 1 */
71 GPIO1->DR |= (1<<3);
72 }
73
74 /*
75 * @description : 短時間延時函數
76 * @param - n : 要延時循環次數(空操作循環次數,模式延時)
77 * @return : 無
78 */
79 void delay_short(volatile unsigned int n)
80 {
81 while(n--){}
82 }
83
84 /*
85 * @description : 延時函數,在 396Mhz 的主頻下
86 * 延時時間大約為 1ms
87 * @param - n : 要延時的 ms 數
88 * @return : 無
89 */
90 void delay(volatile unsigned int n)
91 {
92 while(n--)
93 {
94 delay_short(0x7ff);
95 }
96 }
97
98 /*
99 * @description : main 函數
100 * @param : 無
101 * @return : 無
102 */
103 int main(void)
104 {
105 clk_enable(); /* 使能所有的時鐘 */
106 led_init(); /* 初始化 led */
107
108 while(1) /* 死循環 */
109 {
110 led_off(); /* 關閉 LED */
111 delay(500); /* 延時 500ms */
112
113 led_on(); /* 打開 LED */
114 delay(500); /* 延時 500ms */
115 }
116
117 return 0;
118 }
和一樣I.MX6ULL模仿 STM32 驅動開發格式實驗,main.c 有 7 個函數,這 7 個函數的含義都一樣,只是本代碼我們使用的是移植好的 NXP 官方 SDK 里面的寄存器定義。
重點來看一下 led_init 函數中的第 31 行和第 43 行,這兩行的內容如下:
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);
這 里 使 用 了 兩 個 函 數 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig , 其 中 函 數IOMUXC_SetPinMux 是用來設置 IO 復 用 功 能 的 , 最 終 肯 定 設 置 的 是 寄 存 器
“IOMUXC_SW_MUX_CTL_PAD_XX”。函數 IOMUXC_SetPinConfig 設置的是 IO 的上下拉、速度等的,也就是寄存器“IOMUXC_SW_PAD_CTL_PAD_XX”,所以上面兩個函數其實就是I.MX6ULL模仿 STM32 驅動開發格式實驗中的:
IOMUX_SW_MUX->GPIO1_IO03 = 0X5;
IOMUX_SW_PAD->GPIO1_IO03 = 0X10B0;
函數 IOMUXC_SetPinMux 在文件 fsl_iomuxc.h 中定義,函數源碼如下:
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);}
}
函數 IOMUXC_SetPinMux 有 6 個參數,這 6 個參數的函數如下:
muxRegister : IO 的 復 用 寄 存 器 地 址 , 比 如 GPIO1_IO03 的 IO 復 用 寄 存 器
SW_MUX_CTL_PAD_GPIO1_IO03 的地址為 0X020E0068。
muxMode: IO 復用值,也就是 ALT0~ALT8,對應數字 0~8,比如要將 GPIO1_IO03 設置
為 GPIO 功能的話此參數就要設置為 5。
inputRegister:外設輸入 IO 選擇寄存器地址,有些 IO 在設置為其他的復用功能以后還需要設置 IO 輸入寄存器,比如 GPIO1_IO03 要復用為 UART1_RX 的話還需要設置寄存器
UART1_RX_DATA_SELECT_INPUT,此寄存器地址為 0X020E0624。
inputDaisy:寄存器 inputRegister 的值,比如 GPIO1_IO03 要作為 UART1_RX 引腳的話此參數就是 1。
configRegister:未使用,函數 IOMUXC_SetPinConfig 會使用這個寄存器。
inputOnfield : IO 軟件輸入使能,以 GPIO1_IO03 為例就是寄存器
SW_MUX_CTL_PAD_GPIO1_IO03 的 SION 位(bit4)。如果需要使能 GPIO1_IO03 的軟件輸入功能的話此參數應該為 1,否則的話就為 0。
IOMUXC_SetPinMux 的函數體很簡單,就是根據參數對寄存器 muxRegister 和 inputRegister進行賦值。在代碼第 31 行使用此函數將 GPIO1_IO03 的復用功能設置為
GPIO,如下:
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
第一次看到上面代碼的時候肯定會奇怪,為何只有兩個參數?不是應該 6 個參數的嗎?先看 IOMUXC_GPIO1_IO03_GPIO1_IO03 是個宏,在文件fsl_iomuxc.h 中有定義,NXP 的 SDK 庫將一個 IO 的所有復用功能都定義了一個宏,比如GPIO1_IO03 就有如下 9 個宏定義:
IOMUXC_GPIO1_IO03_I2C1_SDA
IOMUXC_GPIO1_IO03_GPT1_COMPARE3
IOMUXC_GPIO1_IO03_USB_OTG2_OC
IOMUXC_GPIO1_IO03_USDHC1_CD_B
IOMUXC_GPIO1_IO03_GPIO1_IO03
IOMUXC_GPIO1_IO03_CCM_DI0_EXT_CLK
IOMUXC_GPIO1_IO03_SRC_TESTER_ACK
IOMUXC_GPIO1_IO03_UART1_RX
IOMUXC_GPIO1_IO03_UART1_TX
上面 9 個宏定義分別對應著 GPIO1_IO03 的九種復用功能,比如復用為 GPIO 的宏定義就是:
#define IOMUXC_GPIO1_IO03_GPIO1_IO03 0x020E0068U, 0x5U, 0x00000000U,
0x0U, 0x020E02F4U
將這個宏帶入到代碼中的 31 行以后就是:
IOMUXC_SetPinMux (0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0);
這樣就與函數 IOMUXC_SetPinMux 的 6 個參數對應起來了,如果我們要將GPIO1_IO03 復用為 I2C1_SDA 的話就可以使用如下代碼:
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_I2C1_SDA, 0);
函數 IOMUXC_SetPinConfig,此函數同樣在文件 fsl_iomuxc.h 中有定義,函數源碼如下:
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;}
}
函數 IOMUXC_SetPinConfig 有 6 個參數,其中前五個參數和函數 IOMUXC_SetPinMux 一樣,但是此函數只使用了參數 configRegister 和 configValue,cofigRegister 參數是 IO 配置寄存器地址,比如 GPIO1_IO03 的 IO 配置寄存器為IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03,其地址為 0X020E02F4,參數 configValue 就是要寫入到寄存器 configRegister 的值。同理,代碼中的 43 行展開以后就是:
IOMUXC_SetPinConfig(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0X10B0);
根據函數 IOMUXC_SetPinConfig 的源碼可以知道,上面函數就是將寄存器 0x020E02F4 的值設置為 0X10B0。
以后就可以使用這兩個函數來方便的配置 IO 的復用功能和 IO 配置。
五、編譯下載驗證
5.1編寫 Makefile 和鏈接腳本
新建 Makefile 文件,Makefile 文件內容如下:
1 CROSS_COMPILE ?= arm-linux-gnueabihf-
2 NAME ?= ledc
3
4 CC := $(CROSS_COMPILE)gcc
5 LD := $(CROSS_COMPILE)ld
6 OBJCOPY := $(CROSS_COMPILE)objcopy
7 OBJDUMP := $(CROSS_COMPILE)objdump
8
9 OBJS := start.o main.o
10
11 $(NAME).bin:$(OBJS)
12 $(LD) -Timx6ul.lds -o $(NAME).elf $^
13 $(OBJCOPY) -O binary -S $(NAME).elf $@
14 $(OBJDUMP) -D -m arm $(NAME).elf > $(NAME).dis
15
16 %.o:%.s
17 $(CC) -Wall -nostdlib -c -O2 -o $@ $<
18
19 %.o:%.S
20 $(CC) -Wall -nostdlib -c -O2 -o $@ $<
21
22 %.o:%.c
23 $(CC) -Wall -nostdlib -c -O2 -o $@ $<
24
25 clean:
26 rm -rf *.o $(NAME).bin $(NAME).elf $(NAME).dis
Makefile 文件是在I.MX6ULL模仿 STM32 驅動開發格式實驗上修改的,只是使用到了變量。鏈接腳本 imx6ul.lds 的內容和I.MX6ULL模仿 STM32 驅動開發格式實驗一樣,可以直接復制該鏈接腳本文件使用。
5.2編譯下載
使用 Make 命令編譯代碼,編譯成功以后使用軟件 imxdownload 將編譯完成的 ledc.bin 文件下載到 SD 卡中,命令如下:
chmod 777 imxdownload //給予 imxdownload 可執行權限,一次即可
./imxdownload ledc.bin /dev/sdd //燒寫到 SD 卡中,不能燒寫到/dev/sda 或 sda1 設備里面!
燒寫成功以后將 SD 卡插到開發板的 SD 卡槽中,然后復位開發板,如果代碼運行正常的
話 LED0 就會以 500ms 的時間間隔亮滅
END