系列文章目录
I.MX6ULL Linux C语言开发
I.MX6ULL Linux 点灯实验理论
- 系列文章目录
- 一、I.MX6ULL GPIO
- 二、I.MX6ULL IO 命名
- 三、I.MX6ULL IO 复用
- 四、I.MX6ULL IO 配置
- 五、I.MX6ULL GPIO 配置
- 六、I.MX6ULL GPIO 时钟使能
- 七、硬件原理分析
- 八、实验程序编写
一、I.MX6ULL GPIO
一般拿到一款全新的芯片,第一个要做的事情的就是驱动其 GPIO,控制其 GPIO 输
出高低电平
学习 I.MX6U 也一样的,先来学习一下 I.MX6U 的 GPIO,学习方法打开I.MX6NULL手册,然后就是“啃”手册。
总结一下,要将 I.MX6ULL 的 IO 作为 GPIO 使用,我们需要一下几步:
①、使能 GPIO 对应的时钟。
②、设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用为 GPIO 功能。
③、设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等。
④、第②步已经将 IO 复用为了 GPIO 功能,所以需要配置 GPIO,设置输入/输出、是否使用中断、默认输出电平等。
二、I.MX6ULL IO 命名
I.MX6ULL 参考手册的第 32 章“Chapter 32: IOMUX Controller(IOMUXC)”,第 32 章的书签如图所示:
从图可以看出,I.MX6ULL 的 IO 分为两类:SNVS 域的和通用的
这两类 IO 本质上都是一样的,我们就有下面的常用 IO 为例,讲解一下 I.MX6ULL 的 IO 命名方式。
图中的形如“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”的就是 GPIO 命名 (General-Purpose Input/Output(通用输入/输出)),命名形式就是“IOMUXC_SW_MUC_CTL_PAD_XX_XX”,后面的“XX_XX”就是 GPIO 命名,比如:GPIO1_IO01、UART1_TX_DATA、JTAG_MOD 等等。I.MX6ULL是根据某个 IO 所拥有的功能来命名的。比如我们一看到GPIO1_IO01 就知道这个肯定能做 GPIO,看到 UART1_TX_DATA 肯定就知道这个 IO 肯定能做为 UART1 的发送引脚。“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 复用。
三、I.MX6ULL IO 复用
以“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”这个 IO 为例,打开参考手册的 1568 页,如图所示:
从图中可以看到有个名为:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器,
寄存器地址为 0X020E005C,这个寄存器是 32 位的,但是只用到了最低 5 位,其中
bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的。GPIO1_IO00 一共可以复用为 9种功能 IO,分别对应 ALT0~ALT8,其中 ALT5 就是作为 GPIO1_IO00。GPIO1_IO00 还可以作为 I2C2_SCL、GPT1_CAPTURE1、ANATOP_OTG1_ID 等。这个就是 I.MX6U 的 IO 复用
再看一个“IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA”这个 IO,这个 IO 对应
的复用如图所示:
同样的,从图可以看出,UART1_TX_DATA 可以复用为 8 种不同功能的 IO,分为
ALT0~ALT5 和 ALT8、ATL9,其中 ALT5 表示 UART1_TX_DATA 可以复用为 GPIO1_IO16。
由此可见,I.MX6U 的 GPIO 不止 GPIO1_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 章“Chapter 32: IOMUX Controller(IOMUXC)”,第 32 章详细的列出了所有 IO 对应的复用配置寄存器。
如果某个 IO 要作为某个外设引脚使用的话,是需要配置复用寄存器的。
四、I.MX6ULL IO 配置
在《I.MX6UL 参考手册》第 30 章“Chapter 30: IOMUXController(IOMUXC)”的书签中,每一个 IO 会出现两次,它们的名字差别很小,不仔细看就看不出来,比如 GPIO1_IO00 有如下两个书签
上面两个都是跟 GPIO_IO00 有关的寄存器,名字上的区别就是红色部分,一是“MUX”,
一个是“PAD”。IOMUX_SW_MUX_CTL_PAD_GPIO1_IO00 我们前面已经说了,是用来配置GPIO1_IO00 复用功能的,那么 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 是做什么的呢?找到这个书签对应的 1787 页,如图所示:
从图中可以看出,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 也是个寄存器,寄存器地址为 0X020E02E8。这也是个 32 位寄存器,但是只用到了其中的低 17 位,在看这写位的具体含义之前,先来看一下下图所示的 GPIO 功能图:
我们对照着图来详细看一下寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 的
各个位的含义:
HYS(bit16):对应图中 HYS,用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。此位为 0 的时候禁止迟滞比较器,为 1 的时候使能迟滞比较器。
PUS(bit15:14):对应图中的 PUS,用来设置上下拉电阻的,一共有四种选项可以选
择,如表所示:
PUE(bit13):图没有给出来,当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。当为 0 的时候使用状态保持器,当为 1 的时候使用上下拉。状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态。
PKE(bit12):对应图中的 PKE,此位用来使能或者禁止上下拉/状态保持器功能,为0 时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器。
ODE(bit11):对应图中的 ODE,当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能。
SPEED(bit7:6):对应图中的 SPEED,当 IO 用作输出的时候,此位用来设置 IO 速度,设置如表所示:
DSE(bit5:3):对应图 8.1.4.2 中的 DSE,当 IO 用作输出的时候用来设置 IO 的驱动能力,总共有 8 个可选选项,如表所示:
SRE(bit0):对应图 8.1.4.2 中的 SRE,设置压摆率,当此位为 0 的时候是低压摆率,当为 1的时候是高压摆率。这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低。如果你的产品要过 EMC 的话那就可以使用小的压摆率,因为波形缓和,如果你当前所使用的 IO做高速通信的话就可以使用高压摆率。
通过上面的介绍,可以看出寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 是用来配置 GPIO1_IO00 的,包括速度设置、驱动能力设置、压摆率设置等等。I.MX6U 的 IO 是可以设置速度的
五、I.MX6ULL GPIO 配置
IOMUXC_SW_MUX_CTL_PAD_XX_XX 和 IOMUXC_SW_PAD_CTL_PAD_XX_XX 这两
种寄存器都是配置 IO 的,注意是 IO!不是 GPIO,GPIO 是一个 IO 众多复用功能中的一种。比如 GPIO1_IO00 这个 IO 可以复用为:I2C2_SCL、GPT1_CAPTURE1、ANATOP_OTG1_ID、ENET1_REF_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 结构如图所示:
在图中的左下角 的 IOMUXC 框 图 里 面 就 有 SW_MUX_CTL_PAD_* 和
SW_PAD_CTL_PAD_*两种寄存器。这两种寄存器前面说了用来设置 IO 的复用功能和 IO 属性配置。左上角部分的 GPIO 框图就是,当 IO 用作 GPIO 的时候需要设置的寄存器,一共有八个:
DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR 和 ISR。 前面我们说了 I.MX6U 一共有GPIO1~GPIO5 共五组 GPIO,每组 GPIO 都有这 8 个寄存器。我们来看一下这 8 个寄存器都是什么含义。
DR 寄存器此寄存器是数据寄存器,结构图如图所示:
此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当 GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
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 用两个
位,这两个位用来配置中断的触发方式,可配置的选线如表 所示:位设置
以GPIO1_IO15为例,如果要设置GPIO1_IO15为上升沿触发中断,那么GPIO1.ICR1=2<<30,
如果要设置 GPIO1 的 IO16~31 的话就需要设置 ICR2 寄存器了。
IMR 寄存器,这是中断屏蔽寄存器
如图所示:
IMR 寄存器也是一个 GPIO 对应一个位,IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断,那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。例如,要使能 GPIO1_IO00 的中断,那么就可以设置 GPIO1.MIR=1 即可。
寄存器 ISR,ISR 是中断状态寄存器
寄存器如图所示:
ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 清零。
EDGE_SEL 寄存器,这是边沿选择寄存器
寄存器如图所示:
EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一个 GPIO 对应一个位。如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。例如,我们设置 GPIO1.EDGE_SEL=1,那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。
关于 GPIO 的寄存器就讲解到这里,因为 GPIO 是最常用的功能,我们详细的讲解了 GPIO的 8 个寄存器。I.MX6U 的 IO 是需要配置和输出的、是可以设置输出高低电平,也可以读取 GPIO 对应的电平。
六、I.MX6ULL GPIO 时钟使能
I.MX6ULL每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。I.MX6U 的系统时钟参考《I.MX6UL 参考手册》的第 18 章“Chapter 18: Clock Controller Module(CCM)”,这一章主要讲解 I.MX6U 的时钟系统,很复杂。我们先不研究 I.MX6U的时钟系统,我们只看一下 CCM 里 面 的 外 设 时 钟 使 能 寄 存 器 。 CMM 有CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关,我们以 CCM_CCGR0 为例来看一下如何禁止或使能一个外设的时钟,CCM_CCGR0 结构体如图所示:
CCM_CCGR0 是个 32 位寄存器,其中每 2 位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟,两个位就有 4 种操作方式,如表所示:
根据表中的位设置,如果我们要打开 GPIO2 的外设时钟,那么只需要设置
CCM_CCGR0 的 bit31 和 bit30 都为 1 即可,也就是 CCM_CCGR0=3 << 30。反之,如果要关闭GPIO2 的 外 设 时 钟 , 那 就 设 置 CCM_CCGR0 的 bit31 和 bit30 都 为 0 即可。
CCM_CCGR0~CCM_CCGR6 这 7 个寄存器操作都是类似的,只是不同的寄存器对应不同的外设时钟而已,为了方便开发,本教程后面所有的例程将 I.MX6U 的所有外设时钟都打开了。
总结一下,要将 I.MX6ULL 的 IO 作为 GPIO 使用,我们需要一下几步:
①、使能 GPIO 对应的时钟。
②、设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用为 GPIO 功能。
③、设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等。
④、第②步已经将 IO 复用为了 GPIO 功能,所以需要配置 GPIO,设置输入/输出、是否使用中断、默认输出电平等。
七、硬件原理分析
打开 I.MX6U-ALPHA 开发板底板原理图,I.MX6UALPHA 开发板上有一个 LED 灯,原理图如下所示:
从图可以看出,LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03
输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。
八、实验程序编写
我们需要对 GPIO1_IO03 做如下设置:
1、使能 GPIO1 时钟GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制,将这两个位都设置位 11 即可。将 I.MX6U 的所有外设时钟都已经打开了,因此这一步可以不用做。
2、设置 GPIO1_IO03 的复用功能找到 GPIO1_IO03 的复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为0X020E0068,然后设置此寄存器,将 GPIO1_IO03 这个 IO 复用为 GPIO 功能,也就是 ALT5。
3、配置 GPIO1_IO03
找到 GPIO1_IO03 的配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03”的地址为0X020E02F4,根据实际使用情况,配置此寄存器。
4、设置 GPIO
将 GPIO1_IO03 复用为了 GPIO 功能,所以我们需要配置 GPIO。找到 GPIO3 对
应的 GPIO 组寄存器地址,在《IMX6ULL 参考手册》的 1357 页,如图所示:
本实验中 GPIO1_IO03 是作为输出功能的,因此 GPIO1_GDIR 的 bit3 要设置为 1,表示输出
5、控制 GPIO 的输出电平
经过前面几步,GPIO1_IO03 已经配置好了,只需要向 GPIO1_DR 寄存器的 bit3 写入 0 即可控制 GPIO1_IO03 输出低电平,打开 LED,向 bit3 写入 1 可控制 GPIO1_IO03 输出高电平,关闭 LED。
所有的裸机实验我们都在 Ubuntu 下完成,使用 VSCode 编辑器!
既然是实验,肯定要自己动手创建工程,新建一个名为“1_leds”的文件夹,然后在“1_leds”这个目录下新建一个名为“led.s”的汇编文件和一个名为“.vscode”的目录,创建好以后“1_leds”文件夹
.vscode 文件夹里面存放 VSCode 的工程文件,led.s 就是我们新建的汇编文件,
在 led.s 这个文件中编写汇编程序。使用 VSCode 打开 1_leds 这个文件夹,打开以后
如图所示:
在 led.s 中输入如下代码:
1
2 .global _start /* 全局标号 */
3
4 /*
5 * 描述: _start 函数,程序从此函数开始执行此函数完成时钟使能、
6 * GPIO 初始化、最终控制 GPIO 输出低电平来点亮 LED 灯。
7 */
8 _start:
9 /* 例程代码 */
10 /* 1、使能所有时钟 */
11 ldr r0, =0X020C4068 /* 寄存器 CCGR0 */
12 ldr r1, =0XFFFFFFFF
13 str r1, [r0]
14
15 ldr r0, =0X020C406C /* 寄存器 CCGR1 */
16 str r1, [r0]
17
18 ldr r0, =0X020C4070 /* 寄存器 CCGR2 */
19 str r1, [r0]
20
21 ldr r0, =0X020C4074 /* 寄存器 CCGR3 */
22 str r1, [r0]
23
24 ldr r0, =0X020C4078 /* 寄存器 CCGR4 */
25 str r1, [r0]
26
27 ldr r0, =0X020C407C /* 寄存器 CCGR5 */
28 str r1, [r0]
29
30 ldr r0, =0X020C4080 /* 寄存器 CCGR6 */
31 str r1, [r0]
32
33
34 /* 2、设置 GPIO1_IO03 复用为 GPIO1_IO03 */
35 ldr r0, =0X020E0068 /* 将寄存器 SW_MUX_GPIO1_IO03_BASE 加载到 r0 中 */
36 ldr r1, =0X5 /* 设置寄存器 SW_MUX_GPIO1_IO03_BASE 的 MUX_MODE 为 5 */
37 str r1,[r0]
38
39 /* 3、配置 GPIO1_IO03 的 IO 属性
40 *bit 16:0 HYS 关闭
41 *bit [15:14]: 00 默认下拉
42 *bit [13]: 0 kepper 功能
43 *bit [12]: 1 pull/keeper 使能
44 *bit [11]: 0 关闭开路输出
45 *bit [7:6]: 10 速度 100Mhz
46 *bit [5:3]: 110 R0/6 驱动能力
47 *bit [0]: 0 低转换率
48 */
49 ldr r0, =0X020E02F4 /*寄存器 SW_PAD_GPIO1_IO03_BASE */
50 ldr r1, =0X10B0
51 str r1,[r0]
52
53 /* 4、设置 GPIO1_IO03 为输出 */
54 ldr r0, =0X0209C004 /*寄存器 GPIO1_GDIR */
55 ldr r1, =0X0000008
56 str r1,[r0]
57
58 /* 5、打开 LED0
59 * 设置 GPIO1_IO03 输出低电平
60 */
61 ldr r0, =0X0209C000 /*寄存器 GPIO1_DR */
62 ldr r1, =0
63 str r1,[r0]
64
65 /*
66 * 描述: loop 死循环
67 */
68 loop:
69 b loop
第 2 行定义了一个全局标号_start,代码就是从_start 这个标号开始顺序往下执行的。
第 11 行使用 ldr 指令向寄存器 r0 写入 0X020C4068,也就是 r0=0X020C4068,这个是
CCM_CCGR0 寄存器的地址。
第 12 行使用 ldr 指令向寄存器 r1 写入 0XFFFFFFFF,也就是 r1=0XFFFFFFFF。因为要开启所有的外设时钟,因此 CCM_CCGR0~CCM_CCGR6 所有寄存器的 32 位都要置 1,也就是写入 0XFFFFFFFF。
第 13 行使用 str 将 r1 中的值写入到 r0 所保存的地址中去,也就是给 0X020C4068 这个地址写入 0XFFFFFFFF,相当于 CCM_CCGR0=0XFFFFFFFF,就是打开CCM_CCGR0 寄存器所控制的所有外设时钟。
第 15~31 行都是向 CCM_CCGRX(X=1~6)寄存器写入 0XFFFFFFFF。这样我就通过汇编代
码使能了 I.MX6U 的所有外设时钟。
第35~37行是设置GPIO1_IO03的复用功能,GPIO1_IO03的复用寄存器地址为0X020E0068,寄存器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的 MUX_MODE 设置为 5 就是将GPIO1_IO03 设置为 GPIO。
第 49~51 行 是 设 置 GPIO1_IO03 的 配 置 寄 存 器 , 也 就 是 寄 存 器
IOMUX_SW_PAD_CTL_PAD_GPIO1_IO03 的值,此寄存器地址为 0X020E02F4,代码里面已经给出了这个寄存器详细的位设置。
第 54~63 行是设置 GPIO 功能,经过上面几步操作,GPIO1_IO03 这个 IO 已经被配置为了GPIO 功能,所以还需要设置跟 GPIO 有关的寄存器。第 54~56 行是设置 GPIO1->GDIR 寄存器,
将 GPIO1_IO03 设置为输出模式,也就是寄存器的 GPIO1_GDIR 的 bit3 置 1。
第 61~63 行设置 GPIO1->DR 寄存器,也就是设置 GPIO1_IO03 的输出,我们要点亮开发板上的 LED0,那么 GPIO1_IO03 就必须输出低电平,所以这里设置 GPIO1_DR 寄存器为 0。
第 68~69 行是死循环,通过 b 指令,CPU 重复不断的跳到 loop 函数执行,进入一个死循环。
END