赞
踩
固件库是指“RT1052 函数库”,是由 NXP 公司针对 RT1052 提供的函数接口,即
API (Application Program Interface)。
绝大部分时候,我们愿意牺牲一点 CPU 资源,选择库开发
一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。
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 不执行代码修改这些变量,变量的值也有可能被外设修改、更新。
文件名中的 fsl 大概是飞思卡尔半导体(freescale)的缩写,nxp 公司收购了 freescale,在固件库中它们使用 fsl 这个名字
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 的初始化。
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”表示该数字是无符号类型,在这里其实不写也可以,是一种编程习惯。
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;
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 中断时
pinInterruptMode 就是前面定义的中断模式枚举类型,用来指定要选择高电平、低电平、上升沿、下降沿还是双边沿触发模式。
ICR1 与 ICR2 寄存器的说明:
35 行,它对引脚在 EDGE_SEL 寄存器的配置位进行清零,以确保后续对ICR 寄存器的配置能生效。
第 38~55 行,根据输入的枚举变量 pinInterruptMode 参数使用 switch 分出不同的分支给 ICR寄存器赋予对应模式的控制值。
第 57~61 行,这是 pinInterruptMode 变量等于 kGPIO_IntRisingOrFallingEdge 的处理分支,即输入要求配置为双边沿触发,所以在处理的时候是对 EDGE_SEL 寄存器进行赋值的。
这个函数并没有到中断屏蔽寄存器 IMR 进行配置,要使中断开始工作,需要对这个 IMR 寄存器引脚对应的位置 1 进行使能
定义 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 成员的值,分成输入方向配置分支和输出方向配置分支。
调用前面讲解的 GPIO_PinSetInterruptConfig 中断模式配置函数,该函数接收初始化结构体Config 中 interruptMode 成员的值,根据它配置中断模式。
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);
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
此函数将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
在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 个参数也是为了与前面定义的宏对齐,配合使用。
/* 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 (仅输入时有效,施密特触发器,使能后可以过滤输入噪声)
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 }
SDK(Software Development Kit)是 NXP 针对其官方评估版的软件开发包,可以在 NXP 的官网下载到。
官网下载:
访问 NXP 的 MCUXpresso 平台的链接
未登录的用户需要先登录 NXP 官网
在“SDK Dashboard”页面,点击“Select Development Board”
可以根据自己的需要添加 SDK 中包含的组件,例如 CMSIS DSP 库、FatFs 文件系统、USB、lwIP 协议栈、emwin 图形界面库、FreeRTOS 实时系统等。有些库占用的空间比较大,且初学的时候不需要那么多的组件,我们在默认的 FatFs、USB Stack、lwIP 之上增加选项即可
配置好以后点击下面的“Request Build”
SDK包下载
NXP 官方的 SDK 包一共有 7 个文件夹和 3 个其他文件
NXP 针对 RT1050 系列 MCU 写的一些例程
NXP 提供的例程共有 8 种,有 USB 的、emiwn 的、基础例程的和RTOS 操作系统的等等。学习 RT1052 重点就是参考官方 SDK 包里面提供的这些例程。
CMSIS 全称: Cortex Microcontroller SoftwareInterface Standard,叫做 Cortex 微控制器软件接口标准。
为了解决不同的芯片厂商生产的 Cortex 微控制器软件的兼容性问题,ARM 与芯片厂商建立了 CMSIS 标准 (Cortex MicroController SoftwareInterface Standard)。
此文件夹里面有一些重要的跟 Cortex-M 架构有关的头文件,在 include 文件夹里面,创建工程需要用到!此文件夹还有重要的 DSP 库,在 Lib 文件夹里面。
CMSISDriver 和 CMSISInclude 目录,它们分别对应 CMSI 核心层和设备外设函数层的内容。
在源文件中,可以看到它定义了一些串口可能会使用的函数原型,如 - -
这些函数都是空函数,,所以 ARM 也就是提供一个原型,具体代码还是由芯片厂商根据自己的外设来实现。
在头文件中,定义了一些使用串口外设时可能会产生的事件,例如串口外设发送完成后会在寄存器的标志位置 1,以告诉用户产生了该事件。
从这两个文件可以了解到,ARM 针对核外外设定义了 CMSIS 标准,相当于完成了软件架构师的工作
这就是 CMSIS 核心层,与前面只有架构的外设驱动不同,它们是由 ARM 公司提供的直接可用的内核驱动文件。
对于采用同样内核架构的芯片,芯片厂一般不会进行改动,由 ARM 根据自己内核定义出来的 CMSIS 核心层文件就是直接针对内核可用的文件。
ARM 还针对自己的芯片提供了 DSP 处理的运算库函数,例如定点运算、傅利叶变换、PID 算法等。
此文件夹是 RT1052 相关的东西,包括 FSL 库。
FSL 库就在 drivers 文件夹里面,我们创建库函数工程模板的时候就需要 FSL 库,还有一些其他的跟 RT1052 有关的.c 和.h 文件。
fsl_device_registers.h 文件的具体内容
2 * 根据 CPU 型号包含相应的头文件
3 * 在开发环境的全局宏定义中应根据 CPU 指定芯片型号
4 */
5 #if (defined(CPU_MIMXRT1052CVL5A) || defined(CPU_MIMXRT1052CVL5B))
6 #define MIMXRT1052_SERIES
7 /* CMSIS-style 寄存器定义的文件 */
8 #include "MIMXRT1052.h"
9 /* CPU 特性定义的文件 */
10 #include "MIMXRT1052_features.h"
11
12 #else
13 #error "No valid CPU defined!"
14 #endif
固件库通常可以兼容很多型号的芯片,不同的芯片部分寄存器定义、芯片特性等内容可能会有差异。
代码中宏 CPU_MIMXRT1052CVL5A 和宏 CPU_MIMXRT1052CVL5B 的分支下会包含RT1052 芯片对应的 MIMXRT1052.h 以及 MIMXRT1052_features.h 头文件。
所以在我们使用固件库编程的时候,会在开发环境定义一个全局的用于指定当前 CPU 型号的宏如“CPU_MIMXRT1052CVL5B”
MIMXRT1052.h 文件
主要是包含 RT1052 芯片的各种寄存器定义
MIMXRT1052_features.h 文件
此文件主要定义了一些关于 RT1052 芯片特性的内容,例如你想知道本芯片有多少个LPSPI 外设或 LPUART 外设,可以在本文件中找到。
1 #define FSL_FEATURE_SOC_LPSCI_COUNT (0)
2 /* @brief LPSPI availability on the SoC. */
3 #define FSL_FEATURE_SOC_LPSPI_COUNT (4)
4 /* @brief LPTMR availability on the SoC. */
5 #define FSL_FEATURE_SOC_LPTMR_COUNT (0)
6 /* @brief LPTPM availability on the SoC. */
7 #define FSL_FEATURE_SOC_LPTPM_COUNT (0)
8 /* @brief LPUART availability on the SoC. */
9 #define FSL_FEATURE_SOC_LPUART_COUNT (8)
system_MIMXRT1052.ch 文件
system_MIMXRT1052 包含一个源文件和头文件。其中头文件system_MIMXRT1052.h 中主要包含时钟、晶振定义以及源文件相应函数的声明
1 /* 定义时钟源的值 */
2 /* 外部晶振频率 */
3 #define CPU_XTAL_CLK_HZ 24000000UL
4 /* 默认系统时钟频率 */
5 #define DEFAULT_SYSTEM_CLOCK 528000000UL
源文件 system_MIMXRT1052.c 中则主要包含系统初始化和配置系统时钟的函数,例如前面我们以空函数代替的 SystemInit 函数,在这个文件中有了完整的定义
1 void SystemInit (void)
2 {
3 #if ((__FPU_PRESENT == 1) && (__FPU_USED == 1))
4 /* set CP10, CP11 Full Access */
5 SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
6 #endif /* ((__FPU_PRESENT == 1) && (__FPU_USED == 1)) */
7
8 #if defined(__MCUXPRESSO)
9 // Vector table defined in startup code
10 extern uint32_t g_pfnVectors[];
11 SCB->VTOR = (uint32_t)g_pfnVectors;
12 #endif
13
14 /* Watchdog disable */
15
16 #if (DISABLE_WDOG)
17 if (WDOG1->WCR & WDOG_WCR_WDE_MASK) {
18 WDOG1->WCR &= ~WDOG_WCR_WDE_MASK;
19 }
20 if (WDOG2->WCR & WDOG_WCR_WDE_MASK) {
21 WDOG2->WCR &= ~WDOG_WCR_WDE_MASK;
22 }
23 RTWDOG->CNT = 0xD928C520U; /* 0xD928C520U is the update key */
24 RTWDOG->TOVAL = 0xFFFF;
25 RTWDOG->CS = (uint32_t) ((RTWDOG->CS) &
26 ~RTWDOG_CS_EN_MASK) |
27 RTWDOG_CS_UPDATE_MASK;
28 #endif /* (DISABLE_WDOG) */
30 /* Disable Systick which might be enabled by bootrom */
31 if (SysTick->CTRL & SysTick_CTRL_ENABLE_Msk) {
32 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
33 }
34
35 /* Enable instruction and data caches */
36 #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT
37 SCB_EnableICache();
38 #endif
39 #if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT
40 SCB_EnableDCache();
41 #endif
42
43 }
这里默认关闭了看门狗、内核定时器等部件,并使能了代码缓冲功能加快指令获取与执行的速度
MIMXRT1052.xml 文件
MIMXRT1052.xml 文件是 NXP 的开发环境 MCUXpresso 需要的一些记录信息,此处不作介绍。
在不同的编译平台下,使用汇编语言编写的启动文件、各种版本的分散加载文件的语法稍有区别,所以固件库把这些内容放在独立的文件夹。
arm 平台的启动文件和分散加载文件:
cmsis_drivers目录下包含了一些NXP严格根据CMSIS标准编写的外设驱动文件
主要包含UART、I2C 及 SPI 这三种最常用的通讯的外设:
如果不是特别在乎其它平台的可移植性,通常会使用另外专有的外设驱动。
drivers 目录是固件库的主体,有时我们把这些文件称为外设驱动库
这些文件都使用 fsl_xxx.c/h 的命名格式,其中 xxx 是对应的片上外设名字,如 ADC。
特别地,其中的 fsl_common.c 和 fsl_common.h 中的 common 不是 RT1052 芯片的某个外设名字,它表示绝大多数工程都会需要这些“共同”的内容,所以一般工程都会添加这个fsl_common.c 文件
通常我们在编写源文件的时候,第一件事就是:#include“fsl_common.h”:
1 #include <assert.h>
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdlib.h>
6
7 #if defined(__ICCARM__)
8 #include <stddef.h>
9 #endif
10
11 #include "fsl_device_registers.h"
MIMXRT1052project_template 目录与前面 boardsproject_template 目录下都具有同名的文件,都是设计用于进行引脚定义、时钟配置功能的文件
但里边的内容是不同的
是针对 KEIL 软件的 RTE 功能提供的外设驱动原型,目前还不完善,此处不作介绍。
此目录下包含了开发常用的一些调试工具,如串口输出、运行日志、通知功能以及 shell
这些文件主要是在 NOR Flash 存储器使用 XIP(executed in place)功能时需要的启动或加载程序
此文件夹包含了一些常用的第三方组件,比如 fatfs、lijpeg 库、usb、emwi 等。
各个文件中间件的简要说明如下:
rtos 文件夹包含了 FreeRTOS 实时操作系统的源代码及移植范例,使用操作系统可进行多任务编程
tools 包含了一些开发工具,如 cmake,该工具可用于生成 GCC 开发环境编译需要的 makefile 文件
在 SDK 的根目录下还包含了几个 xml 和 txt 文件。它们主要是包含 MCUXpresso
生成的信息和 SDK 第三方构件的一些版权、出处说明
官方的帮助手册,是最好的教程,几乎包含了所有在开发过程中遇到的问题。
打开它下面的 docs 目录后
当中最重要的当属“MCUXpresso SDK API Reference Manual_MIMXRT105x”文件夹以及《MCUXpresso SDK API Reference Manual_MIMXRT105x.pdf》文件,
在开发软件的时候,在用到库函数的地方,直接把库帮助文档中的函数名称复制粘贴到工程文件就可以了。
API 文档中各个章节围绕 fsl_xxx.c 驱动文件进行说明。
以 GPIO 章节为例,它的 GPIO Driver 栏目下又包含了
点击对应的栏目,可以快速找到自己需要的内容
使用浏览器打开 arch.html 文件
GPIO部分可通过左侧的导航栏“API Reference->GPIO->GPIO Driver”部分找到
无论是 pdf 还是网页版的 API 参考手册,它们的说明来源都是程序注释
1 /*!
2 * @brief Sets the output level of the individual GPIO pin to logic 1 or 0.
3 *
4 * @param base GPIO base pointer.
5 * @param pin GPIO port pin number.
6 * @param output GPIOpin output logic level.
7 * - 0: corresponding pin output low-logic level.
8 * - 1: corresponding pin output high-logic level.
9 */
10 void GPIO_PinWrite(GPIO_Type* base, uint32_t pin, uint8_t output);
这些注释包含了一些奇怪的内容,如“/*!”、“@brief”、“@param”等,这些并不是 C 语言的注释要求,而是一种被称为“Doxygen”的注释风格,使用其同名的软件,可以根据这种注释风格的代码自动生成 API 文档。
《IMXRT1050RM》(参考手册)
《IMXRT1050CEC》(数据手册)
《DDI0489D_cortex_m7_trm》(Cortex-M7 技术参考手册)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。