嵌入式Linux开发第一步——汇编点灯实验

I.MX6U IO 复用

  以 IO“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”为例,打开参考手册,如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第1张图片
   从图中可以看到有个名为: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_SCLGPT1_CAPTURE1、 ANATOP_OTG1_ID 等。这个就是 I.MX6U 的 IO 复用,我们学习 STM32 的时候 STM32 的 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 能复用什么外设的话可以直接查阅《IMX6UL 参考手册》的第 4 章“Chapter 4 External Signals and Pin Multiplexing”。如果我们要编写代码,设置某个 IO 的复用功能的话就需要查阅第 30 章“Chapter 30: IOMUXController(IOMUXC)” ,第 30 章详细的列出了所有 IO 对应的复用配置寄存器。

I.MX6U IO 配置

  阅读的过程中发现每个IO会出现两次,但他们的差别很小,比如:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00、
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00;
具体功能如下:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00用来设置复用功能,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00用来进行配置的,比如速度设置、驱动能力设置、压摆率设置等。

I.MX6U GPIO 配置

  GPIO结构图如下所示:
嵌入式Linux开发第一步——汇编点灯实验_第2张图片
  左下角的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寄存器,此寄存器是数据寄存器,结构图如下所示:
嵌入式Linux开发第一步——汇编点灯实验_第3张图片
  此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当 GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
  看完 DR 寄存器,接着看 GDIR 寄存器,这是方向寄存器,用来设置某个 GPIO 的工作方向的,即输入/输出。结构图如下所示:
嵌入式Linux开发第一步——汇编点灯实验_第4张图片
  GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位,如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为输出的话就设置为 1。比如要设置GPIO1_IO00 为输入,那么 GPIO1.GDIR=0。
  接下来看 PSR 寄存器,这是 GPIO 状态寄存器,如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第5张图片

  同样的 PSR 寄存器也是一个 GPIO 对应一个位,读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值。功能和输入状态下的 DR 寄存器一样。

  接下来看ICR1和ICR2这两个寄存器,都是中断控制寄存器, ICR1用于配置低16个GPIO,ICR2 用于配置高 16 个 GPIO, ICR1 寄存器如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第6张图片

  ICR1 用于 IO0~15 的配置, ICR2 用于 IO16~31 的配置。 ICR1 寄存器中一个 GPIO 用两个位,这两个位用来配置中断的触发方式,和 STM32 的中断很类似,可配置的选线如表所示:
嵌入式Linux开发第一步——汇编点灯实验_第7张图片
  以GPIO1_IO15为例,如果要设置GPIO1_IO15为上升沿触发中断,那么GPIO1.ICR1=2<<30,如果要设置 GPIO1 的 IO16~31 的话就需要设置 ICR2 寄存器了。
接下来看 IMR 寄存器,这是中断屏蔽寄存器,如下图所示:
嵌入式Linux开发第一步——汇编点灯实验_第8张图片

  IMR 寄存器也是一个 GPIO 对应一个位, IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断,那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。例如,要使能 GPIO1_IO00 的中断,那么就可以设置 GPIO1.MIR=1 即可。

  接下来看寄存器 ISR, ISR 是中断状态寄存器,如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第9张图片
  ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 清零。

  最后来看一下 EDGE_SEL 寄存器,这是边沿选择寄存器,寄存器如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第10张图片
EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一个 GPIO 对应一个位。如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。例如,我们设置 GPIO1.EDGE_SEL=1,那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。

I.MX6U GPIO 时钟使能

  STM32 的每个外设都有一个外设时钟, GPIO 也不例外,要使用某个外设,必须要先使能对应的时钟。 I.MX6U 其实也一样的,每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。如果要使用某个外设的话必须要先使能其时钟。
  我们先不研究 I.MX6U 的时钟系统,我们只看一下 CCM 里面
的外设时钟使能寄存器。 CCM 有 CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关,我们以CCM_CCGR0 为例来看一下如何禁止或使能一个外设的时钟, CCM_CCGR0 结构体如图所示:
嵌入式Linux开发第一步——汇编点灯实验_第11张图片
  CCM_CCGR0 是个 32 为寄存器,其中每 2 位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟,两个位就有4种操作方式,如表所示:
嵌入式Linux开发第一步——汇编点灯实验_第12张图片
  根据表 8.1.6.1 中的位设置,如果我们要打开 GPIO2 的外设时钟,那么只需要设置CCM_CCGR0 的 bit31 和 bit30 都为 1 即可,也就是CCM_CCGR0=3 << 30。反之,如果要关闭GPIO2 的外设时钟,那就设置CCM_CCGR0的bit31和bit30都为0即可。

  总结一下,要将 I.MX6U 的 IO 作为 GPIO 使用,我们需要一下几步:
① 使能 GPIO 对应的时钟。
② 设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用为 GPIO 功能。
③ 设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等等。
④ 第②步已经将 IO 复用为了 GPIO 功能,所以需要配置 GPIO,设置输入/输出、是否使用中断、默认输出电平等。

硬件原理分析

  本示例采用正点原子的Linux mini板子,硬件原理图如下所示:
嵌入式Linux开发第一步——汇编点灯实验_第13张图片
  从图中可以看出, LED0 接到了 GPIO_3 上,GPIO_3 就是GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。

实验程序编写

实验程序编写具体步骤如下:
1、使能 GPIO1 时钟;
2、设置 GPIO1_IO03 的复用功能;
3、配置 GPIO1_IO03;
4、设置 GPIO;
5、控制 GPIO 的输出电平。

.global _start /* 全局标号 */
_start:
ldr r0, =0X020C4068 /* 寄存器 CCGR0 */
ldr r1, =0XFFFFFFFF
str r1, [r0]
ldr r0, =0X020C406C /* 寄存器 CCGR1 */
str r1, [r0]
ldr r0, =0X020C4070 /* 寄存器 CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* 寄存器 CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* 寄存器 CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* 寄存器 CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* 寄存器 CCGR6 */
str r1, [r0]

/* 2、设置 GPIO1_IO03 复用为 GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器 SW_MUX_GPIO1_IO03_BASE 加载到 r0 中 */
ldr r1, =0X5 /* 设置寄存器 SW_MUX_GPIO1_IO03_BASE 的 MUX_MODE 为 5 */
str r1,[r0]

/* 3、配置 GPIO1_IO03 的 IO 属性
*bit 16:0 HYS 关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper 功能
*bit [12]: 1 pull/keeper 使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度 100Mhz
*bit [5:3]: 110 R0/6 驱动能力
*bit [0]: 0 低转换率
*/
ldr r0, =0X020E02F4 /*寄存器 SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]

/* 4、设置 GPIO1_IO03 为输出 */
ldr r0, =0X0209C004 /*寄存器 GPIO1_GDIR */
ldr r1, =0X0000008
str r1,[r0]
/* 5、打开 LED0
* 设置 GPIO1_IO03 输出低电平
*/
ldr r0, =0X0209C000 /*寄存器 GPIO1_DR */
ldr r1, =0
str r1,[r0]

/*
* 描述: loop 死循环
*/
loop:
	b loop

  最后效果图,LED0常亮:
嵌入式Linux开发第一步——汇编点灯实验_第14张图片

你可能感兴趣的:(Linux驱动开发,linux,stm32,嵌入式)