Linux学习笔记8---官方 SDK 移植实验

在上一章中,我们参考 ST 官方给 STM32 编写的 stm32f10x.h 来自行编写 I.MX6U 的寄存器定义文件。自己编写这些寄存器定义不仅费时费力,没有任何意义,而且很容易写错,幸好NXP 官方为 I.MX6ULL 编写了 SDK 包,在 SDK 包里面 NXP 已经编写好了寄存器定义文件,所以我们可以直接移植 SDK 包里面的文件来用。虽然 NXP 是为 I.MX6ULL 编写的 SDK 包,但是 I.MX6UL 也是可以使用的!本章我们就来讲解如何移植 SDK 包里面重要的文件,方便我们的开发。

1、官网SDK下载

I.MX6ULL 的 SDK 包在 NXP 官网下载,下载界面如图所示:

下载链接:i.MX 6ULL应用处理器NXP官网下载链接

将下载下来的SDK_2.2_MCIM6ULL_RFP_Win.exe文件安装完成以后的SDK如图所示:

所有的例程都在 boards 这个文件夹里面。我们重点是需要 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 包我们就需要上面这三个文件,把这三个文件准备好,我们后面移植要用。

2、 硬件原理分析

可以看出,LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。

3、 SDK 文件移植

新建一个工程,将 fsl_common.h、fsl_iomuxc.h 和 MCIMX6Y2.h 这三个文件拷贝到工程中,需要对其不需要的地方进行删减,修改完成以后的工程目录如图所示:

4、创建 cc.h 文件

新建一个名为 cc.h 的头文件,cc.h 里面存放一些 SDK 库文件需要使用到的数据类型,在cc.h 里面输入如下代码:

cpp 复制代码
#ifndef __CC_H
#define __CC_H

/*
 * 自定义一些数据类型供库文件使用
 */
#define     __I     volatile 
#define     __O     volatile 
#define     __IO    volatile

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	  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 文件中我们定义了很多的数据类型,因为有些第三方库会用到这些变量类型。

5、编写实验代码

新建 start.S 和 main.c 这两个文件,start.S 文件的内容和上一章一样,直接复制过来就可以,创建完成以后工程目录如图所示:

在 main.c 中输入如下所示代码:

cpp 复制代码
#include "fsl_common.h"
#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"

/*
 * @description	: 使能I.MX6U所有外设时钟
 * @param 		: 无
 * @return 		: 无
 */
void clk_enable(void)
{
	CCM->CCGR0 = 0XFFFFFFFF;
	CCM->CCGR1 = 0XFFFFFFFF;

	CCM->CCGR2 = 0XFFFFFFFF;
	CCM->CCGR3 = 0XFFFFFFFF;
	CCM->CCGR4 = 0XFFFFFFFF;
	CCM->CCGR5 = 0XFFFFFFFF;
	CCM->CCGR6 = 0XFFFFFFFF;

}

/*
 * @description	: 初始化LED对应的GPIO
 * @param 		: 无
 * @return 		: 无
 */
void led_init(void)
{
	/* 1、初始化IO复用 */
	IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);		/* 复用为GPIO1_IO0 */

	/* 2、、配置GPIO1_IO03的IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
     */
	IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0);

	/* 3、初始化GPIO,设置GPIO1_IO03设置为输出  */
	GPIO1->GDIR |= (1 << 3);	
	
	/* 4、设置GPIO1_IO03输出低电平,打开LED0 */
	GPIO1->DR &= ~(1 << 3);			
}

/*
 * @description	: 打开LED灯
 * @param 		: 无
 * @return 		: 无
 */
void led_on(void)
{
	/* 将GPIO1_DR的bit3清零 	*/
	GPIO1->DR &= ~(1<<3); 
}

/*
 * @description	: 关闭LED灯
 * @param 		: 无
 * @return 		: 无
 */
void led_off(void)
{
	/* 将GPIO1_DR的bit3置1 */
	GPIO1->DR |= (1<<3); 
}

/*
 * @description	: 短时间延时函数
 * @param - n	: 要延时循环次数(空操作循环次数,模式延时)
 * @return 		: 无
 */
void delay_short(volatile unsigned int n)
{
	while(n--){}
}

/*
 * @description	: 延时函数,在396Mhz的主频下
 * 			  	  延时时间大约为1ms
 * @param - n	: 要延时的ms数
 * @return 		: 无
 */
void delay(volatile unsigned int n)
{
	while(n--)
	{
		delay_short(0x7ff);
	}
}

/*
 * @description	: mian函数
 * @param 		: 无
 * @return 		: 无
 */
int main(void)
{
	clk_enable();		/* 使能所有的时钟 			*/
	led_init();			/* 初始化led 			*/

	while(1)			/* 死循环 				*/
	{	
		led_off();		/* 关闭LED 			*/
		delay(500);		/* 延时500ms 			*/

		led_on();		/* 打开LED 			*/
		delay(500);		/* 延时500ms 			*/
	}

	return 0;
}

和上一章一样,main.c 有 7 个函数,这 7 个函数的含义都一样,只是本例程我们使用的是移植好的 NXP 官方 SDK 里面的寄存器定义。main.c 文件的这 7 个函数的内容都很简单,前面都讲过很多次了,我们重点来看一下 led_init 函数中的第 31 行和第 43 行,这两行的内容如下:

cpp 复制代码
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进行赋值。在"示例代码"中的 31 行使用此函数将 GPIO1_IO03 的复用功能设置为GPIO,如下:

IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);

其中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_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);

main.c 就讲到这里,基本和上一章一样,只是我们使用了 NXP 官方写好的寄存器定义,以后就可以使用这两个函数来方便的配置 IO 的复用功能和 IO 配置。

6、编写 Makefile 和链接脚本

新建 Makefile 文件,Makefile 文件内容如下:

cpp 复制代码
CROSS_COMPILE ?= arm-linux-gnueabihf-
NAME		  ?= ledc

CC 		:= $(CROSS_COMPILE)gcc
LD		:= $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump

OBJS 	:= start.o main.o

$(NAME).bin:$(OBJS)
	$(LD) -Timx6ul.lds -o $(NAME).elf $^
	$(OBJCOPY) -O binary -S $(NAME).elf $@
	$(OBJDUMP) -D -m arm $(NAME).elf > $(NAME).dis

%.o:%.s
	$(CC) -Wall -nostdlib -c -O2 -o $@ $<
	
%.o:%.S
	$(CC) -Wall -nostdlib -c -O2 -o $@ $<
	
%.o:%.c
	$(CC) -Wall -nostdlib -c -O2 -o $@ $<
	
clean:
	rm -rf *.o $(NAME).bin $(NAME).elf $(NAME).dis
	
	

本章实验的 Makefile 文件是在第6章中的 Makefile 上修改的,只是使用到了变量。链接脚本 imx6ul.lds 的内容和上一章一样,可以直接使用上一章的链接脚本文件。

7、编译下载

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 ledc.bin 文件生成可执行的img文件。

cpp 复制代码
make 
./imxdownload2 ledc.bin

利用Win32DiskImager软件将load.img执行文件写入SD卡,SD卡插入开发板上即可正常运行。如果代码运行正常的话 LED0 就会以 500ms 的时间间隔亮灭。

8、实验代码

【免费】Linux学习笔记8-官方SDK移植实验代码资源-CSDN文库

相关推荐
CXDNW2 分钟前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
使者大牙2 分钟前
【大语言模型学习笔记】第一篇:LLM大规模语言模型介绍
笔记·学习·语言模型
ssf-yasuo15 分钟前
SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记
论文阅读·笔记·prompt
As977_25 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
ajsbxi27 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
Rattenking28 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
TeYiToKu1 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws1 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
道法自然04021 小时前
Ethernet 系列(8)-- 基础学习::ARP
网络·学习·智能路由器
爱吃生蚝的于勒1 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法