提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
[3.1 SDK文件移植](#3.1 SDK文件移植)
[3.2 创建cc.h文件](#3.2 创建cc.h文件)
[3.3、 编写实验代码](#3.3、 编写实验代码)
一、前言
在上一章中,我们参考 ST 官方为 STM32 编写的stm32f10x.h,自行实现了 I.MX6U 的寄存器定义文件。不过这种手动编写的方式既耗时费力、缺乏实际意义,还容易出现错误。好在 NXP 官方已为 I.MX6ULL 提供了 SDK 包,其中已经封装好了完善的寄存器定义文件,我们可以直接移植这些文件来简化开发。需要说明的是,尽管该 SDK 包是针对 I.MX6ULL 开发的,I.MX6UL 同样可以兼容使用。本章将详细讲解如何移植 SDK 包中的核心文件,助力后续的开发工作。
二、硬件原理
本次实验还是实验led灯的闪烁。

三、程序编写
首先下载I.MX6ULL 的 SDK 包,从NXP官方下载

SDK包文件如下:

我们主要需要的是其中与寄存器定义相关的文件,需要如下文件:

3.1 SDK文件移植
使用 VSCode 新建工程,将 fsl_common.h、fsl_iomuxc.h 和 MCIMX6Y2.h 这三个文件拷贝
到工程中
3.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
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
#endif
在 cc.h 文件中我们定义了很多的数据类型,因为有些第三方库会用到这些变量类型。
3.3、 编写实验代码
新建 start.S 和 main.c 这两个文件, start.S 文件的内容和之前led实验是一样,直接复制过来就可以
最终的文件目录如下:

在main.c中输入如下代码:
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : main.c
描述 : I.MX6U开发板裸机实验2 C语言点灯
使用C语言来点亮开发板上的LED灯,学习和掌握如何用C语言来
完成对I.MX6U处理器的GPIO初始化和控制。
其他 : 无
**************************************************************/
#include "fsl_iomuxc.h"
#include "fsl_common.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)
{
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);
/* 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 个函数的内容都很简单,前面
都讲过很多次了,重点是看以下两行代码如下:
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);
注意:
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 中定义,他有6个参数:
- muxRegister :IO 复用寄存器地址,例:GPIO1_IO03 的复用寄存器
SW_MUX_CTL_PAD_GPIO1_IO03地址为 0X020E0068。 - muxMode:IO 复用值(对应 ALT0~ALT8,数字 0~8),例:GPIO1_IO03 设为 GPIO 功能时,该参数为 5。
- inputRegister :外设输入 IO 选择寄存器地址,例:GPIO1_IO03 复用为 UART1_RX 时,需配置
UART1_RX_DATA_SELECT_INPUT(地址 0X020E0624)。 - inputDaisy :
inputRegister的配置值,例:GPIO1_IO03 作为 UART1_RX 引脚时,该参数为 1。 - configRegister :未使用,由
IOMUXC_SetPinConfig函数使用。 - inputOnfield:IO 软件输入使能(对应复用寄存器的 SION 位,bit4),例:GPIO1_IO03 使能软件输入时设为 1,否则为 0。
所以这个IOMUXC_SetPinMux 的函数体很简单,就是根据参数对寄存器 muxRegister 和 inputRegister进行赋值。而下行代码的作用就是将GPIO1_IO03 的复用功能设置为 GPIO
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
那为啥这里只有两个参数呢?
*
IOMUXC_GPIO1_IO03_GPIO1_IO03
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 的宏定义就
是IOMUXC_GPIO1_IO03_GPIO1_IO03,定义如下#define IOMUXC_GPIO1_IO03_GPIO1_IO03 0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U
带入到之前哪个函数:
IOMUXC_SetPinMux(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0);
这样就与6个参数对应起来了。
函数 IOMUXC_SetPinConfig同样也有6个参数,其中前五个参数和函数 IOMUXC_SetPinMux 一样
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
uint32_t muxMode,
uint32_t inputRegister,
uint32_t inputDaisy,
uint32_t configRegister,
uint32_t configValue)
但是此函数只使用了参数 configRegister 和 configValue , cofigRegister 参数是 IO 配置寄存
器地址,参数 configValue 就是要写入到寄存器 configRegister 的值
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);
所以上面函数就是将寄存器 0x020E02F4 的值设置为 0X10B0
编译下载
chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload ledc.bin /dev/sdb