韦东山 IMX6ULL和正点原子_「正点原子Linux连载」第十二章官方SDK移植试验
1)實驗平臺:正點原子Linux開發板
2)摘自《正點原子I.MX6U嵌入式Linux驅動開發指南》
關注官方微信號公眾號,獲取更多資料:正點原子
第十二章官方SDK移植試驗
在上一章中,我們參考ST官方給STM32編寫的stm32f10x.h來自行編寫I.MX6U的寄存器定義文件。自己編寫這些寄存器定義不僅費時費力,沒有任何意義,而且很容易寫錯,幸好NXP官方為I.MX6ULL編寫了SDK包,在SDK包里面NXP已經編寫好了寄存器定義文件,所以我們可以直接移植SDK包里面的文件來用。雖然NXP是為I.MX6ULL編寫的SDK包,但是I.MX6UL也是可以使用的!本章我們就來講解如何移植SDK包里面重要的文件,方便我們的開發。
12.1 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芯片都沒有。說明在NXP的定位里面,I.MX6ULL就是一個Cotex-A內核的高端單片機,定位類似ST的STM32H7。說這么多的目的就是想告訴大家,使用Cortex-A內核芯片的時候不要想著有類似STM32庫一樣的東西,I.MX6ULL是一個特例,基本所有的Cortex-A內核的芯片都不會提供裸機SDK包。因此在使用STM32的時候那些用起來很順手的庫文件,在Cotex-A芯片下基本都需要我們自行編寫,比如.s啟動文件、寄存器定義等等。
因為本教程是教大家Linux驅動開發入門的,本教程需要盡可能的降低入門難度,這也是為什么本教程會選擇I.MX6U芯片的一個重要的原因,因為其提供了I.MX6ULL的裸機SDK包,大家上手會很容易。I.MX6ULL的SDK包在NXP官網下載,下載界面如圖12.1.1所示:
圖12.1.1 I.MX6ULL SDK包下載界面
我們下載圖12.1.1中的WIN版本SDK,也就是“SDK2.2_iMX6ULL_WIN”,我們已經下載好放到光盤中,路徑為:開發板光盤-> 7、I.MX6U參考資料->3、I.MX6ULL SDK包->SDK_2.2_MCIM6ULL_RFP_Win.exe。雙擊SDK_2.2_MCIM6ULL_RFP_Win.exe安裝SDK包,安裝的時候需要設置好安裝位置,安裝完成以后的SDK包如圖12.1.2所示:
圖12.1.2 SDK包
我們本教程不是講解SDK包如何開發的,我們只是需要SDK包里面的幾個文件,所以就不去詳細的講解這個SDK包了,感興趣的可以看一下,所有的例程都在boards這個文件夾里面。我們重點是需要SDK包里面與寄存器定義相關的文件,一共需要如下三個文件:
fsl_common.h:位置為SDK_2.2_MCIM6ULLdevicesMCIMX6Y2driversfsl_common.h。
fsl_iomuxc.h: 位置為SDK_2.2_MCIM6ULLdevicesMCIMX6Y2driversfsl_iomuxc.h。
MCIMX6Y2.h:位置為SDK_2.2_MCIM6ULLdevicesMCIMX6Y2MCIMX6YH2.h。
整個SDK包我們就需要上面這三個文件,把這三個文件準備好,我們后面移植要用。
12.2硬件原理圖分析
本章使用到的硬件資源和第八章一樣,就是一個LED0。
12.3試驗程序編寫
本實驗對應的例程路徑為:開發板光盤-> 1、裸機例程->4_ledc_sdk。
12.3.1 SDK文件移植
使用VSCode新建工程,將fsl_common.h、fsl_iomuxc.h和MCIMX6Y2.h這三個文件拷貝到工程中,這三個文件直接編譯的話肯定會出錯的!需要對其做刪減,因為這三個文件里面的代碼都比較大,所以就不詳細列出這三個文件刪減以后的內容了。大家可以參考我們提供的裸機例程來修改這三個文件,很簡單的。修改完成以后的工程目錄如圖12.3.1.1所示:
圖12.3.1.1工程目錄
12.3.2 創建cc.h文件
新建一個名為cc.h的頭文件,cc.h里面存放一些SDK庫文件需要使用到的數據類型,在cc.h里面輸入如下代碼:
示例代碼12.3.2.1 cc.h文件代碼
1 #ifndef __CC_H
2 #define __CC_H
3/***************************************************************
4 Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5文件名 : cc.h
6作者 : 左忠凱
7版本 : V1.0
8描述 : 有關變量類型的定義,NXP官方SDK的一些移植文件會用到。
9其他 : 無
10日志 : 初版V1.0 2019/1/3 左忠凱創建
11 ***************************************************************/
12
13/*
14 * 自定義一些數據類型供庫文件使用
15 */
16 #define __I volatile
17 #define __O volatile
18 #define __IO volatile
19
20 #define ON 1
21 #define OFF 0
22
23typedefsigned char int8_t;
24typedefsigned short int int16_t;
25typedefsigned int int32_t;
26typedefunsigned char uint8_t;
27typedefunsigned short int uint16_t;
28typedefunsigned int uint32_t;
29typedefunsigned long long uint64_t;
30typedefsigned char s8;
31typedefsigned short int s16;
32typedefsigned int s32;
33typedefsigned long long int s64;
34typedefunsigned char u8;
35typedefunsigned short int u16;
36typedefunsigned int u32;
37typedefunsigned long long int u64;
38
39 #endif
在cc.h文件中我們定義了很多的數據類型,因為有些第三方庫會用到這些變量類型。
12.3.3編寫實驗代碼
新建start.S和main.c這兩個文件,start.S文件的內容和上一章一樣,直接復制過來就可以,創建完成以后工程目錄如圖12.3.3.1所示:
圖12.3.3.1 工程目錄文件
在main.c中輸入如下所示代碼:
示例代碼12.3.3.1 main.c文件代碼
/**************************************************************
Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : mian.c
作者 : 左忠凱
版本 : V1.0
描述 : I.MX6U開發板裸機實驗4 使用NXP提供的I.MX6ULL官方IAR SDK包開發
其他 : 前面其他所有實驗中,寄存器定義都是我們自己手寫的,但是I.MX6U
的寄存器有很多,全部自己寫太費時間,而且沒意義。NXP官方提供了
針對I.MX6ULL的SDK開發包,是基于IAR環境的,這個SDK包里面已經提
供了I.MX6ULL所有相關寄存器定義,雖然是針對I.MX6ULL編寫的,但是同樣
適用于I.MX6UL。本節我們就將相關的寄存器定義文件移植到Linux環境下,
要移植的文件有:
fsl_common.h
fsl_iomuxc.h
MCIMX6Y2.h
自定義文件 cc.h
日志 : 初版V1.0 2019/1/3 左忠凱創建
**************************************************************/
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 */
10void 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;
20
21}
22
23/*
24 * @description : 初始化LED對應的GPIO
25 * @param : 無
26 * @return : 無
27 */
28void 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 */
57void led_on(void)
58{
59/* 將GPIO1_DR的bit3清零 */
60 GPIO1->DR &=~(1<<3);
61}
62
63/*
64 * @description : 關閉LED燈
65 * @param : 無
66 * @return : 無
67 */
68void 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 */
79void delay_short(volatileunsignedint n)
80{
81while(n--){}
82}
83
84/*
85 * @description : 延時函數,在396Mhz的主頻下
86 * 延時時間大約為1ms
87 * @param - n : 要延時的ms數
88 * @return : 無
89 */
90void delay(volatileunsignedint n)
91{
92while(n--)
93{
94 delay_short(0x7ff);
95}
96}
97
98/*
99 * @description : mian函數
100 * @param : 無
101 * @return : 無
102 */
103int main(void)
104{
105 clk_enable(); /* 使能所有的時鐘 */
106 led_init(); /* 初始化led */
107
108while(1) /* 死循環 */
109{
110 led_off(); /* 關閉LED */
111 delay(500); /* 延時500ms */
112
113 led_on(); /* 打開LED */
114 delay(500); /* 延時500ms */
115}
116
117return0;
118}
和上一章一樣,main.c有7個函數,這7個函數的含義都一樣,只是本例程我們使用的是移植好的NXP官方SDK里面的寄存器定義。main.c文件的這7個函數的內容都很簡單,前面都講過很多次了,我們重點來看一下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”,所以上面兩個函數其實就是上一章中的:
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進行賦值。在“示例代碼12.3.3.1”中的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
將這個宏帶入到“示例代碼12.3.3.1”的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_SetPinMux就講解到這里,接下來看一下函數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的值。同理,“示例代碼12.3.3.1”的43行展開以后就是:
IOMUXC_SetPinConfig(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0X10B0);
根據函數IOMUXC_SetPinConfig的源碼可以知道,上面函數就是將寄存器0x020E02F4的值設置為0X10B0。函數IOMUXC_SetPinMux和IOMUXC_SetPinConfig就講解到這里,我們以后就可以使用這兩個函數來方便的配置IO的復用功能和IO配置。
main.c就講到這里,基本和上一章一樣,只是我們使用了NXP官方寫好的寄存器定義,另外中斷講解了函數IOMUXC_SetPinMux和IOMUXC_SetPinConfig。
12.4編譯下載驗證
12.4.1編寫Makefile和鏈接腳本
新建Makefile文件,Makefile文件內容如下:
示例代碼12.4.1.1 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文件是在第十一章中的Makefile上修改的,只是使用到了變量。鏈接腳本imx6ul.lds的內容和上一章一樣,可以直接使用上一章的鏈接腳本文件。
12.4.2編譯下載
使用Make命令編譯代碼,編譯成功以后使用軟件imxdownload將編譯完成的ledc.bin文件下載到SD卡中,命令如下:
chmod 777 imxdownload //給予imxdownload可執行權限,一次即可
./imxdownload ledc.bin /dev/sdd //燒寫到SD卡中
燒寫成功以后將SD卡插到開發板的SD卡槽中,然后復位開發板,如果代碼運行正常的話LED0就會以500ms的時間間隔亮滅,實驗現象和上一章一樣。
總結
以上是生活随笔為你收集整理的韦东山 IMX6ULL和正点原子_「正点原子Linux连载」第十二章官方SDK移植试验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab输入二项分布函数,matla
- 下一篇: linux系统分区表修复