RT1021之LED小灯闪烁(EVK)
-
第一步,先看看硬件部分,得看看连接到哪个GPIO口了
USER------GPIO_AD_B0_05 查阅手册观察GPIO引脚,默认引脚在数据手册(对应的管脚号)上:
知道了对应引脚名称,然后在软件上配置寄存器
复用功能为GPIO1 具体端口及引脚号 引脚名 复用功能序号
USER------GPIO_AD_B0_05 -------GPIO1_IO05(后面有用*)
了解了对应的引脚,接下来就是软件部分设计,主要是对例程进行讲解
由于是第一次讲解M7架构的MCU,所以要讲的详细些,我们先引申出一个存储器映射的概念
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,具体见图 5‑5。如果给存储器再分配一个地址就叫存储器重映射。
图 5‑5 Cortex-M7存储器映射
存储器区域功能划分
在这4GB的地址空间中,ARM已经粗线条的平均分成了8个块,,每个块也都规定了用途,具体分类见表格 5‑2。大部分块的大小都有512MB以上,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。表格 5‑2 内核划分的存储器功能分类
序号 |
用途 |
地址范围 |
---|---|---|
Block 0 |
Code |
0x0000 0000 ~ 0x1FFF FFFF(512MB) |
Block 1 |
SRAM |
0x2000 0000 ~ 0x3FFF FFFF(512MB) |
Block 2 |
Peripheral片上外设 |
0x4000 0000 ~ 0x5FFF FFFF(512MB) |
Block 3 |
External RAM(外部RAM) |
0x6000 0000 ~ 0x9FFF FFFF (1024MB) |
Block 4 |
External device(外部设备) |
0xA000 0000 ~ 0xDFFF FFFF(1024MB) |
Block 5 |
内核私有外设内部总线 |
0xE000 0000 ~ 0xE003 FFFF(256KB) |
Block 6 |
内核私有外设外部总线 |
0xE004 0000 ~ 0xE00F FFFF(768KB) |
Block 7 |
系统外设 |
0xE010 0000 ~ 0xFFFF FFFF(511MB) |
在这8个Block里面,有这3个块非常重要,也是我们最关心的三个块。Block0主要用于存储程序代码,一般采用FLASH存储器,Block1主要用于运行时的内存,一般采用SRAM存储器,Block2用来设计成片上的外设,内核通过相应的地址访问片上外设。下面我们简单的介绍下这三个Block里面的具体区域的功能划分。存储器Block0内部区域功能划分根据ARM内核的设计,Block0主要用于存储程序代码,在i.MX RT1021芯片内部又把这部分划分了几个类型。也就是说NXP之类的芯片厂商在ARM内核的存储器功能划分草图之上,细致地根据自己的芯片需求设计出具体的功能分配方案。RT1021芯片对Block0内部区域的功能划分具体见表格首先是ITCM,ITCM是Instruction Tightly-Coupled Memory的缩写,译为指令紧耦合内存。所谓紧耦合是指该内存与内核连接紧密,有非常高的访问速度,而“指令”则表示该内存专用于缓存指令。对于那些我们希望有着极高执行速度的代码,我们可以要求内核上电后把相应的代码从外部FLASH加载至ITCM,那么在运行时,代码的执行速度就不会因外部FLASH的访问速度而存在瓶颈。第二部分是ROMCP,这是一小段ROM空间,用于存储芯片启动时的加载代码,即bootloader,bootloader负责把指令从外部存储器加载至ITCM。第三部分中的SEMC及FlexSPI是RT1052可用于控制外部并行及串行NorFlash的两个外设,此处把它们映射到此代码空间,是为了支持XIP功能(即指令直接在NorFlash上运行,不需要加载到内部的ITCM)。储存器Block1内部区域功能划分Block1用于设计片内的SRAM,也就是芯片运行时的内存,在i.MX RT1021芯片内部把这部分划分了两种RAM类型,Block1内部区域的功能划分具体见表格 5‑4。
第一种类型为DTCM,是Data Tightly-Coupled Memory的缩写,译为数据紧耦合内存,它跟ITCM类似,有着极高的访问速度,不过它是专门用来存储程序数据的,即代码中变量的存储位置。第二种类型为OCRAM,它是On-chip RAM的缩写,即片上内存,可以完全把它理解为传统MCU的内部SRAM,它没有像ITCM和DTCM的专用限制,可用于存储指令和数据(通用目的)。注意:在RT1052芯片中,前面提到的ITCM、DTCM及OCRAM地址范围均分配了512KB,但这并不是说这三种存储器都有512KB大小。实际上这三种存储器共享内部FlexRAM的空间,而这个内部FlexRAM空间在RT1052芯片中为512KB,在RT1060芯片中为1MB。另外,这三种存储器的空间是可以动态调整的,如在RT1052中,默认ITCM和DTCM各占128KB,OCRAM占256KB,也可以调整成ITCM占64KB,DTCM占128KB,OCRAM占320KB,一共有16种配置方式,具体可参考《Using the i.MX RT FlexRAM》文档。储存器Block2内部区域功能划分Block2用于设计片内的外设,在RT1052芯片中,它的外设使用4条总线与内核进行连接,它们的地址分配情况见表格 5‑5。表格 5‑5 存储器Block2 内部区域功能划分(此处仅列出AIPS总线相关部分)
表格 5‑6 AIPS-2总线上的地址分配
该表格的最右一栏是挂载在AIPS-2总线上的外设名称,在后面的章节中我们会对相应的外设进行讲解。以表中阴影处为例,它表示一个名为GPIO1的外设的内存地址分配情况,而GPIO1外设下又包含了RT1021芯片相关的32个引脚,它被分配的内存地址为0x401B 8000~0x401B BFFF,也就是说通过访问这些地址就可以控制这32个引脚了,例如控制引脚输出高低电平,从而达到控制连接到引脚的LED灯亮灭的效果。我们知道,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?在上述存储器Block2这块区域,设计的是片上外设,在相应的地址空间内它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名对应的内存区就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。比如,我们找到GPIO1端口的输出数据寄存器DR的地址是0x401B 8000(至于这个地址如何找到可以先跳过,后面我们会有详细的讲解),DR寄存器是32bit,对应着32个外部IO,写0/1对应的的IO则输出低/高电平。现在我们通过C语言指针的操作方式,让GPIO1的32个IO都输出高电平,具体见代码清单 5‑1。
// GPIO1端口全部输出 高电平
*(unsigned int*)(0x401B8000) = 0xFFFFFFFF;
0x401B 8000在我们看来是GPIO1端口数据输出寄存器DR的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针,即(unsigned int *)0x401B 8000,然后再对这个指针进行 * 操作。刚刚我们说了,通过绝对地址访问内存单元不好记忆且容易出错,我们可以通过寄存器的方式来操作,具体见代码清单 5‑2。
// GPIO1 端口全部输出 高电平
#define GPIO1_DR (unsigned int*)(0x401B8000)
*GPIOF_DR = 0xFFFFFFFF;
为了方便操作,我们干脆把指针操作“*”也定义到寄存器别名里面,具体见代码清单 5‑3。
// GPIO1 端口全部输出 高电平
#define GPIO1_DR *(unsigned int*)(0x401B8000)
GPIOF_DR = 0xFFFFFFFF;
片上外设区的四条AIPS总线挂载着不同的外设,相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。
总线基地址
表格 5‑7 总线基地址
总线名称 | 总线基地址 |
---|---|
AIPS-1 | 0x4000 0000 |
AIPS-2 | 0x4010 0000 |
AIPS-3 | 0x4020 0000 |
AIPS-4 | 0x4030 0000 |
总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX外设基地址”,也叫XX外设的边界地址。具体有关RT1021外设的边界地址请参考《IMXRT1021RM》(参考手册)的第2章中的存储器映射的表。这里面我们以GPIO这个外设来讲解外设的基地址,具体见表格 5‑8。
表格 5‑8 外设GPIO基地址
外设名称 | 外设基地址 |
---|---|
GPIO1 | 0x401B 8000 |
GPIO2 | 0x401B C000 |
GPIO3 | 0x401C 0000 |
GPIO4 | 0x401C 4000 |
GPIO5 | 0x400C 0000 |
可以看到,GPIO1~GPIO4的外设基地址都位于AIPS-2总线地址的范围,而GPIO5比较特殊,它是属于AIPS-1总线的。不过除了所属总线及地址的差异外,它们的功能都一样。
外设寄存器
在XX外设的地址范围内,分布着的就是该外设的寄存器。以GPIO外设为例,GPIO是通用输入输出端口的简称,简单来说就是RT1052可控制的引脚,基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把GPIO的引脚连接到LED灯的阴极,LED灯的阳极接电源,然后通过RT1021控制该引脚的电平,从而实现控制LED灯的亮灭。GPIO有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占4个字节,在该外设的基地址上按照顺序排列,因此寄存器的位置可以用相对该外设基地址的偏移地址来描述。这里我们以GPIO1端口为例,来说明GPIO都有哪些寄存器,具体见表格 5‑8。表格 5‑9 GPIO1端口的寄存器地址列表
寄存器名称 | 类型 | 寄存器地址 | 相对于GPOI1的地址偏移 |
---|---|---|---|
GPIO1_DR | 数据寄存器 | 0x401B 8000 | 0x00 |
GPIO1_GDIR | 方向寄存器 | 0x401B 8004 | 0x04 |
GPIO1_PSR | 状态寄存器 | 0x401B 8008 | 0x08 |
GPIO1_ICR1 | 中断配置寄存器 | 0x401B 800C | 0x0C |
GPIO1_ICR2 | 中断配置寄存器 | 0x401B 8010 | 0x10 |
GPIO1_IMR | 中断掩码寄存器 | 0x401B 8014 | 0x14 |
GPIO1_ISR | 中断状态寄存器 | 0x401B 8018 | 0x18 |
GPIO1_EDGE_SEL | 边沿选择寄存器 | 0x401B 801C | 0x1C |
表中数据寄存器GPIO1_DR是GPIO1中的首个寄存器,所以它的寄存器地址与GPIO1的外设基地址相同,为0x401B 8000,它相对GPIO1基地址的偏移为0;紧挨着的是方向寄存器GPIO1_GDIR,由于前面的GPIO1_DR占据了4个字节,所以安排给它的地址也相对GPIO1_DR增加了4,最终地址为0x401B 8004,它相对GPIO1基地址增加了4,其余的寄存器地址安排依次类推。由于各个GPIO端口的控制方式完全一致,GPIO1~GPIO5都具有同样功能的寄存器用于控制对应端口引脚的特性,所以在《IMXRT1050RM》(参考手册)中对寄存器地址是统一用表格 5‑10来说明的。表格 5‑10 GPIO端口的 寄存器地址列表
表格上面的两行文字说明了GPIO1~GPIO5端口的基地址,然后表格列出各寄存器相对端口基地址的偏移。例如,想要知道GPIO2的中断掩码寄存器(IMR)实际地址时,可以根据公式算出:GPIO2 基地址:0x401B 8000+(2-1)×0x4000 = 0x401B C000IMR寄存器相对基地址的偏移为0x14,所以:GPIO2_IMR寄存器地址:0x401B C000 + 0x14 = 0x401B C014芯片对外设寄存器的这种统一安排不仅方便理解,也简化了程序中对寄存器地址的定义。有关外设的寄存器说明可参考《IMXRT1050RM》(参考手册)中具体章节的寄存器描述部分,在编程的时候我们需要反复的查阅外设的寄存器说明。这里我们以“GPIO中断配置寄存器GPIO_ICR1”为例,教大家如何理解寄存器的说明,具体见图 5‑6。
- 寄存器名称
寄存器说明中首先列出了该寄存器中的名称,同类型外设的寄存器说明是通用的,也就是说,本例子中的GPIO1~GPIO5端口都有这样的一个寄存器,都适用于本说明。
- 偏移地址
偏移地址是指本寄存器相对于这个外设的基地址的偏移。跟前面的说明一样,本寄存器的偏移地址是0x0C,从参考手册中我们可以查到GPIO1外设的基地址为0x401B 8000 ,我们就可以算出GPIO1的这个GPIO1_ICR1寄存器的地址为:0x401B 8000+0x0C ;同理,由于GPIO2的外设基地址为0x401B C000,可算出GPIO2_ICR1寄存器的地址为:0x401B C000+0x0C 。其他GPIO端口以此类推即可。
- 寄存器功能说明
它简要地了本寄存器的主要功能,此处表明GPIO_ICR1寄存器包含16组2位的配置域,每个配置域控制一个输入引脚的中断配置。
- 寄存器位表
紧接着的是本寄存器的位表,表中列出它的0-31位的名称及权限。表上方的数字为位编号,中间为位名称,侧面为读写权限,其中W表示只写,R表示只读,RW表示可读写。本寄存器中的位权限都是RW。有的寄存器位只读,一般是用于表示外设的某种工作状态的,由芯片硬件自动更改,程序通过读取那些寄存器位来判断外设的工作状态。表中最下一栏是对应寄存器位复位后的默认值,每位的值用0或1表示,本寄存器的复位值为全0。
- 配置域功能说明
配置域功能是寄存器说明中最重要的部分,它详细介绍了寄存器每一个位的功能。例如本寄存器中只有一种寄存器配置域,即ICRn,其中的n数值可以是0-15,这里的0-15表示端口的引脚号,如ICR0用于控制GPIOx的第0个引脚,若x表示GPIO1,那就是控制GPIO1的第0引脚,而ICR1就是控制GPIO1第1个引脚。对于GPIO中的16~31号引脚,不在本寄存器的控制范围之内,它们使用另一个寄存器GPIO_ICR2进行配置(此处请注意区分寄存器GPIO_ICR1、GPIO_ICR2和配置域ICR0~ICR15的概念)。图中仅截取了ICR15配置域的说明,其余ICR14~ICR0的部分是完全一样的。该说明以通用的ICRn[1:0]表述,其中n就是代表引脚号的0~15,当ICRn被设置成二进制数00b、01b、10b或11b时分别可把对应引脚设置成低电平、高电平、上升沿或下降沿时引起中断(b表示2进制数,跟h表示16进制数的方式类似)。也就是说,当我们想配置GPIO1编号为15的引脚设置成上升沿引起中断时,需要把GPIO1_ICR1寄存器(地址为0x401B 800C)中的第31~30位写入二进制数10b,其它引脚类似。
C语言对寄存器的封装
以上所有的关于存储器映射的内容,最终都是为大家更好地理解如何用C语言控制读写外设寄存器做准备,此处是本章的重点内容。
封装外设基地址
在编程上为了方便理解和记忆,我们把外设基地址和寄存器地址都以相应的宏定义起来,外设或寄存器都以他们的名字作为宏名,具体见代码清单 5‑4。
/* GPIO - 外设基地址 */
/** GPIO1 外设基地址 */
#define GPIO1_BASE (0x401B8000u)
/** GPIO2 外设基地址 */
#define GPIO2_BASE (0x401BC000u)
/** GPIO3 外设基地址 */
#define GPIO3_BASE (0x401C0000u)
/** GPIO4 外设基地址 */
#define GPIO4_BASE (0x401C4000u)
/** GPIO5 外设基地址 */
#define GPIO5_BASE (0x400C0000u)
/* 寄存器地址,以 GPIO1 为例*/
#define GPIO1_DR (GPIO1_BASE+0x00)
#define GPIO1_GDIR (GPIO1_BASE+0x04)
#define GPIO1_PSR (GPIO1_BASE+0x08)
#define GPIO1_ICR1 (GPIO1_BASE+0x0C)
#define GPIO1_ICR2 (GPIO1_BASE+0x10)
#define GPIO1_IMR (GPIO1_BASE+0x14)
#define GPIO1_ISR (GPIO1_BASE+0x18)
#define GPIO1_EDGE_SEL (GPIO1_BASE+0x1C)
这代码首先定义了GPIO1~GPIO5的外设基地址,最后在外设基地址上加入各寄存器的地址偏移,得到特定寄存器的地址。一旦有了具体地址,就可以用指针进行读写操作,具体见代码清单 5‑5。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/* 控制GPIO1 引脚6配置为高电平引起中断
(GPIO1_ICR1寄存器的ICR6设置为01b,即0x01) */
/* 先对配置域ICR6的2个数据位清0 */
*(unsigned int *)GPIO1_ICR1 &= ~(0x3<<(2*6));
/* 给配置域ICR6的2个数据位赋值01b */
*(unsigned int *)GPIO1_ICR1 |= (0x01<<(2*6));
/* 控制GPIO1 引脚6配置为上升沿引起中断
(GPIO1_ICR1寄存器的ICR6设置为10b,即0x02) */
/* 先对配置域ICR6的2个数据位清0 */
*(unsigned int *)GPIO1_ICR1 &= ~(0x3<<(2*6));
/* 给配置域ICR6的2个数据位赋值10b */
*(unsigned int *)GPIO1_ICR1 |= (0x02<<(2*6));
unsigned int temp;
/* 控制GPIO1 端口所有引脚的电平(读DR寄存器) */
temp = *(unsigned int *)GPIO1_DR;
|
该代码使用 (unsigned int *) 把GPIO1_ICR1宏的数值强制转换成了地址,然后再用“*”号做取指针操作,对该地址的赋值,从而实现了写寄存器的功能(整个过程包括位清零及赋值两个过程,具体说明见本教程5.5.3 小节)。同样,读寄存器也是用取指针操作,把寄存器中的数据取到变量里,从而获取外设的状态。
封装寄存器列表
用上面的方法去定义地址,还是稍显繁琐,例如GPIO1~GPIO5都各有一组功能相同的寄存器,如GPIO1_DR/GPIO2_DR/GPIO3_DR等等,它们只是地址不一样,但却要为每个寄存器都定义它的地址。为了更方便地访问寄存器,我们引入C语言中的结构体语法对寄存器进行封装,具体见代码清单 5‑6。
typedef unsigned int uint32_t; /*无符号32位变量*/
typedef unsigned short int uint16_t; /*无符号16位变量*/
/** GPIO - 寄存器列表 */
typedef struct {
uint32_t DR; /**< GPIO 数据寄存器, 地址偏移: 0x0 */
uint32_t GDIR; /**< GPIO 方向寄存器, 地址偏移: 0x4 */
uint32_t PSR; /**< GPIO 状态寄存器, 地址偏移: 0x8 */
uint32_t ICR1; /**< GPIO 中断配置寄存器1, 地址偏移: 0xC */
uint32_t ICR2; /**< GPIO 中断配置寄存器2, 地址偏移: 0x10 */
uint32_t IMR; /**< GPIO 中断掩码寄存器, 地址偏移: 0x14 */
uint32_t ISR; /**< GPIO 中断状态寄存器, 地址偏移: 0x18 */
uint32_t EDGE_SEL; /**< GPIO 边沿选择寄存器, 地址偏移: 0x1C */
} GPIO_Type;
这段代码用typedef 关键字声明了名为GPIO_Type的结构体类型,结构体内有8个 成员变量,变量名正好对应寄存器的名字。C语言的语法规定,结构体内变量的存储空间是连续的,其中32位的变量
占用4个字节,16位的变量占用2个字节,具体见图 5‑7
。
图 5‑7 GPIO_Type结构体成员的地址偏移也就是说,假如我们定义一个GPIO_Type 类型的结构体,且结构体的首地址为0x401B 8000(这也是第一个成员变量DR的地址), 那么结构体中第二个成员变量GDIR的地址即为0x401B 8000 +0x04 ,加上的这个0x04 ,正是代表DR所占用的4个字节地址的偏移量,其它成员变量相对于结构体首地址的偏移,在上述代码右侧注释已给出。这样的地址偏移与RT1052的 GPIO外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器了,具体见代码清单 5‑7。
GPIO_Type * GPIOx; //定义一个GPIO_Type型结构体指针变量GPIOx
GPIOx = GPIO1_BASE; //把指针地址设置为宏GPIO1_BASE地址
GPIOx->DR = 0xFFFF; //通过指针访问并修改GPIO1_DR寄存器
GPIOx->GDIR = 0xFFFFFFFF; //修改GPIO1_GDIR寄存器
GPIOx->ICR1 =0xFFFFFFFF; //修改GPIO1_ICR1寄存器
uint32_t temp;
temp = GPIOx->DR; //读取GPIOF_DR寄存器的值到变量temp中
这段代码先用GPIO_Type类型定义一个结构体指针GPIOx,并让指针指向地址GPIO1_BASE(0x401B 8000),使地址确定下来,然后根据C语言访问结构体的语法,用GPIOx->DR、GPIOx->GDIR及GPIOx->ICR1等方式读写寄存器。最后,我们更进一步,直接使用宏来定义好GPIO_Type类型的指针,而且指针指向各个GPIO端口的基地址,使用时我们直接用该宏访问寄存器即可,具体代码清单 5‑8。
/*使用GPIO_Type把地址强制转换成指针*/
#define GPIO1 ((GPIO_Type *) GPIO1_BASE)
#define GPIO2 ((GPIO_Type *) GPIO2_BASE)
#define GPIO3 ((GPIO_Type *) GPIO3_BASE)
#define GPIO4 ((GPIO_Type *) GPIO4_BASE)
#define GPIO5 ((GPIO_Type *) GPIO5_BASE)
/*使用定义好的宏直接访问*/
/*访问GPIO1端口的寄存器*/
GPIO1->DR = 0xFFFF; //通过指针访问并修改GPIO1_DR寄存器
GPIO1->GDIR = 0xFFFFFFF; //修改GPIO1_GDIR寄存器
GPIO1->ICR1 =0xFFFFFFF; //修改GPIO1_ICR1寄存器
uint32_t temp;
temp = GPIO1->DR; //读取GPIO1_DR寄存器的值到变量temp中
/*访问GPIO5端口的寄存器*/
GPIO5->DR = 0xFFFF; //通过指针访问并修改GPIO5_DR寄存器
GPIO5->GDIR = 0xFFFFFFF; //修改GPIO5_GDIR寄存器
GPIO5->ICR1 =0xFFFFFFF; //修改GPIO5_ICR1寄存器
uint32_t temp;
temp = GPIO5->IDR; //读取GPIO5_DR寄存器的值到变量temp中
这里我们仅是以GPIO这个外设为例,给大家讲解了C语言对寄存器的封装。以此类推,其他外设也同样可以用这种方法来封装。好消息是,这部分工作都由固件库帮我们完成了,这里我们只是分析了下这个封装的过程,让大家知其然,也只其所以然。
修改寄存器的位操作方法
使用C语言对寄存器赋值时,我们常常要求只修改该寄存器的某几位的值,且其它的寄存器位不变,这个时候我们就需要用到C语言的位操作方法了。
把变量的某位清零
此处我们以变量a代表寄存器,并假设寄存器中本来已有数值,此时我们需要把变量a的某一位清零,且其它位不变,方法见代码清单 5‑9。
把变量的某几个连续位清零
由于寄存器中有时会有连续几个寄存器位用于控制某个功能,现假设我们需要把寄存器的某几个连续位清零,且其它位不变,方法见代码清单 5‑10。
对变量的某几位进行赋值。
寄存器位经过上面的清零操作后,接下来就可以方便地对某几位写入所需要的数值了,且其它位不变,方法见代码清单 5‑11,这时候写入的数值一般就是需要设置寄存器的位参数。
//a = 1000 0011 b
//此时对清零后的第2组bit4、bit5设置成二进制数“01 b ”
a |= (1<<2*2);
//a = 1001 0011 b,成功设置了第2组的值,其它组不变
对变量的某位取反
某些情况下,我们需要对寄存器的某个位进行取反操作,即 1变0 ,0变1,这可以直接用如下操作,其它位不变,见代码清单 5‑12。
GPIO框图剖析
上图为RT1021框图细节图 7‑1 GPIO结构框图通过GPIO硬件结构框图,就可以从整体上深入了解GPIO外设及它的各种应用模式。该图从最右端看起,①中的就表示RT1021芯片引出的GPIO引脚,其余部件都位于芯片内部。7.2.1. 基本结构分析
下面我们按图 7‑1中的编号对GPIO端口的结构部件进行说明。7.2.1.1. PAD
PAD代表了一个RT1052的GPIO引脚。在它的左侧是一系列信号通道及控制线,如input_on控制输入开关,Dir控制引脚的输入输出方向,Data_out控制引脚输出高低电平,Data_in作为信号输入,这些信号都经过一个IOMUX的器件连接到左侧的寄存器。另外,对于每个引脚都有很多关于属性的配置,这些配置是由图 7‑2中的框架结构实现的。图 7‑2 PAD接口框架①PAD引脚
框图中的最右侧的PAD同样是代表一个RT1052的引脚。②输出缓冲区
当输出缓冲区使能时,引脚被配置为输出模式。在输出缓冲区中,又包含了如下的属性配置:- DSE驱动能力
DSE可以调整芯片内部与引脚串联电阻R0的大小,从而改变引脚的驱动能力。例如,R0的初始值为260欧姆,在3.3V电压下其电流驱动能力为12.69mA,通过DSE可以把R0的值配置为原值的1/2、1/3…1/7等。
- SRE压摆率配置
压摆率是指电压转换速率,可理解为电压由波谷升到波峰的时间。增大压摆率可减少输出电压的上升时间。RT1052的引脚通过SRE支持低速和高速压摆率这两种配置。压摆率是大信号特性,下面的带宽是小信号特性。
- SPEED带宽配置
通过SPEED可以设置IO的带宽,分别可设置为50MHz、100MHz以及200MHz。带宽的意思是能通过这个IO口最高的信号频率,通俗点讲就是方波不失真,如果超过这个频率方波就变正弦波。但是这个带宽要区别于IO的翻转速率,IO的翻转速率的信号来自于GPIO这个外设,而IO的带宽只是限制了IO口引脚的物理特性,IO口的信号可以来自于内部定时器输出的PWM信号,也可以来自于GPIO翻转输出的信号,两者相比之下,PWM信号的频率是远远高于GPIO翻转输出的信号频率。况且1052没有高速GPIO,GPIO的翻转率约为10M,1060系列则有高速GPIO,GPIO翻转速率达到了逆天的150M。但要使用1060的快速GPIO,需要改用GPIO编号。
- ODE开漏输出配置
通过ODE可以设置引脚是否工作在开漏输出模式。在该模式时引脚可以输出高阻态和低电平,输出高阻态时可由外部上拉电阻拉至高电平。开漏输出模式常用在一些通讯总线中,如I2C。
③输入缓冲区
当输入缓冲区使能时,引脚被配置为输入模式。在输入缓冲区中,又包含了如下的属性配置- HYS滞后使能
RT1052的输入检测可以使用普通的CMOS检测或施密特触发器模式(滞后模式)。施密特触发器具有滞后效应,对正向和负向变化的输入信号有不同的阈值电压,常被用于电子开关、波形变换等场合,其转换特性和对比见图 7‑3及图 7‑4,如检测按键时,使用施密特模式即可起到消抖的功能。
④Pull/Keeper上下拉、保持器
引脚的控制逻辑中还包含了上下拉、保持器的功能。芯片内部的上拉和下拉电阻可以将不确定的信号钳位在高、低电平,或小幅提高的电流输出能力,上拉提供输出电流,下拉提供输入电流。注意这些上下拉配置只是弱拉,对于类似I2C之类的总线,还是必须使用外部上拉电阻。RT1052芯片的电源模块中包含转换器,当转换器停止工作时,保持器会保持输入输出电压。上下拉、保持器可以通过如下属性配置:- PUS上下拉配置
PUS可配置项可选为100K欧下拉以及22K欧、47K欧及100K欧上拉。
- PUE上下拉、保持器选择
上下拉功能和保持器功能是二选一的,可以通过PUE来选择。
- PKE上下拉、保持器配置
上下拉功能和保持器还通过PKE来控制是否使能。
IOMUX复用选择器
继续分析图 7‑1,图中标注的第2部分IOMUX译为IO复用选择器。RT1052的芯片每个GPIO都通过IOMUX支持多种功能,例如一个IO可用于网络外设ENET的数据接收引脚,也可以被配置成PWM外设的输出引脚,这样的设计大大增加了芯片的适用性,这样可选的功能就是由IOMUX实现的。IOMUX相当于增加了多根内部信号线与IO引脚相连,最多有8根,也就是说一个IO最多可支持8种可选的功能。IOMUX由其左侧的IOMUXC控制(C表示Controler),IOMUXC提供寄存器给用户进行配置,它又分成MUX Mode(IO模式控制)以及Pad Settings(Pad配置)两个部分:- MUX Mode配置
MUX Mode就是用来配置引脚的复用功能,按上面的例子,即是具体是用于网络外设ENET的数据接收,还是用于PWM外设的输出引脚,当然,也可以配置成普通的IO口,仅用于控制输出高低电平。
- Pad Settings配置
Pad Settings用于配置引脚的属性,例如驱动能力,是否使用上下拉电阻,是否使用保持器,是否使用开漏模式以及使用施密特模式还是CMOS模式等。关于属性的介绍会在后面给出,在学习各种外设时,也将会接触到这些属性在不同场合下的应用。
IOMUXC控制类型 | 寄存器名称 |
---|---|
MUX Mode | IOMUXC_SW_MUX_CTL_PAD_XXXX |
Pad Settings | IOMUXC_SW_PAD_CTL_PAD_XXXX |
IOMUXC_SW_MUX_CTL_PAD_XXXX引脚模式寄存器
下面以GPIO_AD_B0_09引脚为例对 MUX寄存器进行说明,该引脚相应的MUX寄存器在参考手册中的描述见图 7‑5。图 7‑5 参考手册中对GPIO1.9引脚MUX配置寄存器的说明 可以看到,该寄存器主要有两个配置域,分别是SION和MUX_MODE,其中SION用于设置引脚在输出模式下同时开启输入通道。重点是MUX_MODE的配置,它使用3个寄存器位表示可选的ALT0~ALT7这8个模式,如ALT2模式就是用于ENET以太网外设的数据接收信号;若配置为ALT4则引脚会连接至CSI摄像头外设,作为数据信号;配置为ALT5时,该引脚则用于最基础的GPIO模式,简单地通过后面介绍的GPIO外设控制输入输出。IOMUXC_SW_PAD_CTL_PAD_XXXX引脚属性寄存器
类似地,以GPIO_AD_B0_09引脚中PAD寄存器在参考手册中的描述见图 7‑6。图 7‑6 参考手册中对GPIO1.9引脚PAD配置寄存器的说明(部分)相对来说PAD寄存器的配置项就更丰富了,而且图中仅是该寄存器的部分说明,仔细看这些配置项,它们就是前面图 7‑2介绍的各项属性,如HYS设置使用施密特模式的滞后功能,PUS配置上下拉电阻的阻值,其它的还包含PUE、PKE、ODE、SPEED、DSE及SRE的配置。Block外设功能控制块
Block是外设功能控制块,例如具有ENET的数据接收功能的引脚,它就需要网络外设ENET的支持,具有PWM输出功能的引脚,它需要PWM外设的支持,这些外设在芯片内部会有独立的功能逻辑控制块,这些控制块通过IOMUX的复用信号与IO引脚相连。使用时通过IOMUX选择具体哪个外设连接到IO。GPIO外设
GPIO模块是每个IO都具有的外设,它具有IO控制最基本的功能,如输出高低电平、检测电平输入等。它也占用IOMUX分配的复用信号,也就是说使用GPIO模块功能时同样需要使用IOMUX选中GPIO外设。图中的GPIO.DR、GPIO.GDIR、GPIO.PSR等是指GPIO外设相关的控制寄存器,它们分别是数据寄存器、方向寄存器以及引脚状态寄存器,功能介绍如下:GPIO.GDIR方向寄存器
控制一个GPIO引脚时,要先用GDIR方向寄存器配置该引脚用于输出电平信号还是用作输入检测。典型的例子是使用输出模式可以控制LED灯的亮灭,输入模式时可以用来检测按键是否按下。GDIR寄存器的每一个数据位代表一个引脚的方向,对应的位被设置为0时该引脚为输入模式,被设置为1时该引脚为输出模式,具体见图 7‑7。图 7‑7 参考手册中对GDIR的寄存器说明例如,对GPIO1的GDIR寄存器的bit3位被写入为1,那么GPIO1.3引脚的模式即为输出。GPIO.DR数据寄存器
DR数据寄存器直接代表了引脚的电平状态,它也使用1个数据位表示1个引脚的电平,每位用1表示高电平,用0表示低电平。DR寄存器在参考手册中的说明见图 7‑8。图 7‑8 参考手册中对DR数据寄存器的说明当GDIR方向寄存器设置引脚为输出模式时,写入DR数据寄存器对应的位即可控制该引脚输出的电平状态,如这时GPIO1的DR寄存器的bit3被写入为1,则引脚为输出高电平。当GDIR方向寄存器设置引脚为输入模式时,读取DR数据寄存器对应的位即可获取该引脚当前的输入电平状态,例如这里读取GPIO1的DR寄存器的bit3,得到该位的值为0,表示当前引脚的输入状态为低电平。GPIO.PSR引脚状态寄存器
PSR引脚状态寄存器相当于DR寄存器的简化版,它仅在GDIR方向寄存器设置为输入模式时有效,它的每个位表示一个引脚当前的输入电平状态。PSR寄存器的权限是只读的,对它进行写操作是无效的。特别地,当引脚被配置成输出模式时,若IOMUXC中的MUX寄存器使能了SION功能(输出通道回环至输入),可以通过PSR寄存器读取回引脚的状态值。与其它引脚的连接
GPIO功能框中的第5项表示另一个引脚PAD2,它与PAD1有一根信号线连接,表示部分引脚的输出可以作为另一个引脚的输入。实验:使用寄存器点亮LED灯
本小节中,我们以实例讲解如何通过控制寄存器来点亮LED灯。此处侧重于讲解原理,请您直接用KEIL5软件打开我们提供的实验例程配合阅读,先了解原理,学习完本小节后,再尝试自己建立一个同样的工程。本节配套例程名称为“使用寄存器点亮LED灯”,在工程目录下找到后缀为“.uvprojx”的文件,用KEIL5打开即可。 打开该工程,具体见图 7‑9,可看到一共有三个文件,分别startup_MIMXRT1052.s、MIMXRT1052.h 以及main.c,接下来我们讲会对这三个工程文件进行讲解。图 7‑9 工程文件结构硬件连接
在本教程中核心板上RT1021芯片的GPIO1_IO05引脚与一个LED灯连接,这可以在核心板原理图查到,具体见图 7‑10。注意:如果以后硬件有升级,则以最新的硬件接线为主,不要纠结与具体使用的是哪个IO,重点是要掌握原理。图 7‑10表示LED灯的阳极串联一个电阻与电源3.3V相连,阴极与标号为JTAG_nTRST的引脚相连,所以当JTAG_nTRST引脚为低电平的时候,LED灯就会被点亮。在RT1052芯片中这个JTAG_nTRST引脚同时是GPIO1_IO05引脚,即GPIO1端口编号05的引脚。 单看LED灯部分的原理图,有时并不清楚这个JTAG_TDI引脚与电路中的其它模块是如何连接的,这时就可以在pdf软件的搜索栏输入要查询的引脚标号查看其它模块的连接了,见图 7‑11。 图 7‑11在原理图中查找引脚标号,摘自《i.MX RT1021核心板原理图》这个电路模块包含了RT1021芯片的部分引脚,从中可以查询到标号为JTAG_nTRST的信号直接与芯片的GPIO_AD_B0_05引脚相连,在芯片的参考手册中,就是直接用这个引脚名命名寄存器的,如它对应的MUX寄存器名为:SW_MUX_CTL_PAD_GPIO_AD_B0_05,了解这些在后续写程序的时候会方便我们查阅相关资料。现在,我们的目标是把这个与LED灯相连的GPIO_IO05(GPIO_AD_B0_09)引脚设置成输出模式并且输出低电平,这样就能让LED灯亮起来了。
MIMXRT1021.h文件
看完启动文件,那我们立即写SystemInit和main函数吧?别着急,定义好了SystemInit函数和main我们又能写什么内容?连接LED灯的GPIO引脚,是要通过读写寄存器来控制的,就这样空着手,如何控制寄存器。在上一章,我们知道寄存器就是特殊的内存空间,可以通过指针操作访问寄存器。所以此处我们根据RT1052的存储器映射先定义好各个寄存器的地址,把这些地址定义都统一写在MIMXRT1052.h文件中,见代码清单 7‑3。#define GPIO_DR_DR_MASK (0xFFFFFFFFU)
#define GPIO_DR_DR_SHIFT (0U)
/*! DR - DR data bits
*/
#define GPIO_DR_DR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_DR_DR_SHIFT)) & GPIO_DR_DR_MASK)
/*! @} */
/*! @name GDIR - GPIO direction register */
/*! @{ */
#define GPIO_GDIR_GDIR_MASK (0xFFFFFFFFU)
#define GPIO_GDIR_GDIR_SHIFT (0U)
/*! GDIR - GPIO direction bits
*/
#define GPIO_GDIR_GDIR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_GDIR_GDIR_SHIFT)) & GPIO_GDIR_GDIR_MASK)
/*! @} */
/*! @name PSR - GPIO pad status register */
/*! @{ */
#define GPIO_PSR_PSR_MASK (0xFFFFFFFFU)
#define GPIO_PSR_PSR_SHIFT (0U)
/*! PSR - GPIO pad status bits
*/
#define GPIO_PSR_PSR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_PSR_PSR_SHIFT)) & GPIO_PSR_PSR_MASK)
/*! @} */
/*! @name ICR1 - GPIO interrupt configuration register1 */
/*! @{ */
#define GPIO_ICR1_ICR0_MASK (0x3U)
#define GPIO_ICR1_ICR0_SHIFT (0U)
/*! ICR0 - Interrupt configuration field for GPIO interrupt 0
* 0b00..Interrupt 0 is low-level sensitive.
* 0b01..Interrupt 0 is high-level sensitive.
* 0b10..Interrupt 0 is rising-edge sensitive.
* 0b11..Interrupt 0 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR0(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR0_SHIFT)) & GPIO_ICR1_ICR0_MASK)
#define GPIO_ICR1_ICR1_MASK (0xCU)
#define GPIO_ICR1_ICR1_SHIFT (2U)
/*! ICR1 - Interrupt configuration field for GPIO interrupt 1
* 0b00..Interrupt 1 is low-level sensitive.
* 0b01..Interrupt 1 is high-level sensitive.
* 0b10..Interrupt 1 is rising-edge sensitive.
* 0b11..Interrupt 1 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR1(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR1_SHIFT)) & GPIO_ICR1_ICR1_MASK)
#define GPIO_ICR1_ICR2_MASK (0x30U)
#define GPIO_ICR1_ICR2_SHIFT (4U)
/*! ICR2 - Interrupt configuration field for GPIO interrupt 2
* 0b00..Interrupt 2 is low-level sensitive.
* 0b01..Interrupt 2 is high-level sensitive.
* 0b10..Interrupt 2 is rising-edge sensitive.
* 0b11..Interrupt 2 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR2(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR2_SHIFT)) & GPIO_ICR1_ICR2_MASK)
#define GPIO_ICR1_ICR3_MASK (0xC0U)
#define GPIO_ICR1_ICR3_SHIFT (6U)
/*! ICR3 - Interrupt configuration field for GPIO interrupt 3
* 0b00..Interrupt 3 is low-level sensitive.
* 0b01..Interrupt 3 is high-level sensitive.
* 0b10..Interrupt 3 is rising-edge sensitive.
* 0b11..Interrupt 3 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR3(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR3_SHIFT)) & GPIO_ICR1_ICR3_MASK)
#define GPIO_ICR1_ICR4_MASK (0x300U)
#define GPIO_ICR1_ICR4_SHIFT (8U)
/*! ICR4 - Interrupt configuration field for GPIO interrupt 4
* 0b00..Interrupt 4 is low-level sensitive.
* 0b01..Interrupt 4 is high-level sensitive.
* 0b10..Interrupt 4 is rising-edge sensitive.
* 0b11..Interrupt 4 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR4(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR4_SHIFT)) & GPIO_ICR1_ICR4_MASK)
#define GPIO_ICR1_ICR5_MASK (0xC00U)
#define GPIO_ICR1_ICR5_SHIFT (10U)
/*! ICR5 - Interrupt configuration field for GPIO interrupt 5
* 0b00..Interrupt 5 is low-level sensitive.
* 0b01..Interrupt 5 is high-level sensitive.
* 0b10..Interrupt 5 is rising-edge sensitive.
* 0b11..Interrupt 5 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR5(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR5_SHIFT)) & GPIO_ICR1_ICR5_MASK)
#define GPIO_ICR1_ICR6_MASK (0x3000U)
#define GPIO_ICR1_ICR6_SHIFT (12U)
/*! ICR6 - Interrupt configuration field for GPIO interrupt 6
* 0b00..Interrupt 6 is low-level sensitive.
* 0b01..Interrupt 6 is high-level sensitive.
* 0b10..Interrupt 6 is rising-edge sensitive.
* 0b11..Interrupt 6 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR6(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR6_SHIFT)) & GPIO_ICR1_ICR6_MASK)
#define GPIO_ICR1_ICR7_MASK (0xC000U)
#define GPIO_ICR1_ICR7_SHIFT (14U)
/*! ICR7 - Interrupt configuration field for GPIO interrupt 7
* 0b00..Interrupt 7 is low-level sensitive.
* 0b01..Interrupt 7 is high-level sensitive.
* 0b10..Interrupt 7 is rising-edge sensitive.
* 0b11..Interrupt 7 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR7(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR7_SHIFT)) & GPIO_ICR1_ICR7_MASK)
#define GPIO_ICR1_ICR8_MASK (0x30000U)
#define GPIO_ICR1_ICR8_SHIFT (16U)
/*! ICR8 - Interrupt configuration field for GPIO interrupt 8
* 0b00..Interrupt 8 is low-level sensitive.
* 0b01..Interrupt 8 is high-level sensitive.
* 0b10..Interrupt 8 is rising-edge sensitive.
* 0b11..Interrupt 8 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR8(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR8_SHIFT)) & GPIO_ICR1_ICR8_MASK)
#define GPIO_ICR1_ICR9_MASK (0xC0000U)
#define GPIO_ICR1_ICR9_SHIFT (18U)
/*! ICR9 - Interrupt configuration field for GPIO interrupt 9
* 0b00..Interrupt 9 is low-level sensitive.
* 0b01..Interrupt 9 is high-level sensitive.
* 0b10..Interrupt 9 is rising-edge sensitive.
* 0b11..Interrupt 9 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR9(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR9_SHIFT)) & GPIO_ICR1_ICR9_MASK)
#define GPIO_ICR1_ICR10_MASK (0x300000U)
#define GPIO_ICR1_ICR10_SHIFT (20U)
/*! ICR10 - Interrupt configuration field for GPIO interrupt 10
* 0b00..Interrupt 10 is low-level sensitive.
* 0b01..Interrupt 10 is high-level sensitive.
* 0b10..Interrupt 10 is rising-edge sensitive.
* 0b11..Interrupt 10 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR10(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR10_SHIFT)) & GPIO_ICR1_ICR10_MASK)
#define GPIO_ICR1_ICR11_MASK (0xC00000U)
#define GPIO_ICR1_ICR11_SHIFT (22U)
/*! ICR11 - Interrupt configuration field for GPIO interrupt 11
* 0b00..Interrupt 11 is low-level sensitive.
* 0b01..Interrupt 11 is high-level sensitive.
* 0b10..Interrupt 11 is rising-edge sensitive.
* 0b11..Interrupt 11 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR11(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR11_SHIFT)) & GPIO_ICR1_ICR11_MASK)
#define GPIO_ICR1_ICR12_MASK (0x3000000U)
#define GPIO_ICR1_ICR12_SHIFT (24U)
/*! ICR12 - Interrupt configuration field for GPIO interrupt 12
* 0b00..Interrupt 12 is low-level sensitive.
* 0b01..Interrupt 12 is high-level sensitive.
* 0b10..Interrupt 12 is rising-edge sensitive.
* 0b11..Interrupt 12 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR12(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR12_SHIFT)) & GPIO_ICR1_ICR12_MASK)
#define GPIO_ICR1_ICR13_MASK (0xC000000U)
#define GPIO_ICR1_ICR13_SHIFT (26U)
/*! ICR13 - Interrupt configuration field for GPIO interrupt 13
* 0b00..Interrupt 13 is low-level sensitive.
* 0b01..Interrupt 13 is high-level sensitive.
* 0b10..Interrupt 13 is rising-edge sensitive.
* 0b11..Interrupt 13 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR13(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR13_SHIFT)) & GPIO_ICR1_ICR13_MASK)
#define GPIO_ICR1_ICR14_MASK (0x30000000U)
#define GPIO_ICR1_ICR14_SHIFT (28U)
/*! ICR14 - Interrupt configuration field for GPIO interrupt 14
* 0b00..Interrupt 14 is low-level sensitive.
* 0b01..Interrupt 14 is high-level sensitive.
* 0b10..Interrupt 14 is rising-edge sensitive.
* 0b11..Interrupt 14 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR14(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR14_SHIFT)) & GPIO_ICR1_ICR14_MASK)
#define GPIO_ICR1_ICR15_MASK (0xC0000000U)
#define GPIO_ICR1_ICR15_SHIFT (30U)
/*! ICR15 - Interrupt configuration field for GPIO interrupt 15
* 0b00..Interrupt 15 is low-level sensitive.
* 0b01..Interrupt 15 is high-level sensitive.
* 0b10..Interrupt 15 is rising-edge sensitive.
* 0b11..Interrupt 15 is falling-edge sensitive.
*/
#define GPIO_ICR1_ICR15(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR1_ICR15_SHIFT)) & GPIO_ICR1_ICR15_MASK)
/*! @} */
/*! @name ICR2 - GPIO interrupt configuration register2 */
/*! @{ */
#define GPIO_ICR2_ICR16_MASK (0x3U)
#define GPIO_ICR2_ICR16_SHIFT (0U)
/*! ICR16 - Interrupt configuration field for GPIO interrupt 16
* 0b00..Interrupt 16 is low-level sensitive.
* 0b01..Interrupt 16 is high-level sensitive.
* 0b10..Interrupt 16 is rising-edge sensitive.
* 0b11..Interrupt 16 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR16(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR16_SHIFT)) & GPIO_ICR2_ICR16_MASK)
#define GPIO_ICR2_ICR17_MASK (0xCU)
#define GPIO_ICR2_ICR17_SHIFT (2U)
/*! ICR17 - Interrupt configuration field for GPIO interrupt 17
* 0b00..Interrupt 17 is low-level sensitive.
* 0b01..Interrupt 17 is high-level sensitive.
* 0b10..Interrupt 17 is rising-edge sensitive.
* 0b11..Interrupt 17 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR17(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR17_SHIFT)) & GPIO_ICR2_ICR17_MASK)
#define GPIO_ICR2_ICR18_MASK (0x30U)
#define GPIO_ICR2_ICR18_SHIFT (4U)
/*! ICR18 - Interrupt configuration field for GPIO interrupt 18
* 0b00..Interrupt 18 is low-level sensitive.
* 0b01..Interrupt 18 is high-level sensitive.
* 0b10..Interrupt 18 is rising-edge sensitive.
* 0b11..Interrupt 18 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR18(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR18_SHIFT)) & GPIO_ICR2_ICR18_MASK)
#define GPIO_ICR2_ICR19_MASK (0xC0U)
#define GPIO_ICR2_ICR19_SHIFT (6U)
/*! ICR19 - Interrupt configuration field for GPIO interrupt 19
* 0b00..Interrupt 19 is low-level sensitive.
* 0b01..Interrupt 19 is high-level sensitive.
* 0b10..Interrupt 19 is rising-edge sensitive.
* 0b11..Interrupt 19 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR19(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR19_SHIFT)) & GPIO_ICR2_ICR19_MASK)
#define GPIO_ICR2_ICR20_MASK (0x300U)
#define GPIO_ICR2_ICR20_SHIFT (8U)
/*! ICR20 - Interrupt configuration field for GPIO interrupt 20
* 0b00..Interrupt 20 is low-level sensitive.
* 0b01..Interrupt 20 is high-level sensitive.
* 0b10..Interrupt 20 is rising-edge sensitive.
* 0b11..Interrupt 20 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR20(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR20_SHIFT)) & GPIO_ICR2_ICR20_MASK)
#define GPIO_ICR2_ICR21_MASK (0xC00U)
#define GPIO_ICR2_ICR21_SHIFT (10U)
/*! ICR21 - Interrupt configuration field for GPIO interrupt 21
* 0b00..Interrupt 21 is low-level sensitive.
* 0b01..Interrupt 21 is high-level sensitive.
* 0b10..Interrupt 21 is rising-edge sensitive.
* 0b11..Interrupt 21 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR21(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR21_SHIFT)) & GPIO_ICR2_ICR21_MASK)
#define GPIO_ICR2_ICR22_MASK (0x3000U)
#define GPIO_ICR2_ICR22_SHIFT (12U)
/*! ICR22 - Interrupt configuration field for GPIO interrupt 22
* 0b00..Interrupt 22 is low-level sensitive.
* 0b01..Interrupt 22 is high-level sensitive.
* 0b10..Interrupt 22 is rising-edge sensitive.
* 0b11..Interrupt 22 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR22(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR22_SHIFT)) & GPIO_ICR2_ICR22_MASK)
#define GPIO_ICR2_ICR23_MASK (0xC000U)
#define GPIO_ICR2_ICR23_SHIFT (14U)
/*! ICR23 - Interrupt configuration field for GPIO interrupt 23
* 0b00..Interrupt 23 is low-level sensitive.
* 0b01..Interrupt 23 is high-level sensitive.
* 0b10..Interrupt 23 is rising-edge sensitive.
* 0b11..Interrupt 23 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR23(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR23_SHIFT)) & GPIO_ICR2_ICR23_MASK)
#define GPIO_ICR2_ICR24_MASK (0x30000U)
#define GPIO_ICR2_ICR24_SHIFT (16U)
/*! ICR24 - Interrupt configuration field for GPIO interrupt 24
* 0b00..Interrupt 24 is low-level sensitive.
* 0b01..Interrupt 24 is high-level sensitive.
* 0b10..Interrupt 24 is rising-edge sensitive.
* 0b11..Interrupt 24 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR24(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR24_SHIFT)) & GPIO_ICR2_ICR24_MASK)
#define GPIO_ICR2_ICR25_MASK (0xC0000U)
#define GPIO_ICR2_ICR25_SHIFT (18U)
/*! ICR25 - Interrupt configuration field for GPIO interrupt 25
* 0b00..Interrupt 25 is low-level sensitive.
* 0b01..Interrupt 25 is high-level sensitive.
* 0b10..Interrupt 25 is rising-edge sensitive.
* 0b11..Interrupt 25 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR25(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR25_SHIFT)) & GPIO_ICR2_ICR25_MASK)
#define GPIO_ICR2_ICR26_MASK (0x300000U)
#define GPIO_ICR2_ICR26_SHIFT (20U)
/*! ICR26 - Interrupt configuration field for GPIO interrupt 26
* 0b00..Interrupt 26 is low-level sensitive.
* 0b01..Interrupt 26 is high-level sensitive.
* 0b10..Interrupt 26 is rising-edge sensitive.
* 0b11..Interrupt 26 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR26(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR26_SHIFT)) & GPIO_ICR2_ICR26_MASK)
#define GPIO_ICR2_ICR27_MASK (0xC00000U)
#define GPIO_ICR2_ICR27_SHIFT (22U)
/*! ICR27 - Interrupt configuration field for GPIO interrupt 27
* 0b00..Interrupt 27 is low-level sensitive.
* 0b01..Interrupt 27 is high-level sensitive.
* 0b10..Interrupt 27 is rising-edge sensitive.
* 0b11..Interrupt 27 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR27(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR27_SHIFT)) & GPIO_ICR2_ICR27_MASK)
#define GPIO_ICR2_ICR28_MASK (0x3000000U)
#define GPIO_ICR2_ICR28_SHIFT (24U)
/*! ICR28 - Interrupt configuration field for GPIO interrupt 28
* 0b00..Interrupt 28 is low-level sensitive.
* 0b01..Interrupt 28 is high-level sensitive.
* 0b10..Interrupt 28 is rising-edge sensitive.
* 0b11..Interrupt 28 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR28(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR28_SHIFT)) & GPIO_ICR2_ICR28_MASK)
#define GPIO_ICR2_ICR29_MASK (0xC000000U)
#define GPIO_ICR2_ICR29_SHIFT (26U)
/*! ICR29 - Interrupt configuration field for GPIO interrupt 29
* 0b00..Interrupt 29 is low-level sensitive.
* 0b01..Interrupt 29 is high-level sensitive.
* 0b10..Interrupt 29 is rising-edge sensitive.
* 0b11..Interrupt 29 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR29(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR29_SHIFT)) & GPIO_ICR2_ICR29_MASK)
#define GPIO_ICR2_ICR30_MASK (0x30000000U)
#define GPIO_ICR2_ICR30_SHIFT (28U)
/*! ICR30 - Interrupt configuration field for GPIO interrupt 30
* 0b00..Interrupt 30 is low-level sensitive.
* 0b01..Interrupt 30 is high-level sensitive.
* 0b10..Interrupt 30 is rising-edge sensitive.
* 0b11..Interrupt 30 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR30(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR30_SHIFT)) & GPIO_ICR2_ICR30_MASK)
#define GPIO_ICR2_ICR31_MASK (0xC0000000U)
#define GPIO_ICR2_ICR31_SHIFT (30U)
/*! ICR31 - Interrupt configuration field for GPIO interrupt 31
* 0b00..Interrupt 31 is low-level sensitive.
* 0b01..Interrupt 31 is high-level sensitive.
* 0b10..Interrupt 31 is rising-edge sensitive.
* 0b11..Interrupt 31 is falling-edge sensitive.
*/
#define GPIO_ICR2_ICR31(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ICR2_ICR31_SHIFT)) & GPIO_ICR2_ICR31_MASK)
/*! @} */
/*! @name IMR - GPIO interrupt mask register */
/*! @{ */
#define GPIO_IMR_IMR_MASK (0xFFFFFFFFU)
#define GPIO_IMR_IMR_SHIFT (0U)
/*! IMR - Interrupt Mask bits
*/
#define GPIO_IMR_IMR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_IMR_IMR_SHIFT)) & GPIO_IMR_IMR_MASK)
/*! @} */
/*! @name ISR - GPIO interrupt status register */
/*! @{ */
#define GPIO_ISR_ISR_MASK (0xFFFFFFFFU)
#define GPIO_ISR_ISR_SHIFT (0U)
/*! ISR - Interrupt status bits
*/
#define GPIO_ISR_ISR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_ISR_ISR_SHIFT)) & GPIO_ISR_ISR_MASK)
/*! @} */
/*! @name EDGE_SEL - GPIO edge select register */
/*! @{ */
#define GPIO_EDGE_SEL_GPIO_EDGE_SEL_MASK (0xFFFFFFFFU)
#define GPIO_EDGE_SEL_GPIO_EDGE_SEL_SHIFT (0U)
/*! GPIO_EDGE_SEL - Edge select
*/
#define GPIO_EDGE_SEL_GPIO_EDGE_SEL(x) (((uint32_t)(((uint32_t)(x)) << GPIO_EDGE_SEL_GPIO_EDGE_SEL_SHIFT)) & GPIO_EDGE_SEL_GPIO_EDGE_SEL_MASK)
/*! @} */
/*! @name DR_SET - GPIO data register SET */
/*! @{ */
#define GPIO_DR_SET_DR_SET_MASK (0xFFFFFFFFU)
#define GPIO_DR_SET_DR_SET_SHIFT (0U)
/*! DR_SET - Set
*/
#define GPIO_DR_SET_DR_SET(x) (((uint32_t)(((uint32_t)(x)) << GPIO_DR_SET_DR_SET_SHIFT)) & GPIO_DR_SET_DR_SET_MASK)
/*! @} */
/*! @name DR_CLEAR - GPIO data register CLEAR */
/*! @{ */
#define GPIO_DR_CLEAR_DR_CLEAR_MASK (0xFFFFFFFFU)
#define GPIO_DR_CLEAR_DR_CLEAR_SHIFT (0U)
/*! DR_CLEAR - Clear
*/
#define GPIO_DR_CLEAR_DR_CLEAR(x) (((uint32_t)(((uint32_t)(x)) << GPIO_DR_CLEAR_DR_CLEAR_SHIFT)) & GPIO_DR_CLEAR_DR_CLEAR_MASK)
/*! @} */
/*! @name DR_TOGGLE - GPIO data register TOGGLE */
/*! @{ */
#define GPIO_DR_TOGGLE_DR_TOGGLE_MASK (0xFFFFFFFFU)
#define GPIO_DR_TOGGLE_DR_TOGGLE_SHIFT (0U)
/*! DR_TOGGLE - Toggle
*/
#define GPIO_DR_TOGGLE_DR_TOGGLE(x) (((uint32_t)(((uint32_t)(x)) << GPIO_DR_TOGGLE_DR_TOGGLE_SHIFT)) & GPIO_DR_TOGGLE_DR_TOGGLE_MASK)
/*! @} */
/*!
* @}
*/ /* end of group GPIO_Register_Masks */
/* GPIO - Peripheral instance base addresses */
/** Peripheral GPIO1 base address */
#define GPIO1_BASE (0x401B8000u)
/** Peripheral GPIO1 base pointer */
#define GPIO1 ((GPIO_Type *)GPIO1_BASE)
/** Peripheral GPIO2 base address */
#define GPIO2_BASE (0x401BC000u)
/** Peripheral GPIO2 base pointer */
#define GPIO2 ((GPIO_Type *)GPIO2_BASE)
/** Peripheral GPIO3 base address */
#define GPIO3_BASE (0x401C0000u)
/** Peripheral GPIO3 base pointer */
#define GPIO3 ((GPIO_Type *)GPIO3_BASE)
/** Peripheral GPIO5 base address */
#define GPIO5_BASE (0x400C0000u)
/** Peripheral GPIO5 base pointer */
#define GPIO5 ((GPIO_Type *)GPIO5_BASE)
/** Array initializer of GPIO peripheral base addresses */
#define GPIO_BASE_ADDRS { 0u, GPIO1_BASE, GPIO2_BASE, GPIO3_BASE, 0u, GPIO5_BASE }
/** Array initializer of GPIO peripheral base pointers */
#define GPIO_BASE_PTRS { (GPIO_Type *)0u, GPIO1, GPIO2, GPIO3, (GPIO_Type *)0u, GPIO5 }
/** Interrupt vectors for the GPIO peripheral type */
#define GPIO_IRQS { NotAvail_IRQn, GPIO1_INT0_IRQn, GPIO1_INT1_IRQn, GPIO1_INT2_IRQn, GPIO1_INT3_IRQn, GPIO1_INT4_IRQn, GPIO1_INT5_IRQn, GPIO1_INT6_IRQn, GPIO1_INT7_IRQn, NotAvail_IRQn, NotAvail_IRQn, NotAvail_IRQn, NotAvail_IRQn }
#define GPIO_COMBINED_LOW_IRQS { NotAvail_IRQn, GPIO1_Combined_0_15_IRQn, GPIO2_Combined_0_15_IRQn, GPIO3_Combined_0_15_IRQn, NotAvail_IRQn, GPIO5_Combined_0_15_IRQn }
#define GPIO_COMBINED_HIGH_IRQS { NotAvail_IRQn, GPIO1_Combined_16_31_IRQn, GPIO2_Combined_16_31_IRQn, GPIO3_Combined_16_31_IRQn, NotAvail_IRQn, GPIO5_Combined_16_31_IRQn }
/* 地址可查阅 参考手册-GPIO章节-GPIO Memory Map */
/* GPIO1外设基地址 */
#define GPIO1_BASE (unsigned int)(0x401B8000u)
/* GPIO1寄存器地址,强制转换成指针 */
#define GPIO1_DR *(unsigned int*)(GPIO1_BASE+0x00)
#define GPIO1_GDIR *(unsigned int*)(GPIO1_BASE+0x04)
#define GPIO1_PSR *(unsigned int*)(GPIO1_BASE+0x08)
#define GPIO1_ICR1 *(unsigned int*)(GPIO1_BASE+0x0C)
#define GPIO1_ICR2 *(unsigned int*)(GPIO1_BASE+0x10)
#define GPIO1_IMR *(unsigned int*)(GPIO1_BASE+0x14)
#define GPIO1_ISR *(unsigned int*)(GPIO1_BASE+0x18)
#define GPIO1_EDGE_SEL *(unsigned int*)(GPIO1_BASE+0x1C)
/* 地址可查阅 参考手册-IOMUXC章节-IOMUXC Memory Map/Register Definition */
/* IOMUXC基地址 */
#define IOMUXC_BASE (unsigned int)(0x401F8000u)
/* MUX模式选择寄存器 IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_09 ,强制转换成指针*/
#define IOMUXC_MUX_GPIO_AD_B0_09 *(unsigned int*)(IOMUXC_BASE + 0xE0 )
/* PAD属性设置寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_09 ,强制转换成指针*/
#define IOMUXC_PAD_GPIO_AD_B0_09 *(unsigned int*)(IOMUXC_BASE + 0x2D0 )
/* 地址可查阅 参考手册-CCM章节-CCM Memory Map/Register Definition */
/* 时钟控制外设基地址 */
#define CCM_BASE (unsigned int)(0x400FC000u)
/* 时钟配置寄存器CCM_CCGR1地址 */
#define CCM_CCGR1 *(unsigned int*)(CCM_BASE + 0x6C)
main文件
现在就可以开始编写程序了,在main文件中先编写一个 main 函数,里面什么都没有,暂时为空。
int main (void) { }
只写main函数,如果此时直接编译的话,会出现如下错误: “Error: L6218E: Undefined symbol SystemInit (referred from startup_mimxrt1052.o).” 错误提示SystemInit 没有定义。从分析启动文件时我们知道,Reset_Handler 调用了该函数用来初始化RT1021芯片的基础功能,如浮点运算单元、系统定时器、看门狗以及指令缓存,为了简单起见,我们在 main 文件里面定义一个 SystemInit 空函数,什么也不做,为的是骗过编译器,把这个错误去掉。也就是说,不使用这些基础功能,我们也能控制芯片完成点亮LED灯的任务,我们在main中添加SystemInit空函数:
// 函数为空,目的是为了骗过编译器不报错 void SystemInit(void) { }
这时再编译就没有错了,完美解决。接下来在main函数中添加代码,对寄存器进行控制,有关GPIO寄存器的详细描述请阅读《IMXRT1050RM》(参考手册)中IOMUXC以及GPIO章节的寄存器描述部分。
/* 设置MUX寄存器为0x05,表示把引脚用于普通GPIO */
IOMUXC_MUX_GPIO_AD_B0_09 = (unsigned int)0x05;
#define BOARD_USER_LED_GPIO GPIO1 /*!< GPIO peripheral base pointer */
#define BOARD_USER_LED_GPIO_PIN 5U /*!< GPIO pin number */
#define BOARD_USER_LED_GPIO_PIN_MASK (1U << 5U) /*!< GPIO pin mask */
#define BOARD_USER_LED_PORT GPIO1 /*!< PORT peripheral base pointer */
#define BOARD_USER_LED_PIN 5U /*!< PORT pin number */
#define BOARD_USER_LED_PIN_MASK (1U << 5U) /*!< PORT pin mask */
#define EXAMPLE_LED_GPIO BOARD_USER_LED_GPIO #define EXAMPLE_LED_GPIO_PIN BOARD_USER_LED_PIN
/** Peripheral IOMUXC base address */ #define IOMUXC_BASE (0x401F8000u) /** Peripheral IOMUXC base pointer */ #define IOMUXC ((IOMUXC_Type *)IOMUXC_BASE) /** Array initializer of IOMUXC peripheral base addresses */ #define IOMUXC_BASE_ADDRS { IOMUXC_BASE } /** Array initializer of IOMUXC peripheral base pointers */ #define IOMUXC_BASE_PTRS { IOMUXC }
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 != 0UL) { *((volatile uint32_t *)inputRegister) = inputDaisy; } }
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 != 0UL) { *((volatile uint32_t *)configRegister) = configValue; } }
void GPIO_PinWrite(GPIO_Type *base, uint32_t pin, uint8_t output)
{
assert(pin < 32U);
if (output == 0U)
{
#if (defined(FSL_FEATURE_IGPIO_HAS_DR_CLEAR) && FSL_FEATURE_IGPIO_HAS_DR_CLEAR)
base->DR_CLEAR = (1UL << pin);
#else
base->DR &= ~(1UL << pin); /* Set pin output to low level.*/
#endif
}
else
{
#if (defined(FSL_FEATURE_IGPIO_HAS_DR_SET) && FSL_FEATURE_IGPIO_HAS_DR_SET)
base->DR_SET = (1UL << pin);
#else
base->DR |= (1UL << pin); /* Set pin output to high level.*/
#endif
}
}
位号 | 配置域 | 配置值(二进制) | 功能 |
---|---|---|---|
0 | SRE | 0 | 慢压摆率 |
1-2 | 00 | ||
3-5 | DSE | 110 | 串联电阻R0/6 |
6-7 | SPEED | 10 | 带宽100MHz |
8-10 | 000 | ||
11 | ODE | 0 | 不使用开漏模式 |
12 | PKE | 0 | 关闭上下拉、保持器 |
13 | PUE | 0 |
|
14-15 | PUS | 00 |
|
16 | HYS | 0 | 不使用滞后功能 |
17-31 | 0000 0000 0000 0000 |
/* 设置PAD寄存器控制引脚的属性 */
IOMUXC_PAD_GPIO_AD_B0_09 = (unsigned int)0x000B0;
/* 属性配置说明:
十六进制数0x000B0 = 二进制数0b0000 0000 0000 1011 0000
*/
/* bit0: SRE: 0b0 压摆率: 慢压摆率
bit1~bit2: -: 0b00 寄存器保留项
bit3~bit5: DSE: 0b110 驱动强度: R0/6 (仅作为输出时有效 )
bit6~bit7: SPEED:0b10 带宽 : medium(100MHz)
bit8~bit10: -: 0b000 寄存器保留项
bit11: ODE: 0b0 开漏配置: 关闭
(开漏高阻态常用于总线配置,如I2C )
bit12: PKE: 0b0 拉/保持器配置: 关闭
bit13: PUE: 0b0 拉/保持器选择: 关闭了上下拉及保持器,任意值无效
bit14~bit15: PUS: 0b00 上拉/下拉选择: 关闭了上下拉及保持器,任意值无效
bit16: HYS: 0b0 滞回器配置: 关闭
(仅输入时有效,施密特触发器,使能后可以过滤输入噪声)
bit17~bit31: -: 0b0 寄存器保留项
*/
/* 设置GPIO1_09引脚的方向,使用输出模式 */
GPIO1_GDIR |= (unsigned int)(1<<9);
- GPIO1_GDIR在MIMXRT1052.h头文件已经定义好,它表示的就是GPIO1端口的GDIR寄存器指针,并且包含“* ”取指针操作。
- (1<<9)是一个bit9为1的数字,即0x200,通常来说在代码中不会写0x200这样的结果数字,而是用(1<<9)这种运算来表示。如bit10为1的数字,则用(1<<10)表示。
- 使用“|=”这种位操作方法是为了避免影响到寄存器中的其它位。这种操作的等效代码是
GPIO1_GDIR = GPIO1_GDIR | (unsigned int)(1<<9),
GPIO1_GDIR = (unsigned int)(1<<9);
/* 控制引脚为高电平,关闭LED灯 */
GPIO1_DR |= (unsigned int)(1<<5);
/* 控制引脚为低电平,点亮LED灯 */
GPIO1_DR &=~ (unsigned int)(1<<5);
1
2
3
4
5
6
7
|
/* 开启GPIO1端口的时钟 */
/* 清空控制GPIO1端口时钟的bit26、bit27 */
CCM_CCGR1 &= ~(unsigned int)(3<<26);
/* 把bit26、bit27设置为0b01,即开启GPIO1时钟 */
CCM_CCGR1 |= (unsigned int)(1<<26);
|
- CCM_CCGR1在MIMXRT1052.h头文件已经定义好,它表示的就是CCM的CCGR1寄存器指针,并且包含“* ”取指针操作。
- (3<<26)表示十六进制数0x0c000000,它的bit26~bit27这2位为1,使用这样的数值与CCM_CCGR1寄存器进行“&=~”操作可以把它的bit26、bit27进行清零,其它位的值不变。代码中(3<<26)的3就是二进制值0b11,2个数字1表示要清空2个寄存器位,26表示bit26,类似地,假如要清除寄存器的bit10、bit11、bit12以及bit13这4位,操作时就使用二进制数0b1111<<10即(0x0F<<10)参与“&=~”运算。
- bit26、bit27被清零后,使用(1<<26)与CCM_CCGR1寄存器进行“|=”,把它的bit26、bit27位设置为0b01,即开启GPIO1端口的时钟,然后就可以对GPIO1端口进行操作了。
int main(void)
{
/* Board pin init */
BOARD_ConfigMPU();
BOARD_InitBootPins();
BOARD_InitBootClocks();
/* Set systick reload value to generate 1ms interrupt */
if (SysTick_Config(SystemCoreClock / 1000U))
{
while (1)
{
}
}
while (1)
{
/* Delay 1000 ms */
SysTick_DelayTicks(1000U);
if (g_pinSet)
{
GPIO_PinWrite(EXAMPLE_LED_GPIO, EXAMPLE_LED_GPIO_PIN, 0U);
g_pinSet = false;
}
else
{
GPIO_PinWrite(EXAMPLE_LED_GPIO, EXAMPLE_LED_GPIO_PIN, 1U);
g_pinSet = true;
}
}
}
void GPIO_PinWrite(GPIO_Type *base, uint32_t pin, uint8_t output)
{
assert(pin < 32U);
if (output == 0U)
{
#if (defined(FSL_FEATURE_IGPIO_HAS_DR_CLEAR) && FSL_FEATURE_IGPIO_HAS_DR_CLEAR)
base->DR_CLEAR = (1UL << pin);
#else
base->DR &= ~(1UL << pin); /* Set pin output to low level.*/
#endif
}
else
{
#if (defined(FSL_FEATURE_IGPIO_HAS_DR_SET) && FSL_FEATURE_IGPIO_HAS_DR_SET)
base->DR_SET = (1UL << pin);
#else
base->DR |= (1UL << pin); /* Set pin output to high level.*/
#endif
}
}
#define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK (0x7U)
#define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT (0U)
/*! MUX_MODE - MUX Mode Select Field.
* 0b000..Select mux mode: ALT0 mux port: SEMC_DATA00 of instance: semc
* 0b001..Select mux mode: ALT1 mux port: QTIMER2_TIMER0 of instance: qtimer2
* 0b010..Select mux mode: ALT2 mux port: LPUART4_CTS_B of instance: lpuart4
* 0b011..Select mux mode: ALT3 mux port: SPDIF_SR_CLK of instance: spdif
* 0b100..Select mux mode: ALT4 mux port: LPSPI2_SCK of instance: lpspi2
* 0b101..Select mux mode: ALT5 mux port: GPIO2_IO00 of instance: gpio2
* 0b110..Select mux mode: ALT6 mux port: FLEXCAN1_TX of instance: flexcan1
* 0b111..Select mux mode: ALT7 mux port: PIT_TRIGGER02 of instance: pit
*/
#define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(x) (((uint32_t)(((uint32_t)(x)) << IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT)) & IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK)
#define IOMUXC_SW_MUX_CTL_PAD_SION_MASK (0x10U)
#define IOMUXC_SW_MUX_CTL_PAD_SION_SHIFT (4U)
/*! SION - Software Input On Field.
* 0b1..Force input path of pad GPIO_EMC_00
* 0b0..Input Path is determined by functionality
*/
#define IOMUXC_SW_MUX_CTL_PAD_SION(x) (((uint32_t)(((uint32_t)(x)) << IOMUXC_SW_MUX_CTL_PAD_SION_SHIFT)) & IOMUXC_SW_MUX_CTL_PAD_SION_MASK)
/*! @} */
下载验证把编译好的程序下载到开发板并复位,可看到核心板上的LED灯被点亮。 接下来就是调试部分: