2. 汇编实现GPIO输出实验(LED)

基于I.MX6U通过汇编实现点亮LED的实验,分析GPIO的控制方法和配置。

一、原理图分析

2. 汇编实现GPIO输出实验(LED)_第1张图片
LED0 接到了 GPIO_3 上, GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。

二、GPIO相关寄存器分析

1、IO功能复用寄存器

2. 汇编实现GPIO输出实验(LED)_第2张图片此实验功能选择ALT5: 0101 GPIO

2、IO的属性配置寄存器

2. 汇编实现GPIO输出实验(LED)_第3张图片
2. 汇编实现GPIO输出实验(LED)_第4张图片
2. 汇编实现GPIO输出实验(LED)_第5张图片

  • HYS(bit16)用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。
  • PUS(bit15:14)用来设置上下拉电阻的,一共有四种选项可以选择。
  • PUE(bit13)图中没有给出来,当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态。
  • PKE(bit12): 此位用来使能或者禁止上下拉/状态保持器功能。
  • ODE(bit11):当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能。
  • SPEED(bit7:6): 当 IO 用作输出的时候,此位用来设置 IO 速度。
  • DSE(bit5:3):当 IO 用作输出的时候用来设置 IO 的驱动能力,总共有 8 个可选项
  • SRE(bit0): 设置压摆率。这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低。如果你的产品要过 EMC 的话那就可以使用小的压摆率,因为波形缓和,如果你当前所使用的 IO做高速通信的话就可以使用高压摆率。

3、IO设置寄存器

2. 汇编实现GPIO输出实验(LED)_第6张图片上图中左下角两个寄存器为1、2中的两个寄存器。

  • DR(数据寄存器_data register)
    2. 汇编实现GPIO输出实验(LED)_第7张图片此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当 GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。

  • GDIR(方向寄存器_direction register)
    2. 汇编实现GPIO输出实验(LED)_第8张图片GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。每个 IO 对应一个位。

  • PSR(状态寄存器_pad status register)
    2. 汇编实现GPIO输出实验(LED)_第9张图片
    PSR 寄存器是只读的,一个 GPIO 对应一个位,读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值。功能和输入状态下的 DR 寄存器一样。

  • ICR1和ICR2(中断控制寄存器_interrupt control register)
    2. 汇编实现GPIO输出实验(LED)_第10张图片
    ICR1 用于 IO0~15 的配置, ICR2 用于 IO16~31 的配置。 ICR1 寄存器中一个 GPIO 用两个位,这两个位用来配置中断的触发方式。

  • IMR(中断屏蔽寄存器_Interrupt Mask Register)
    2. 汇编实现GPIO输出实验(LED)_第11张图片IMR 寄存器也是一个GPIO 对应一个位,IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断,那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。

  • ISR(中断状态寄存器_ interrupt status register)

2. 汇编实现GPIO输出实验(LED)_第12张图片ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 清零。

  • EDGE_SEL(边缘选择寄存器_edge select register)
    2. 汇编实现GPIO输出实验(LED)_第13张图片EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一个 GPIO 对应一个位。如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。例如,我们设置 GPIO1.EDGE_SEL=1,那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。

  • 寄存器地址表
    2. 汇编实现GPIO输出实验(LED)_第14张图片

4、时钟模块控制寄存器

2. 汇编实现GPIO输出实验(LED)_第15张图片在这里插入图片描述32 位寄存器,其中每 2 位控制一个外设的时钟,比如 bit27:16 控制着GPIO3 的外设时钟,两个位就有 4 中操作方式:
2. 汇编实现GPIO输出实验(LED)_第16张图片

三、代码编写

.global _start @全局标号

_start:
    /*使能外设时钟 */
    ldr r0, =0x020c406c @CCGR1
    ldr r1, =0xffffffff @要向CCGR0写入的数据
    str r1, [r0]        @将0xffffffff写入到CCGR0中  
    
    /* 配置 GPIO1_IO03 PIN的复用为GPIO,也就是设置
     * IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03=5
     * IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器地址为0X020E0068
     */
    ldr r0, =0x020E0068 @IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
    ldr r1, =0x5        @要写入的数据
    str r1, [r0]        @将5写入到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中

    /* 配置GPIO1_IO03的电气属性 也就是寄存器:
     * IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器地址为0x020e02f4
     *
     * bit0:    0 低速率
     * bit5:3: 110 R0/6驱动能力
     * bit7:6: 10 100MHz速度
     * bit11:  0 关闭开路输出
     * bit12:  1 使能pull/kepper
     * bit13:  0 kepper
     * bit15:14:00 100K下拉
     * bit16: 0 关闭hys
     *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03=0x10b0
     */
    ldr r0, =0x020e02f4
    ldr r1, =0x10b0
    str r1, [r0]

    /* 设置GPIO1_GDIR寄存器,设置GPIO1_GPIO03为输出
     * GPIO1_GDIR寄存器地址为0x0209c004,设置GPIO1_GDIR寄存器bit3为1,
     * 也就是设置GPIO1_IO03为输出。
     */
     ldr r0, = 0x0209c004
     ldr r1, = 0x8
     str r1, [r0]

     /* 打开LED,也就是设置GPIO1_IO03为0 
      * GPIO1_DR寄存器地址为0x0209c000
      */
      ldr r0, =0x0209c000
      ldr r1, =0x0
      str r1, [r0]

loop:
    b loop

三、代码编译及烧写

1、arm-linux-gnueabihf-gcc 编译文件
arm-linux-gnueabihf-gcc -g -c led.s -o led.o #将led.s编译生成led.o文件

2、arm-linux-gnueabihf-ld 链接文件
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名

链接起始地址为 0X87800000。 I.MX6U-ALPHA 开发板的 DDR 容量有两种: 512MB 和256MB,起始地址都为0X80000000,只不过 512MB 的终止地址为 0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000 这个地址是因为后面要讲的 Uboot 其链接地址就是0X87800000,这样我们统一使用 0X87800000 这个链接地址,不容易记混。

3、arm-linux-gnueabihf-objcopy 格式转换
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息。

4、arm-linux-gnueabihf-objdump 反汇编
arm-linux-gnueabihf-objdump -D led.elf > led.dis
“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件。

5、编写Makefile

led.bin:led.s
    arm-linux-gnueabihf-gcc -g -c led.s -o led.o
    arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
    arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
    arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
    rm -rf *.o led.bin led.elf led.dis

6、将bin烧写到SD卡中
./imxdownload led.bin /dev/sdd
在执行这一步时会多出个load.imx文件。load.imx 这个文件就是软件 imxdownload 根据 NXP 官方启动方式介绍的内容,在 led.bin 文件前面添加了一些数据头以后生成的。最终烧写到 SD 卡里面的就是这个 load.imx 文件,而非led.bin。
led.bin 烧写到 SD 卡中的大小是 3.2KB,用时 0.0160821s,烧写速度是 201KB/s。注意这个烧写速度,如果这个烧写速度在几百 KB/s 以下那么就是正常烧写。如果这个烧写速度大于几十 MB/s、甚至几百 MB/s 那么肯定是烧写失败了!

如何区分自己用的是哪个设备名:
插入SD卡后现像:电脑多出了/dev/sdb、 /dev/sdc、 /dev/sdd、 /dev/sdd1、 /dev/sda和/dev/sdf 这 6 个存储设备
结果:dev/sdd 和/dev/sdd1 是我的 SD 卡,为什么呢?因为只有/dev/sdd 有个对应的/dev/sdd1, /dev/sdd 是我的 SD 卡, /dev/sdd1 是 SD 卡的第一个分区。如果你的 SD 卡有多个分区的话可能会出现/dev/sdd2、 /dev/sdd3 等等

四、现像

SD卡插入卡槽后,将启动方式选为SD卡启动。
LED0 被正常点亮,可能 LED0 之前会有一点微亮,那是因为 I.MX6U的 IO 默认电平可能让 LED0 导通了,但是 IO 的默认配置内部可能有很大的电阻,所以电流就很小,导致 LED0 微亮。但是我们自己编写代码、配置好 IO 以后就不会有这个问题, LED0 就很亮了。

你可能感兴趣的:(I.MX6U,NoOS,物联网,单片机,嵌入式)