公众号:嵌入式攻城狮(ID:andyxi_linux)

作者:安迪西

 

之前的文章中介绍了新旧字符设备驱动开发的方式,并利用虚拟的字符设备来学习其开发流程,没有涉及到操作Linux开发板上的硬件。对硬件的操作,究其本质最终都是要操作处理器的寄存器。因此在操作硬件之前,我们需要先了解有关GPIO的寄存器配置原理及方法

 

1. 认识i.MX6ULL处理器GPIO

 

1.1 基本概念

 

以下是一些名词缩写及其含义:

IO:Input Output,用于CPU与外界进行信息交互

GPIO:General Purpose IO ports,通用IO口

SNVS:Secure Non-Volatile Storage 安全的非易失性存储

IOMUX:Input Output Multiplexer 输入/输出多路复用器

IOMUXC:Input Output Multiplexer Controller 输入/输出多路复用控制器

SW:Switch 开关

 

1.2 GPIO逻辑结构

 

下图为i.MX6ULL处理器的GPIO硬件结构框图,其中PAD1和PAD2表示i.MX6ULL芯片引出的GPIO引脚,其余部件均位于芯片内部

 

 

上图中各部分的功能和作用如下

 

⏩ PAD1:GPIO引脚,其左侧通过IOMUX复用选择器连接到各种寄存器上

⏩ IOMUX复用选择器:用于将芯片引脚与最左侧的各种寄存器连接起来

⏩ IOMUXC复用控制器:IO通过MUX寄存器来配置复用功能,如GPIO、IIC等;通过PAD寄存器来配置引脚属性,如驱动能力,是否使用上下拉电阻

⏩ Block外设功能控制块:例如具有PWM输出功能的引脚,它需要PWM外设的支持

⏩ GPIO外设:GPIO是每个IO都有的外设,是IO控制的基本功能, 如输出高低电平、 检测电平输入等。当需要使用引脚的GPIO功能时,就要配置GPIO外设中的各个寄存器

⏩ PAD2:另一个引脚,它与PAD1有一根信号线连接,表示部分引脚的输出可以作为另一个引脚的输入

 

1.3 GPIO命名

 

i.MX6ULL处理器的GPIO被分为5组,且每组的数量不同,具体可查阅数据手册

 

 

另外根据IO类别,可以分为两大类:SNVS域IO和通用域IO,这两类IO本质上是一样的

 

 

2. 配置i.MX6ULL处理器GPIO

 

下面以GPIO1_IO00引脚为例,介绍GPIO配置的步骤

 

2.1 时钟配置

 

I.MX6ULL每个外设都有一个外设时钟,使用GPIO时,也必须先使能对应的时钟。CCM(Clock Controller Module)时钟控制模块寄存器用来使能外设时钟。CMM一共有CCM_CCGR0~CCM_CCGR6这7个寄存器,控制着I.MX6U的所有外设时钟开关

 

以CCM_CCGR0为例,它是个32位寄存器,每2位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟,两个位就有 4 种操作方式:

 

 

若要打开GPIO2的外设时钟,只需要设置CCM_CCGR0的bit31和bit30为1即可,即 CCM_CCGR0=3 << 30

 

 

2.2 IO配置

 

⏩ 配置MUX寄存器:配置复用功能

 

 

IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00寄存器地址为:0x020E005C,只用到了最低的5位,bit0~bit3就是设置复用功能的,该引脚可复用为9种不同功能的IO

 

⏩ 配置PAD寄存器:配置引脚属性

 

 

IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00寄存器地址为:0x020E02E8,只用到了低17位,用于设置速度、驱动能力、压摆率等

 

2.3 GPIO配置

 

上面的MUX和PAD寄存器都是配置IO的,当把IO配置为了GPIO功能后,还需要继续对GPIO外设的各种寄存器进行配置⏩ 配置DR寄存器:数据寄存器,一个GPIO组最大只有32个IO,32位DR寄存器中的每一个位对应一个GPIO

 

 

当设置为输出模式,向指定的位写入数据,相应的IO就会输出相应的高低电平;当设置为输入模式,比如GPIO1_IO00引脚接地的话,GPIO1.DR的bit0就是0

 

⏩ 配置GDIR寄存器:方向寄存器,设置GPIO的工作方向是输入还是输出,每个位对应一个IO

 

 

若要设置GPIO1_IO00为输入,则GPIO1.GDIR=0;反之GPIO1.GDIR=1

 

⏩ 配置PSR寄存器:状态寄存器,每个IO对应一个位,用于获取对应的GPIO的状态

 

 

是一个只读寄存器,功能类似于输入状态下的DR寄存器

 

⏩ 配置ICR1/ICR2寄存器:中断控制寄存器,每两位对应一个GPIO,ICR1用于配置低16个GPIO,ICR2用于配置高16个GPIO

 

 

例如设置GPIO1_IO15为上升沿触发中断,则GPIO1.ICR1 = 2 << 30

 

⏩ 配置IMR寄存器:中断屏蔽寄存器,每个IO对应一个位,用于控制GPIO的中断使能和禁止

 

 

例如要使能GPIO1_IO00的中断,则GPIO1.MIR=1

 

⏩ 配置ISR寄存器:中断状态寄存器,每个IO对应一个位,只要某个GPIO的中断发生,ISR中相应的位会被置1

 

 

可通过读取ISR寄存器来判断GPIO中断是否发生,相当于中断标志位。处理完中断后,必须清除中断标志位

 

⏩ 配置EDGE_SEL寄存器:边沿选择寄存器,每个IO对应一个位,用来设置边沿中断, 并会覆盖ICR1和ICR2的设置

 

 

若相应位被置1,相当于设置了对应GPIO双边沿(上升沿和下降沿)触发。例如,设置GPIO1.EDGE_SEL=1,则表示双边沿触发中断,无论 GFPIO1_CR1的设置为多少

 

3. GPIO配置总结

 

综上所述,i.MX6ULL的IO作为GPIO使用,需要进行以下几个步骤的配置:

 

使能GPIO对应的时钟

配置MUX寄存器,设置IO的复用功能,使其复用为GPIO功能

配置PAD寄存器,设置IO的上下拉、速度等

配置GPIO的各种寄存器,设置输入/输出、是否使用中断、默认输出电平等

裸机开发中,需要自已定义寄存器及其地址,通常代码如下:

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE              (0X020C406C) 
#define SW_MUX_SNVS_TAMPER3_BASE    (0X02290014)
#define SW_PAD_SNVS_TAMPER3_BASE    (0X02290058)
#define GPIO5_DR_BASE               (0X020AC000)
#define GPIO5_GDIR_BASE             (0X020AC004)