文章目录
- 8.1 什么是固件库
- 8.2 什么时候使用
- 8.3 封装注意点
- 8.3.1 fsl
- 8.4 GPIO库函数定义
- 8.4.1 初始化结构体 gpio_pin_config_t
- 8.4.2 定义引脚模式的枚举类型
- 8.4.3 初始化结构体
- 8.4.4 定义中断配置函数
- 8.4.5 定义GPIO初始化函数
- 8.4.6 使用函数控制GPIO
- 8.4.7 定义 IOMUXC 外设控制的寄存器位
- 8.4.8 IOMUXC_SetPinMux ()
- 8.4.9 IOMUXC_SetPinConfig ()
- 8.5 使用函数控制LED
- 9 Nxp的固件库
- 9.1 获取 SDK 及固件库
- 9.2 SDK包内容
- 9.2.1 boards 文件夹
- 9.2.2 CMSIS 文件夹
- 9.2.2.1 CMSISDriver 目录:片上外设相关的驱动原型
- 9.2.2.2 CMSISInclude 目录:内核相关头文件
- 9.2.2.3 DSP 库
- 9.2.3 devices 文件夹
- 9.2.4 middleware 文件夹:
8.1 什么是固件库
固件库是指“RT1052 函数库”,是由 NXP 公司针对 RT1052 提供的函数接口,即
API (Application Program Interface)。
- 开发者可调用这些函数接口来配置 RT1052 的寄存器,使开发人员得以脱离最底层的寄存器操作
8.2 什么时候使用
绝大部分时候,我们愿意牺牲一点 CPU 资源,选择库开发
一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。
8.3 封装注意点
6 /* volatile 表示易变的变量,防止编译器优化, */
7 #define __IO volatile /* 可读写,一般用于定义有可读写权限的寄存器 */
8 #define __I volatile const /* 只读,一般用于定义只读权限的寄存器 */
/* 使用更简短直观的方式来定义无符号 32 、 16 、 8 位变量 */
11 typedef unsigned int uint32_t;
12 typedef unsigned short uint16_t;
13 typedef unsigned char uint8_t;
15 /* GPIO 寄存器结构体 */
16 typedef struct {
17 __IO uint32_t DR; /* DR 数据寄存器, 地址偏移 : 0x0 */
18 __IO uint32_t GDIR; /* GDIR 方向寄存器, 地址偏移 : 0x4 */
19 __I uint32_t PSR; /* PSR 状态寄存器, 地址偏移 : 0x8 */
20 __IO uint32_t ICR1; /* ICR1 中断配置寄存器 1, 地址偏移 : 0xC */
21 __IO uint32_t ICR2; /* ICR2 中断配置寄存器 2, 地址偏移 : 0x10 */
22 __IO uint32_t IMR; /* IMR 中断掩码寄存器 , 地址偏移 : 0x14 */
23 __IO uint32_t ISR; /* ISR 中断状态寄存器 , 地址偏移 : 0x18 */
24 __IO uint32_t EDGE_SEL; /* EDGE_SEL 边沿选择寄存器, 地址偏移 : 0x1C */
25 } GPIO_Type;
“__IO”代表了 C 语言中的关键字“volatile”,在 C 语言中该关键字用于修饰易变的变量,要求编译器不要优化
“__I”则代表“volatile const”在“__IO”的基础上增加不可修改的属性
寄存器很多时候是由外设或 RT1052 芯片状态修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新。
- 所以每次使用这些变量的时候,我们都要求 CPU 去该变量的地址重新访问。
- 若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。
8.3.1 fsl
文件名中的 fsl 大概是飞思卡尔半导体(freescale)的缩写,nxp 公司收购了 freescale,在固件库中它们使用 fsl 这个名字
8.4 GPIO库函数定义
8.4.1 初始化结构体 gpio_pin_config_t
1 /* GPIO 引脚配置结构体定义 */
2 typedef struct _gpio_pin_config {
3
4 /* 指定引脚的方向 */
5 uint8_t direction;
6
7 /* 设置一个默认的输出电平,在输入方向时本设置无效 */
8 uint8_t outputLogic;
9
10 /* 设置引脚的中断模式 */
11 uint8_t interruptMode;
12
13 } gpio_pin_config_t;
结构体中包含了初始化 GPIO 所需要的信息,包括引脚输入输出方向、默认输出电平以及中断模式。
然后把这个结构体作为“GPIO 初始化函数”的输入参数,该函数能根据这个变量值中的内容去配置寄存器,从而实现 GPIO 的初始化。
8.4.2 定义引脚模式的枚举类型
1 /* GPIO 方向枚举定义 */
2 typedef enum _gpio_pin_direction {
3 kGPIO_DigitalInput = 0U, /* 设置引脚为输入方向 */
4 kGPIO_DigitalOutput = 1U, /* 设置引脚为输出方向 */
5 } gpio_pin_direction_t;
6
7 /* GPIO 中断模式枚举定义 */
8 typedef enum _gpio_interrupt_mode {
9 kGPIO_NoIntmode = 0U, /* 设置引脚为通用 IO 功能(不使用中断) */
10 kGPIO_IntLowLevel = 1U, /* 设置引脚低电平引起中断 */
11 kGPIO_IntHighLevel = 2U, /* 设置引脚高电平引起中断 */
12 kGPIO_IntRisingEdge = 3U, /* 设置引脚上升沿引起中断 */
13 kGPIO_IntFallingEdge = 4U, /* 设置引脚下降沿引起中断 */
14 kGPIO_IntRisingOrFallingEdge = 5U, /* 设置引脚上升沿和下降沿都引脚中断 */
15 } gpio_interrupt_mode_t
“U”表示该数字是无符号类型,在这里其实不写也可以,是一种编程习惯。
8.4.3 初始化结构体
1 /* 定义 gpio_pin_config_t 类型的结构体变量 */
2 gpio_pin_config_t led_config;
3 /* 配置方向为输出模式 */
4 led_config.direction = kGPIO_DigitalOutput;
5 /* 配置默认输出高电平 */
6 led_config.outputLogic = 1;
7 /* 配置不使用中断 */
8 led_config.interruptMode = kGPIO_NoIntmode;
8.4.4 定义中断配置函数
GPIO 初始化中涉及到众多的中断配置寄存器,为此我们定义一个 GPIO_PinSetInterruptConfig 中断模式配置函数专门处理这些事情
/*
2 * 设置指定引脚的中断模式
3 * base: GPIO_Type 类型的指针,如 GPIO1 、 GPIO2 等宏
4 * pin: 要控制引脚的编号
5 * pininterruptMode: gpio_interrupt_mode_t 类型的指针
6 * 该结构体包含中断配置的信息
7 */
8 void GPIO_PinSetInterruptConfig(GPIO_Type* base, uint32_t pin,
9 gpio_interrupt_mode_t pinInterruptMode)
10 {
11 volatile uint32_t *icr;
12 uint32_t icrShift;
13
14 /* icrShift 初值为引脚号,后面用来定位引脚对应的寄存器配置位
15 * 如 pin0 的配置位为 bit0 、 bit1 , pin1 的配置位为 bit2 、 bit3
16 */
17 icrShift = pin;
18
19 /* 编号小于 16 的使用 ICR1 寄存器控制,其它在 ICR2 控制 */
20 if (pin < 16) {
21 /* icr 指针指向 ICR1 */
icr = &(base->ICR1);
23 } else {
24 /* icr 指针指向 ICR2 */
25 icr = &(base->ICR2);
26 /* 对应引脚配置位跟引脚号的关系要减 16
27 * 如 pin16 的配置位为 bit0 、 bit1 , pin17 的配置位为 bit2 、 bit3
28 */
29 icrShift -= 16;
30 }
31
32 /* 先对 EDGE_SEL 寄存器相应引脚的控制位清零,
33 因为 EDGE_SEL 非零的话 ICR 寄存器的配置无效,
34 引脚会被直接配置为双边沿模式 */
35 base->EDGE_SEL &= ~(1U << pin);
36
37 /* 根据中断模式配置寄存器 */
38 switch (pinInterruptMode) {
39 /* 高低电平或单边沿触发配置 ICR 寄存器 */
40 case (kGPIO_IntLowLevel):
41 /* 对应 ICR 寄存器位清零: 0b00 ,低电平触发 */
42 *icr &= ~(3U << (2 * icrShift));
43 break;
44 case (kGPIO_IntHighLevel):
45 /* 对应 ICR 寄存器位清零后赋值为 1 : 0b01 ,高电平触发 */
46 *icr = (*icr & (~(3U << (2 * icrShift)))) | (1U << (2 * icrShift));
47 break;
48 case (kGPIO_IntRisingEdge):
49 /* 对应 ICR 寄存器位清零后赋值为 2 : 0b10 ,上升沿触发 */
50 *icr = (*icr & (~(3U << (2 * icrShift)))) | (2U << (2 * icrShift));
51 break;
52 case (kGPIO_IntFallingEdge):
53 /* 对应 ICR 寄存器位赋值为 3 : 0b11 ,下降沿触发 */
54 *icr |= (3U << (2 * icrShift));
55 break;
56
57 /* 双边沿触发配置 EDGE_SEL 寄存器 */
58 case (kGPIO_IntRisingOrFallingEdge):
59 /* 对应 EDGE_SEL 寄存器位置 1 ,配置为双边沿触发 */
60 base->EDGE_SEL |= (1U << pin);
61 break;
62 default:
63 break;
64 }
65 }
《IMXRT1050RM》(参考手册)GPIO 章节中对这些寄存器进行了说明,以下是简要说明:
使用 GPIO 中断时
- 使用中断控制寄存器 ICR1、ICR2 或中断双边沿选择寄存器EDGE_SEL 中选择触发中断的模式,分别有高电平、低电平、上升沿、下降沿以及双边沿触发模式
- 选择好模式后控制中断屏蔽寄存器 IMR 使能中断。
- 当 GPIO 引脚出现触发模式对应的电气状态时,在中断状态寄存器 ISR 可检测到中断事件标志。
pinInterruptMode 就是前面定义的中断模式枚举类型,用来指定要选择高电平、低电平、上升沿、下降沿还是双边沿触发模式。
- 函数体中使用 icr 及 icrShift 两个变量用于运算缓冲
- 因为 ICR1 寄存器用于配置编号0-15 引脚,ICR2 寄存器用于配置编号 16~31 的引脚
- 对于 0~15 号引脚,icr 指向 ICR1 寄存器
- 对于 16~31 号引脚,icr 指向 ICR2 寄存器
ICR1 与 ICR2 寄存器的说明:
35 行,它对引脚在 EDGE_SEL 寄存器的配置位进行清零,以确保后续对ICR 寄存器的配置能生效。
- 引脚在 EDGE_SEL 寄存器中的配置位为 1 时,无论引脚在 ICR1 或 ICR2 寄存器中配置成什么模式都会被覆盖成 EDGE_SEL 寄存器配置的双边沿模式触发。
第 38~55 行,根据输入的枚举变量 pinInterruptMode 参数使用 switch 分出不同的分支给 ICR寄存器赋予对应模式的控制值。
第 57~61 行,这是 pinInterruptMode 变量等于 kGPIO_IntRisingOrFallingEdge 的处理分支,即输入要求配置为双边沿触发,所以在处理的时候是对 EDGE_SEL 寄存器进行赋值的。
这个函数并没有到中断屏蔽寄存器 IMR 进行配置,要使中断开始工作,需要对这个 IMR 寄存器引脚对应的位置 1 进行使能
8.4.5 定义GPIO初始化函数
定义 GPIO 初始化函数
9 void GPIO_PinInit(GPIO_Type* base, uint32_t pin,
10 const gpio_pin_config_t* Config)
11 {
12 /* 对相应引脚 IMR 寄存器的控制位清零,先关闭中断 */
13 base->IMR &= ~(1U << pin);
14
15 /* 配置 GPIO 引脚的方向 */
16 if (Config->direction == kGPIO_DigitalInput) {
17 /* 输入模式 */
18 base->GDIR &= ~(1U << pin);
19 } else {
20 /* 输出模式 */
21 /* 先对 DR 寄存器赋值默认电平 */
22 GPIO_PinWrite(base, pin, Config->outputLogic);
23 /* 配置为输出模式 */
24 base->GDIR |= (1U << pin);
25 }
26
27 /* 配置 GPIO 引脚的中断模式 */
28 GPIO_PinSetInterruptConfig(base, pin, Config->interruptMode);
29 }
这个函数有 base、pin 以及 Config 三个输入参数,分别是 GPIO 外设指针、引脚编号和 GPIO 初始化结构体指针。分别用来指定要初始化的 GPIO 端口、引脚号和引脚的工作模式。
该函数的实现说明:
-
对引脚在中断屏蔽寄存器 IMR 中的配置位清零,即关闭中断。
-
根据输入参数初始化结构体 Config 中的 direction 成员的值,分成输入方向配置分支和输出方向配置分支。
- 要设置为输入方向时对方向寄存器 GDIR 对应的位清零。
- 要设置为输出方向时先调用GPIO_PinWrite 函数根据初始化结构体 Config 中的outputLogic成员设置引脚默认要输出高电平还是低电平,设置好后再对方向寄存器GDIR对应的位置 1,这样的配置顺序能保证引脚初始化成功后立即输出 outputLogic 表示的电平值。
-
调用前面讲解的 GPIO_PinSetInterruptConfig 中断模式配置函数,该函数接收初始化结构体Config 中 interruptMode 成员的值,根据它配置中断模式。
8.4.6 使用函数控制GPIO
4 int main(void)
5 {
6 /* 使用 GPIO 初始化结构体定义一个变量用于配置 GPIO */
7 gpio_pin_config_t led_config;
8 /* 开启 GPIO1 端口的时钟 */
9
10 /* 清空控制 GPIO1 端口时钟的 bit26 、 bit27 */
11
12 CCM_CCGR1 &= ~(unsigned int)(3<<26);
13
14 /* 把 bit26 、 bit27 设置为 0b01 ,即开启 GPIO1 时钟 */
15 CCM_CCGR1 |= (unsigned int)(1<<26);
16
17 /* 设置 MUX 寄存器为 0x05 ,表示把引脚用于普通 GPIO */
18 IOMUXC_MUX_GPIO_AD_B0_09 = (unsigned int)0x05;
19
20 /* 设置 PAD 寄存器控制引脚的属性 */
21 IOMUXC_PAD_GPIO_AD_B0_09 = (unsigned int)0x000B0;
48 led_config.direction = kGPIO_DigitalOutput; // 输出模式
49 led_config.outputLogic = 1; // 默认高电平
50 led_config.interruptMode = kGPIO_NoIntmode; // 不使用中断
51
52 /* 使用 led_config 初始化 GPIO1_IO09*/
53 GPIO_PinInit(GPIO1,9,&led_config);
54
55 /* 控制 GPIO1_IO09 为低电平,点亮 LED 灯 */
56 GPIO_PinWrite(GPIO1,9,0);
8.4.7 定义 IOMUXC 外设控制的寄存器位
1 /* MUX 寄存器各个配置域的掩码、偏移及设置宏 */
2 /* MUX_MODE 配置 */
3 #define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK (0x7U)
4 #define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT (0U)
5 #define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(x) \
6 (((uint32_t)(((uint32_t)(x)) << IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT))\
7 & IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK)
IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK 表示是 MUX 寄存器中的 MUX_MODE 配置域的 MASK(掩码),所谓掩码是用位值 1 来表示配置域在寄存器中占据的位置,使用掩码可以方便后续的寄存器位运算。
5-7 行比较复杂,这几行代码实际是一个整体,它其实就是一个带参数的宏,我们把第 5-7 行和第 12~14 行的宏使用同功能的函数实现:
1 //MUX_MODE 与 IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(x) 功能等效的函数实现
2 uint32_t IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(uint32_t x)
3 {
4 uint32_t x_shift;
5 uint32_t config;
6 /* 把配置值偏移到对应的寄存器位置 */
7 x_shift = (uint32_t)x << IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT;
8 /* 与掩码做 & 运算确保其它无关位均为 0 不受影响 */
9 config = (uint32_t)(x_shift & IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK);
10 /* 返回 config , config 中包含 MUX_MODE 位配置为 x, 其余位均为 0 的数值 */
11 return config;
12 }
13
14 //SION 与 IOMUXC_SW_MUX_CTL_PAD_SION(x) 功能等效的函数实现
15 uint32_t IOMUXC_SW_MUX_CTL_PAD_SION(uint32_t x)
16 {
17 uint32_t x_shift;
18 uint32_t config;
19 /* 把配置值偏移到对应的寄存器位置 */
20 x_shift = (uint32_t)x << IOMUXC_SW_MUX_CTL_PAD_SION_SHIFT;
21 /* 与掩码做 & 运算确保其它无关位均为 0 不受影响 */
22 config = (uint32_t)(x_shift & IOMUXC_SW_MUX_CTL_PAD_SION_MASK);
23 /* 返回 config , config 中包含 SION 位配置为 x, 其余位均为 0 的数值 */
24 return config;
25 }
根据输入参数 x 生成一个 32 位的数值,这个 32 位数中仅相关的配置域为 x,其余位为 0,各个配置域利用这样的函数(带参宏),使用“|”运算组合出一个最终要赋予给寄存器的值
1 /* 给 GPIO_AD_B0_09 的 MUX 寄存器赋值 */
2 /* 赋值结果为 MUX_MODE 配置域为 5 , SION 配置域为 1 */
3 IOMUXC_MUX_GPIO_AD_B0_09 = IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(5)| IOMUXC_SW_MUX_CTL_PAD_SION(1);
在 NXP 的库函数代码中这些宏命名大体遵照这样的格式:寄存器类型名 _ 配置域名 _ 宏功能,如前面介绍的 IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK
- IOMUXC_SW_MUX_CTL_PAD 表示它是 MUX 类型寄存器
- MUX_MODE 表示这个宏用于控制 MUX_MODE 配置域
- 宏的功能则是 MASK(掩码)。
8.4.8 IOMUXC_SetPinMux ()
此函数将IO复用为GPIO。
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0); /* 复用为GPIO1_IO03 */
//参数说明
31 * muxRegister: 本引脚 MUX 寄存器的地址
32 * muxMode: 要配置的复用模式
33 * inputRegister: 可选的要设置的寄存器地址
34 * inputDaisy: 要给上述可选的寄存器赋予的值
35 * configRegister: 本引脚 PAD 属性配置寄存器的地址
36 * inputOnfield:SION 输入回路配置域的值, 1 或 0
37 */
38 static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
39 uint32_t muxMode,
40 uint32_t inputRegister,
41 uint32_t inputDaisy,
42 uint32_t configRegister,
43 uint32_t inputOnfield)
44 {
45 /* 设置 MUX_MODE 及 SION */
46 *((volatile uint32_t *)muxRegister) =
47 IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) |
48 IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);
49
50 /* 若可选寄存器非 0 ,则向它赋值 */
51 if (inputRegister) {
52 *((volatile uint32_t *)inputRegister) = inputDaisy;
53 }
54 }
第 47~49 行仍然是使用 muxMode 和 inputOnfield 生成寄存器的配置值然后赋予给 muxRegister 参数指定的地址中。
第 50~53 行是增加的代码,它判断增加的参数 inputRegister 非 0 后,就向 inputRegister 指向的寄存器赋予参数 inputDaisy 的值。
**
特别地,在这个函数中并没有使用 configRegister 参数
**
IOMUXC_GPIO1_IO03_GPIO1_IO03在iomuxc.h 中的宏定义
#define IOMUXC_GPIO1_IO03_GPIO1_IO03 0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U
配置IOMUXC_SetPinMux()的6个参数为
uint32_t muxRegister, 0x020E0068U,
uint32_t muxMode, 0x5U,
uint32_t inputRegister, 0x00000000U,
uint32_t inputDaisy, 0x0U,
uint32_t configRegister, 0x020E02F4U,
uint32_t inputOnfield 0,
1.IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的寄存器地址为020E 0068h
2.0101 ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1
配置为GPIO1,所以向020E0068h,中写入0x05
3.The select input register选择输入寄存器 选择信号线等,有的外设没有,没有就是0。
4.The input daisy 同上外设没有不设置。如果inputRegister不为0,就写入inputDaisy
5.The config register
6.寄存器bit4,SION位 Software input on field
8.4.9 IOMUXC_SetPinConfig ()
在main.c中的表现为
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0);
IOMUXC_GPIO1_IO03_GPIO1_IO03在iomuxc.h 中的宏定义
#define IOMUXC_GPIO1_IO03_GPIO1_IO03 0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U
由以上三段代码可知,配置IOMUXC_SetPinConfig () 的6个参数为
uint32_t muxRegister, 0x020E0068U,
uint32_t muxMode, 0x5U,
uint32_t inputRegister, 0x00000000U,
uint32_t inputDaisy, 0x0U,
uint32_t configRegister, 0x020E02F4U,
uint32_t configValue 0x10B0,
1.IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的寄存器地址为020E 0068h
2.0101 ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1 配置为GPIO1,所以向020E0068h,中写入0x05
3.The select input register选择输入寄存器 选择信号线等,有的外设没有,没有就是0。
4.The input daisy 同上外设没有不设置。如果inputRegister不为0,就写入inputDaisy
5.The config register配置寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址为020E 02F4h P1793
6.The pin config value配置数值10B0h=0001 0000 1011 0000
源码:
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;
}
}
这个函数在函数的主体,只是针对 configRegister 参数指定的寄存器赋予了 configValue 参数的值,完全没有使用前面 4 个参数,所以这 4 个参数也是为了与前面定义的宏对齐,配合使用。
- configRegister 参数,用于指定 PAD 属性配置寄存器的地址。
- configValue 参数,用于指定要给 PAD 属性配置寄存器赋予的值。
/* SRE 压摆率选择 */
2 #define SRE_0_SLOW_SLEW_RATE IOMUXC_SW_PAD_CTL_PAD_SRE(0)
3 #define SRE_1_FAST_SLEW_RATE IOMUXC_SW_PAD_CTL_PAD_SRE(1)
/* 驱动能力配置,配置阻值的大小 */
6 #define DSE_0_OUTPUT_DRIVER_DISABLED IOMUXC_SW_PAD_CTL_PAD_DSE(0)
7 /* R0 260 Ohm @ 3.3V, 150Ohm@1.8V, 240 Ohm for DDR */
8 #define DSE_1_R0_1 IOMUXC_SW_PAD_CTL_PAD_DSE(1)
9 /* R0/2 */
10 #define DSE_2_R0_2 IOMUXC_SW_PAD_CTL_PAD_DSE(2)
11 /* R0/3 */
12 #define DSE_3_R0_3 IOMUXC_SW_PAD_CTL_PAD_DSE(3)
13 /* R0/4 */
14 #define DSE_4_R0_4 IOMUXC_SW_PAD_CTL_PAD_DSE(4)
15 /* R0/5 */
16 #define DSE_5_R0_5 IOMUXC_SW_PAD_CTL_PAD_DSE(5)
17 /* R0/6 */
18 #define DSE_6_R0_6 IOMUXC_SW_PAD_CTL_PAD_DSE(6)
19 /* R0/7 */
20 #define DSE_7_R0_7 IOMUXC_SW_PAD_CTL_PAD_DSE(7)
21
22 /* SPEED 带宽配置 */
23 #define SPEED_0_LOW_50MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(0)
24 #define SPEED_1_MEDIUM_100MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(1)
25 #define SPEED_2_MEDIUM_100MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(2)
26 #define SPEED_3_MAX_200MHz IOMUXC_SW_PAD_CTL_PAD_SPEED(3)
27
28 /* ODE 是否使用开漏模式 */
29 #define ODE_0_OPEN_DRAIN_DISABLED IOMUXC_SW_PAD_CTL_PAD_ODE(0)
30 #define ODE_1_OPEN_DRAIN_ENABLED IOMUXC_SW_PAD_CTL_PAD_ODE(1)
31
32 /* PKE 是否使能保持器或上下拉功能 */
33 #define PKE_0_PULL_KEEPER_DISABLED IOMUXC_SW_PAD_CTL_PAD_PKE(0)
34 #define PKE_1_PULL_KEEPER_ENABLED IOMUXC_SW_PAD_CTL_PAD_PKE(1)
36 /* PUE 选择使用保持器还是上下拉 */
37 #define PUE_0_KEEPER IOMUXC_SW_PAD_CTL_PAD_PUE(0)
38 #define PUE_1_PULL IOMUXC_SW_PAD_CTL_PAD_PUE(1)
39
40 /* PUS 上下拉配置 */
41 #define PUS_0_100K_OHM_PULL_DOWN IOMUXC_SW_PAD_CTL_PAD_PUS(0)
42 #define PUS_1_47K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(1)
43 #define PUS_2_100K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(2)
44 #define PUS_3_22K_OHM_PULL_UP IOMUXC_SW_PAD_CTL_PAD_PUS(3)
45
46 /* HYS 滞后功能 */
47 #define HYS_0_HYSTERESIS_DISABLED IOMUXC_SW_PAD_CTL_PAD_HYS(0)
48 #define HYS_1_HYSTERESIS_ENABLED IOMUXC_SW_PAD_CTL_PAD_HYS(1)
引脚属性设置:
1 /* 设置引脚属性 */
2 IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_09_GPIO1_IO09,
3 IOMUXC_SW_PAD_CTL_PAD_SRE(0)|
4 IOMUXC_SW_PAD_CTL_PAD_DSE(0x6)|
5 IOMUXC_SW_PAD_CTL_PAD_SPEED(0x2)|
6 IOMUXC_SW_PAD_CTL_PAD_ODE(0)|
7 IOMUXC_SW_PAD_CTL_PAD_PKE(0)|
8 IOMUXC_SW_PAD_CTL_PAD_PUE(0)|
9 IOMUXC_SW_PAD_CTL_PAD_PUS(0)|
10 IOMUXC_SW_PAD_CTL_PAD_HYS(0)
11 );
12
13/* bit0: SRE: 0b0 压摆率 : 慢压摆率
14 bit3~bit5: DSE: 0b110 驱动强度 : R0/6 (仅作为输出时有效 )
15 bit6~bit7: SPEED:0b10 带宽 : medium(100MHz)
16 bit11: ODE: 0b0 开漏配置 : 关闭
17 (开漏高阻态常用于总线配置,如 I2C )
18 bit12: PKE: 0b0 拉 / 保持器配置 : 关闭
19 bit13: PUE: 0b0 拉 / 保持器选择 : 关闭了上下拉及保持器,任意值无效
20 bit14~bit15: PUS: 0b00 上拉 / 下拉选择 : 关闭了上下拉及保持器,任意值无效
21 bit16: HYS: 0b0 滞回器配置 : 关闭
22 (仅输入时有效,施密特触发器,使能后可以过滤输入噪声)
8.5 使用函数控制LED
4 int main(void)
5 {
6 /* 使用 GPIO 初始化结构体定义一个变量用于配置 GPIO */
7 gpio_pin_config_t led_config;
* 开启 GPIO1 端口的时钟 */
10
11 /* 清空控制 GPIO1 端口时钟的 bit26 、 bit27 */
CCM_CCGR1 &= ~(unsigned int)(3<<26);
14
15 /* 把 bit26 、 bit27 设置为 0b01 ,即开启 GPIO1 时钟 */
16 CCM_CCGR1 |= (unsigned int)(1<<26);
17 /* 设置引脚为 MUX_MODE 及 SION
18 IOMUXC_GPIO_AD_B0_09_GPIO1_IO09 宏表示的五个参数:
19 0x401F80E0U, GPIO_AD_B0_09 的 MUX 寄存器的地址
20 0x5U, 选择 ATL5, 即 GPIO 功能
21 0, 可选的要同时配置的寄存器,为 0 表示不配置
22 0, 可选寄存器要赋予的值
23 0x401F82D0U GPIO_AD_B0_09 的 PAD 属性配置寄存器的地址
24 */
25 /* 设置复用为 GPIO1_IO09 功能
26 不使用 SION 功能
27 */
28 IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_09_GPIO1_IO09,
29 0 );
30
31
32
33 /* 设置引脚属性 */
34 IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_09_GPIO1_IO09,
35 SRE_0_SLOW_SLEW_RATE|
36 DSE_6_R0_6|
37 SPEED_2_MEDIUM_100MHz|
38 ODE_0_OPEN_DRAIN_DISABLED|
39 PKE_0_PULL_KEEPER_DISABLED|
40 PUE_0_KEEPER_SELECTED|
41 PUS_0_100K_OHM_PULL_DOWN|
42 HYS_0_HYSTERESIS_DISABLED
43 );
45 led_config.direction = kGPIO_DigitalOutput; // 输出模式
46 led_config.outputLogic = 1; // 默认高电平
47 led_config.interruptMode = kGPIO_NoIntmode; // 不使用中断
48
49 /* 使用 led_config 初始化 GPIO1_IO09*/
50 GPIO_PinInit(GPIO1,9,&led_config);
51
52 /* 控制 GPIO1_IO09 为低电平,点亮 LED 灯 */
53 GPIO_PinWrite(GPIO1,9,0);
54
55 while (1);
56
57 }
9 Nxp的固件库
9.1 获取 SDK 及固件库
SDK(Software Development Kit)是 NXP 针对其官方评估版的软件开发包,可以在 NXP 的官网下载到。
- SDK 中包含了各种程序范例,我们心心念念的固件库也包含在它里边。
官网下载:
访问 NXP 的 MCUXpresso 平台的链接
未登录的用户需要先登录 NXP 官网
在“SDK Dashboard”页面,点击“Select Development Board”
- NXP 官方的 SDK 是针对 NXP 自己的 RT1050-EVK 开发板写的,所以在选择 Boards 的时候选择 NXP 官方的 RT1052 开发板,如果需要下载 NXP 其他
型号的 MCU SDK 的时候就选择相应的 Boards
跳转到
进一步配置 SDK,比如开发主机系统:是 windows 还是 linux?所使用的 IDE 环境:是 KEIL、IAR 还是 GCC?是否需要第三方组件?都需要些啥组件等等
这里我们直接选择“All toolchains”
点击“Add software component”按钮,确认需要增加的组件
可以根据自己的需要添加 SDK 中包含的组件,例如 CMSIS DSP 库、FatFs 文件系统、USB、lwIP 协议栈、emwin 图形界面库、FreeRTOS 实时系统等。有些库占用的空间比较大,且初学的时候不需要那么多的组件,我们在默认的 FatFs、USB Stack、lwIP 之上增加选项即可
配置好以后点击下面的“Request Build”
SDK包下载
9.2 SDK包内容
NXP 官方的 SDK 包一共有 7 个文件夹和 3 个其他文件
9.2.1 boards 文件夹
NXP 针对 RT1050 系列 MCU 写的一些例程
NXP 提供的例程共有 8 种,有 USB 的、emiwn 的、基础例程的和RTOS 操作系统的等等。学习 RT1052 重点就是参考官方 SDK 包里面提供的这些例程。
- cmsis_driver_examples 提供了一些按照 CMSIS 标准编写的驱动范例。CMSIS 是 ARM 提出的一个编程标准。
- demo_apps 包含了一些应用范例,如串口打印“hello world”、使用 lwip 协议栈进行网络通讯等内容
- driver_example 包 含 了 RT1052 每 种 片 上 外 设 的 使 用 范 例, 非 常 详 细。
其中的“gpio”目录下又包含了使用 gpio 外设点亮 LED 灯和中断检测的例子 - emwin_examples 包含了使用 emWin 图形软件库编写的图形界面示例,使用 emWin 可以编
写出漂亮的界面程序。 - project_template 包含了官方示例使用的一些必备文件,这些文件主要是针对官方评估板做
了一些引脚定义、时钟配置等功能
- rtos_examples 包含了使用 FreeRTOS 实时操作系统的应用范例
- usb_examples 包含了各种 USB 程序示例
USB 设备种类繁多且驱动复杂
9.2.2 CMSIS 文件夹
CMSIS 全称: Cortex Microcontroller SoftwareInterface Standard,叫做 Cortex 微控制器软件接口标准。
为了解决不同的芯片厂商生产的 Cortex 微控制器软件的兼容性问题,ARM 与芯片厂商建立了 CMSIS 标准 (Cortex MicroController SoftwareInterface Standard)。
此文件夹里面有一些重要的跟 Cortex-M 架构有关的头文件,在 include 文件夹里面,创建工程需要用到!此文件夹还有重要的 DSP 库,在 Lib 文件夹里面。
9.2.2.1 CMSISDriver 目录:片上外设相关的驱动原型
CMSISDriver 和 CMSISInclude 目录,它们分别对应 CMSI 核心层和设备外设函数层的内容。
- CMSISDriver 目录,打开后发现它又包含两个文件夹
它们分别包含了一些关于片上外设的 C 源文件和 C 头文件,如 CAN、I2C、USART 等外设
在这两个目录下的 Driver_USART.c 和 Driver_USART.h 文件就是 ARM 官方基于 CMSIS 标准针对 USART 外设编写的原型文件
在源文件中,可以看到它定义了一些串口可能会使用的函数原型,如 - -
- ARM_USART_Send 用来发送数据
- ARM_USART_Receive 用来接收串口数据
这些函数都是空函数,,所以 ARM 也就是提供一个原型,具体代码还是由芯片厂商根据自己的外设来实现。
在头文件中,定义了一些使用串口外设时可能会产生的事件,例如串口外设发送完成后会在寄存器的标志位置 1,以告诉用户产生了该事件。
从这两个文件可以了解到,ARM 针对核外外设定义了 CMSIS 标准,相当于完成了软件架构师的工作
9.2.2.2 CMSISInclude 目录:内核相关头文件
这就是 CMSIS 核心层,与前面只有架构的外设驱动不同,它们是由 ARM 公司提供的直接可用的内核驱动文件。
- 包含了针对编译环境差异屏蔽的 core_compiler.h、cmsis_armcc.h 等文件
- 包含了定义 CMSIS 版本号的文件 cmsis_version.h 文件
- 以及针对不同内核寄存器定义的 core_cm3.h、core_cm4、core_cm7.h 等文件。
对于采用同样内核架构的芯片,芯片厂一般不会进行改动,由 ARM 根据自己内核定义出来的 CMSIS 核心层文件就是直接针对内核可用的文件。
- 例如,分别由 NXP 和 ST 公司生产的 RT1052 和 STM32F7 芯片,它们都使用 ARM 的 Cortex-M7 内核,所以它们使用的内核驱动文件都是 ARM 针对 Cortex-M7 提供的 core_cm7.h 内核文件,是完全一样的
9.2.2.3 DSP 库
ARM 还针对自己的芯片提供了 DSP 处理的运算库函数,例如定点运算、傅利叶变换、PID 算法等。
- 本教程讲解的内容不包含 DSP 库,有需要的话请按照前面《9.1 获取 SDK 及固件库》小节下载 SDK,且在组件中勾选“CMSIS DSP Library”项。这样下载得的 SDK 在“CMSISDSP Lib”目录下可找到 DSP 相关的文件。
9.2.3 devices 文件夹
此文件夹是 RT1052 相关的东西,包括 FSL 库。
FSL 库就在 drivers 文件夹里面,我们创建库函数工程模板的时候就需要 FSL 库,还有一些其他的跟 RT1052 有关的.c 和.h 文件。
9.2.4 middleware 文件夹:
此文件夹包含了一些常用的第三方组件,比如 fatfs、lijpeg 库、usb、emwi 等。