前言:
本文是根据哔哩哔哩网站上"正点原子[第二期]Linux之ARM(MX6U)裸机篇"视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。
引用:
正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》第 8.1 章
《正点原子资料_A盘/02开发板原理图/IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》
资料盘
开发板资料链接: https://pan.baidu.com/s/1j5Jzbdx9i-g0cWIi3wf2XA提取码:ag1u
正文:
本文是 "正点原子[第二期]Linux之ARM(MX6U)裸机篇--第6讲" 的读书笔记。

1. LED 驱动程序
1.1 查看电路原理图中LED连接的GPIO
查看我自己购买的正点原子 I.MX6ULL Mini 开发板对应的电路原理图(电路原理图在正点原子官方网站提供的百度网盘下载链接可以获取到),从电路原理图中找到到 LED 灯所使用的 GPIO 引脚。

代开我自己购买的 "正点原子 I.MX6ULL Mini 开发板" 开发板对应的电路原理图。

在电路原理图中找到"正点原子 I.MX6ULL Mini 开发板"开发板上的 LED 灯对应芯片管脚gpio号。找到Mini 开发板电路原理图中的LED灯的部分,上面标记有 "LED0LED1",继续找到"LED0LED1" 对应芯片的哪个引脚Pin。

在电路原理图PDF文件里搜索"LED0"关键字,找到 LED0 连接到 I.MX6ULL 芯片上的 "GPIO3"引脚上,在Mini 开发板电路原理图里标记为 "GPIO3 LED0"。

1.2 查看I.MX6ULL参考手册寄存器GPIO定义
上面通过电路原理图,已经找到我的"正点原子 I.MX6ULL Mini 开发板"上LED灯连接的是I.MX6ULL芯片的 'GPIO3' 引脚。接下来需要到I.MX6ULL芯片参考手册中找到 'GPIO3'对应的寄存器定义。在上一节5讲视频读书笔记中,已经知道I.MX6ULL芯片的每一个 PAD 管脚对应着两个寄存器:
- 其中一个寄存器配置芯片PAD管脚的复用特性。
- 另一个寄存器配置芯片PAD管脚的电气特性。
I.MX6ULL芯片参考手册第32章是芯片管脚IO的定义,从这里找到 gpio3 对应的两个寄存器。
'IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03'

'IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03'

1.3 I.MX6ULL IO 复用
在I.MX6ULL芯片参考手册中芯片管脚的定义规律如下
- I.MX6ULL芯片的是 PAD_XXX_XXX,其中 XXX_XXX 就是管脚的名字,例如,PAD_GPIO_IO03,UART1_TX_DATA、 JTAG_MOD 等。
- I.MX6ULL芯片的管脚一般对应着两个寄存器,一个寄存器控制IO的复用(Multiplex),另一个寄存器控制IO的电气特性。
- 控制芯片管脚IO复用的寄存器名字,使用 SW_MUX_CTL 作为名字的一部分(可以理解为 software multiplex control),例如,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03。
- 控制芯片管脚IO电气特性的寄存器名字,使用 SW_PAD_CTL 作为名字的一部分(可以理解为 PAD Control),例如,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03。
"Chapter 32: IOMUX Controller(IOMUXC)"这一章列出了 I.MX6ULL 的所有 IO,如果你找遍 32 章的书签,你会发现貌似 GPIO 只有 GPIO1_IO00~GPIO1_IO09,难道I.MX6ULL 的 GPIO 只有这 10 个?显然不是的, 我们知道 STM32 的很多 IO 是可以复用为其它 功 能 的 , 那 么 I.MX6ULL 的 其 它 IO 也 是 可 以 复 用 为 GPIO 功 能 。同 样 的 ,GPIO1_IO00~GPIO_IO09 也是可以复用为其它外设引脚的,接下来就是 I.MX6ULL IO 复用。
以"IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03"这个 IO 为例,打开参考手册的 1568 页,
如图 8.1.3.1 所示:
从图8.1.3.1 可以看到有有个名为 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的寄存器,寄存器的地址为 0x020E_0068h (大致地址落在范围在 0x0200_0000 = 32*2^20 = 32MB地址区间),这个寄存器是32位的,但只用到了低5位(0-4bit),其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO03 的复用功能。GPIO1_IO03 一种可以服用为9种功能IO, 分别对应 ALT0~ALT8,起重工ALT5就是作为 GPIO1_IO03。GIPIO1_IO03还可以作为 I2C_SCL, GPT1_COMPARE3 等。这个就是 I.MX6U 的 IO 复用。
再来看一个 "IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA"这个IO,这个IO对应的复用如图8.1.3.2所示

同样的,从8.1.3.2可以看出,UART1_TX_DATA 可以服用为8种不同功能的IO,分别为 ALT0~ALT5和ALT8,ALT9,起重工ALT5表示 UART1_TX_DATA 可以复用为 GPIO1_IO16。
由此可见,I.MX6U 的 GPIO 不止 GIPO1_IO00~GPIO1_IO09这10个,其它的IO都可以复用为GPIO来使用。I.MX6U 的 GPIO 一共有5组:GPIO1, GPIO2, GPIO3, GPIO4 和 GPIO5,其中GPIO1有32个IO,GPIO2有22个IO,GPIO3有29个IO,GPIO4有29个IO,GPIO5最少,只有12个IO,这样一共有124个GPIO。如果只想看每个IO能复用什么外设的话可以直接查阅《IMX6ULL参考手册》第4章 "Chapter 4 External Signals and Pin Multiplexing"。如果我们要编写代码,设置某个IO复用功能的话就要查阅第32章"Chapter32:IOMUX Controller(IOMUX)",第32章详细列出了所有IO对应的复用配置寄存器。
至此我们就解决了 8.1.1 节中的第3个疑问,那就是 I.MX6U 是有IO复用功能的,如果某个IO要作为某个外设引脚使用的话,是要配置复用寄存器的。
1.5 I.MX6U IO 配置
细心的读者应该会发现在《I.MX6UL参考手册》第30章"Chapter 30: IOMUX Controller(IOMUX)"的书签中,每个IO会出现两次,它们的名字差异很小,不仔细看就看不出来,比如 GIPO1_IO00有如下两个书签
IOMUXC_SW_MUX_CTL_PAD_CPIO0_IO00
IOMUXC_SW_PAD_CTL_PAD_CPIO0_IO00
上面两个都是跟 GPIO0_IO00 有关的寄存器,名字上的区别就是红色部分,一个是"MUX",一个是"PAD"。IOMUXC_SW_MUX_CTL_PAD_GPIO0_IO00 我们前面已经说了,是用来配置GPIO0_IO00复用功能的,那么 IOMUXC_SW_PAD_CTL_PAD_GPIO0_IO00 是做什么的呢?赵
、
从图8.4.1可以看出,IOMUXC_SW_PAD_CTL_PAD_GPIO0_IO00也是个寄存器,寄存器地址为 0x02E0_02E8。这个也是个32位寄存器,但是只用到了其中的低17位,在看这些位的具体含义之前,先看下图 8.1.4.2 所示的 GPIO 功能图。

我们对照这8.1.4.2 来详细看一下寄存器 IOMUXC_SW_PAD_CTL_GPIO1_IO00的各个位的含义:
HYS(bit16):对应图8.1.4.2中的HYS,用来使能迟滞比较器,当IO作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。
PUS(bit15:14):对应图8.1.4.2中的PUS,用来设置上下拉电阻的,一种有四种选项可以选择,如下表
|-----|--------|
| 位设置 | 含义 |
| 00 | 100K下拉 |
| 01 | 47K上拉 |
| 10 | 100K上拉 |
| 11 | 22K上拉 |
PUE(bit13):图8.1.4.2 没有给出来,当IO作为输入使用的时候,这个位用来设置IO使用上下拉还是状态保持器。当位0的时候使用状态保持器,当位1的时候使用上下拉。状态保持器在IO作为输入的时候才有用,顾名思义,就是当外部电路断电以后此IO口可以保持住以前的状态。
PKE(bit12):对应图8.1.4.2中的PKE,此位用来使能或禁用上下拉/状态保持器功能,位0时禁用上下拉/状态保持器,为1时使能上下拉和状态保持器。
**ODE(bit11):**对应图8.1.4.2中的ODE,当IO作为输出的时候,此位用来禁止或者使能开路输出,此位为0的时候禁止开路输出,此位为1的时候使能开路输出。
SPEED(bit7:6):对应图8.1.4.2中的SPEED,当IO用作输出的时候,此位用来设置IO速度,设置如下表所示
|-----|----------|
| 位设置 | 含义 |
| 00 | 低速50M |
| 01 | 中速100M |
| 10 | 中速100M |
| 11 | 最大速度200M |
DSE(bit5:3):对应图8.1.4.2中的DSE,当IO用作输出的时候用来设置IO的驱动能力,总共有8个可选选型,如下表所示。
|-----|--------------------------------------------|
| 位设置 | 速度 |
| 000 | 输出驱动关闭 |
| 001 | R0(3.3V下R0是260Ω,1.8.V下R0是150Ω,接DDR的时候是240Ω |
| 010 | R0/2 |
| 011 | R0/3 |
| 100 | R0/4 |
| 101 | R0/5 |
| 110 | R0/6 |
| 111 | R0/7 |
SRE(bit0):对应图8.1.4.2中的SRE,设置压摆率,当此位为0的时候是低压摆率,当位1的时候是高压摆率。这里的压摆率就是IO电平跳变所需要的时间,比如从0到1需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就低。如果你的产品要过EMC的话那就可以使用小压摆率,因为波形平缓,如果你当前所使用的IO做高速通信的话就可以使用高压摆率。
通过上面的介绍,可以看出寄存器 IOMUXC_SW_PAD_CTL_PAD_GIO1_IO00 是用来配置 GPIO1_IO00 的,包括速度设置,驱动能力设置,压摆率设置等等。至此,我们就解决了8.1.1中的第2个疑问,那就是I.MX6U的IO是可以设置速率的。但是我们没有看到如何设置IO为输入还是输出?IO的默认电平如何设置等等,所以我们继续看。
1.6 I.MX6U GPIO设置
IOMUXC_SW_MUX_CTL_PAD_XXX_XXX 和 IOMUXC_SW_PAD_CTL_PAD_XXX_XXX 这两种寄存器都是配置 IO 的,注意是 IO!不是 GPIO ,GPIO 是一个 IO 众多复用功能中的一种。比如 GIPIO1_IO00 这个IO可以复用为: I2C2_SCL, GPT1_CAPTURE1,ANATOP_OTG1_ID,ENET1_REE_CLK,MQS_RIGHT,GPIO1_IO00,ENET1_1588_EVENT0_IN,SRC_SYSTEM_RESET 和 WDOG3_WDOG_B 这9个功能,GPIO1_IO00是其中一种,我们想要把 GPIO1_IO00 用作哪个外设就复用为那个外设功能即可。如果我们要用 GPIO1_IO00 来点个灯,作为按键输入啥的就是使用其 GPIO (通用输入输出)的功能。将其复用为 GPIO 以后,还需要对其GPIO的功能进行配置,关于 I.MX6U 的 GPIO 请参考《IMX6UL 参考手册》的第 26
章"Chapter 26 General Purpose Input/Ouput(GPIO)",GPIO 的结构如下图所示。

在图 8.1.5.1 的左下角的 IOMUXC 框图中就有 SW_MUX_CTL_PAD_* 和 SW_PAD_CTL_PAD_* 两种寄存器。这两种寄存器就是前面说了用来设置 IO 复用功能 和 IO 电气特性。做上交部分的 GPIO 框图就是,当 IO 用作GPIO的时候需要设置的寄存器,一共有8个:DR,GDIR,PSR,ICR1,ICR2,EDGE_SEL,IMR和ISR。
- GPIO.DR
- GPIO.GDIR
- GPIO.PSR
- GPIO.ICR1
- GPIO.ICR2
- GPIO.EDGE_SEL
- GPIO.IMR
- GPIOl.ISR
前面我们说了I.MX6U一共有GPIO1~GPIO5共5组GPIO,每组GPIO都有这个8个寄存器。我们来看一下这个8个寄存器都是什么含义。
首先来看一下DR寄存器,此寄存器是数据寄存器,结构如下图所示



《I.MX6U参考手册.pdf》章节28.5 GPIO Data register (GPIOx_DR) 对该寄存器其的解释如下,
28.5.1 GPIO data register (GPIOx_DR)
The 32-bit GPIO_DR register stores data that is ready to be driven to the output lines. If
the IOMUXC is in GPIO mode and a given GPIO direction bit is set, then the
corresponding DR bit is driven to the output. If a given GPIO direction bit is cleared, then
a read of GPIO_DR reflects the value of the corresponding signal.Two wait states are
required in read access for synchronization
GPIOx_DR 寄存器是数据寄存器,其结构如下图所示

此寄存器是32位的,一个GPIO组最大只有32个IO,因此 DR 寄存器中的每个位都对应一个 GPIO。当GPIO被配置为输出功能以后,向指定的为写入数据那么相应的IO就会 输出相应的高低电平,比如要配置 GPIO1_IO00输出高电平,那么就应该设置 GPIO1.DR=0x01。当GPIO配置为输入模式以后,此寄存器就保存这个对应IO的电平值,每个位对应一个GPIO,例如,GPIO1_IO00这个引脚接地的话,GPIO1.DR的bit0就是0。
看完DR寄存器,接着看 GDIR 寄存器,这个是方向寄存器,用来设置某个 GPIO 的工作方向的,即输入/输出,GDIR 寄存器的结构如下所示

GDIR寄存器也是32位,此寄存器用来设置某个IO的方向,是输入还是输出。通样的,每个IO对应一个位,如果要设置GPIO作为输入的话就设置相应为位为0,如果要设置为输出的话就设置为1。比如要设置GPIO1_IO00为输入,那么GPIO1.GDIR=0;
接下来看PSR寄存器,这是GPIO状态寄存器,如下图所示

同样 PSR 寄存器也是一个GPIO对应一个位,读取相应的位即可获取对应的GPIO的状态,也就是GPIO的高低电平值。功能和输入状态下的DR寄存器一样。
接下来ICR1和ICR2这两个寄存器,都是中断控制寄存器,ICR1用于配置低16个GPIO,ICR2用来配置高16个GPIO,ICR1寄存区如下图所示。

ICR1用于IO0~15的配置,ICR2用于IO16~31的配置。ICR1寄存器中一个GPIO用两个位,这两个位用来配置中断的触发方式,可配置的选项如下表所示:
|-----|-------|
| 位设置 | 速度 |
| 00 | 低电平触发 |
| 01 | 高电平触发 |
| 10 | 上升沿触发 |
| 11 | 下降沿触发 |
以GPIO_IO15为例,如果要设置GPIO1_IO15位上升沿触发中断,那么 GIPO1.ICR1=2<<30,如果压迫设置GPIO1的 IO16-31 的话就要设置ICR2寄存器了。
接下来看IMR寄存器,这是中断屏蔽寄存器,如下图所示

IMR也是一个GPIO对应一位,IMR寄存器用来控制GPIO的中断禁止和使能,如果使能某个GPIO中断,那么设置相应的位为1即可,反之,如果要禁止中断,那么设置相应的位为0即可。例如,要使能GPIO1_IO00的中断,那么就可以设置 GIPO1.IMR=1。
接下来看寄存器ISR,ISR是中断状态寄存器,寄存器如下图所示:

ISR寄存器也是32位寄存器,一个GPIO对应一个位,只要某个GPIO的中断发生,那么ISR中相应的位就会被置1。所以,我们可以通过读取ISR寄存器来判断GPIO中断是否发生,相当于ISR中的这些位就是中断标志位。当我们处理完中断之后,必须清楚中断标志位,清楚的方法就是向ISR中相应位写1,也就是写1清零。
最后看一下 EDGE_SEL 寄存器,这是边沿选择寄存器,寄存器如下图所示

EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖ISCR1和ICR2的设置,同样是一个GPIO对应一个位。如果相应位被置1,那么就相当于设置了对应GPIO是上升沿河下降沿(双边沿)触发。例如,我们设置 GPIO1.EDGE_SEL=1,那么就表示GPIO1_IO01是双边沿触发中断,无论 GPIO1.ICR1 的设置为多少,都是双边沿触发。
关于GPIO的寄存器就讲解到这里,因为GPIO是最常用的功能,我们详细的讲解了GPIO的8被寄存器。自此,我们就解决了8.1.1中的第3个和第4个疑问,那就是 I.MX6U 的IO是需要配置和输出的,是可以设置输出高低电平的,也可以读取GPIO对应的电平。
1.7 I.MX6U GPIO 时钟使能
还有最后一个疑问,那就是 I.MX6U 的GPIO是否需要使能时钟?I.MX6U 其实也一样的,每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。I.MX6U 的系统时钟参考《I.MX6UL 参考手册》的第 18 章"Chapter 18: Clock Controller Module(CCM)",这一章主要讲解 I.MX6U 的时钟系统,很复杂。我们先不研究 I.MX6U 的时钟系统,我们只看一下CCM (Clock Controller Module)里面的外设时钟使能寄存器。CCM 有 CCM_CCGR0~CCM_CCGR6 这7寄存器,这7个寄存器控制着 I.MX6U 的所有外设时钟开关,我们以 CCM_CCGR0 为例看一下如何禁止或使能一个外设时钟,CCM_CCGR0 结构如下图所示

CCM_CCGR0 是32位寄存器,其中每2位控制一个外设的时钟,比如bit31:30控制着GPIO2的外设时钟,两个位就有4中操作方式,如下表所示
|-----|----------------------------------|
| 位设置 | 时钟控制 |
| 00 | 所有模式下都关闭外设时钟 |
| 01 | 只有在运行模式下打开外设时钟,等待模式和停止模式下均关闭外设时钟 |
| 10 | 未使用(保留) |
| 11 | 处理停止模式以外,其它所有模式下时钟都打开 |
根据上表中的位设置,如果我们要打开GPIO2的外设时钟,那么就需要设置 CCM_CCGR0 bit31和bit30都为0即可。
CCM_CCGR0~CCM_CCGR6这7个寄存器操作都是类似的,只是不同寄存器对应不同的外设时钟而已,为了方便开发,本教程后面的所有历程都将 I.MX6U 的所有外设时钟都打开了。至此,我们就解决了 8.1.1 中的所有问题,I.MX6U 的每个外设的时钟都可以独立的禁止和使能。总结一下,要将 I.MX6U 的 IO用作GPIO使用,我们需要一下几步:
- 使能GPIO对应的时钟
- 设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置IO的复用功能,使其复用为GPIO功能。
- 设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置IO的上下拉,速度等等。
- 第2步已经将IO复用为了GPIO功能,所以需要配置GPIO,设置输入输出,是否使能中断,默认输出电平等等。