5.汇编实现裸机LED

Posted zhaipanger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5.汇编实现裸机LED相关的知识,希望对你有一定的参考价值。



  首先:操作LED就要操作GPIO  alpha的芯片是NXP的IMX6ULL  其GPIO和STM32的命名有所区别

                技术图片

 

 

   可以看到IMX6ULL的GPIO以其功能进行命名,对应上图中PAD之后的部分

    即GPIO_IO00~GPIO_IO09 JTAG_MOD等,用户按照名字就可以知道对应引脚的功能。并且IMX6ULL的GPIO也是可以复用的。

  I.MX6U GPIO 一共有 5 组: GPIO1GPIO2GPIO3GPIO4 GPIO5

    其中 GPIO1 32 IOGPIO2 22 IOGPIO3 29 IOGPIO4 29 IOGPIO5最少,只有 12 IO,这样一共有 124 GPIO  


 


   对IO的配置


   首先要确定IO的复用功能是GPIO,看一下控制复用功能的寄存器(这里截取的是GPIO1_IO00的复用控制寄存器)

       技术图片

     显然,bit4是复用功能的“总开关”,设置为1时强制为GPIO1_IO00功能;置0时由bit3:0来决定

    bit3:0决定该IO复用为什么功能,可以看到这里ALT5就是复用为GPIO1_IO00功能,除此之外还有八种可选


   再看一下IO的功能图  

                 技术图片

 

  功能图中

      HYS:用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。

         如果需要对输入波形进行整形的话可以使能此位。此位为 0 的时候禁止迟滞比较器,为 1 的时候使能迟滞比较器。

      PUS:用来设置上下拉电阻的,一共有四种选项可以选择,分别为100K下拉,47K上拉,100K上拉,22K上拉。

      PUE(图中未给出):当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。

         当为 0 的时候使用状态保持器,当为 1 的时候使用上下拉。

         状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态。 

      PKE:此为用来使能或者禁止上下拉/状态保持器功能,为0 时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器。
      ODE:当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能。
      SPEED:当 IO 用作输出的时候,此位用来设置 IO 速度,分别为低俗50M,中速100M,中速100M,最大速度200M。  

      DSE:IO 用作输出的时候用来设置 IO 的驱动能力,总共有 8 个可选选项

          技术图片

       SRE:设置压摆率,当此位为 0 的时候是低压摆率,当为 1的时候是高压摆率。

         这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 1 需要多少时间,时间越小波形就越陡,说明压摆率越高; 


   对应每个IO,都有一组寄存器去控制,这是毋庸置疑的,下面是GPIO_IO00的控制寄存器

  技术图片

  技术图片

  可见 对于IO的每个功能端都有相应的寄存器位进行配置。


 


 

 GPIO功能配置  

  但是我们没有看到如何设置 IO 为输入还是输出? 因为上面的配置只是针对IO的配置,对于GPIO来说,它只是IO的一个复用功能而已,所以还要对GPIO的功能进行配置

  所以对于GPIO来说,也需要有相应的寄存器来控制,GPIO的控制框图如下:

           技术图片

     可见,配置好SW_MUX_CTL_PAD和SW_PAD_CTL_PAD两个寄存器后,还需要对GPIO进行单独配置,共有DR/GDIR/PSR/ICR1/ICR2/EDGE_SEL/IMR/ISR八个寄存器

    技术图片技术图片

     I.MX6U 一共有GPIO1~GPIO5 共五组 GPIO,每组 GPIO 都有这 8 个寄存器。


DR寄存器:数据寄存器(data register)

    技术图片

   此寄存器是 32 位的,一个 GPIO 组最大只有 32 IO,因此 DR 寄存器中的每个位都对应一个 GPIO

  当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1

  GPIO 被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR bit0 就是 0。 

      技术图片

  这里注意:如果GDIR[n]设置为输入,但该IO复用功能不是GPIO,那么读DR[n]始终是0值,所以读之前请确认IO是否复用为GPIO。


GDIR寄存器:方向寄存器(GPIO direction register)

  技术图片

  很简单,每一个位对应一个GPIO,对应位为0则该位为输入模式,反之为输出模式。


 PSR寄存器:GPIO状态寄存器(pad status register)

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

 

   技术图片


ICR寄存器:中断配置寄存器(interrupt configuration register)

  分为ICR1和ICR2两个寄存器,分别配置低16位和高16位GPIO,即每个GPIO占两位。

   技术图片

     通过每个GPIO对应的两位设置可以配置低电平、高电平、上升沿、下降沿四种模式。


 IMR寄存器:中断屏蔽寄存器(interrupt mask register)

  每个GPIO对应一位,0为屏蔽中断,1为使能中断

  (个人认为这里的屏蔽翻译不恰当,因为下面UNMASKED和MASKED的功能和使用中屏蔽的意思好像是相反的呢?)

   技术图片


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

  当中断发生时,相应中短线对应的位被置1,用来在特定中断组中查询是哪条中短线发生了中断。

  在每次处理中断完成后必须手动清零,清除的方法是向要清除的位写1。

  技术图片


EDGE_SEL寄存器:边沿选择寄存器(edge select register)

   EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 ICR2 的设置,同样是一个 GPIO 对应一个位。

   如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。

   例如,我们设置 GPIO1.EDGE_SEL=1,那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。

   因为ICR中没有双边沿的设置选项。

  技术图片



 GPIO时钟配置

  配置完相应GPIO的功能还不能使GPIO工作起来,还要对GPIO的时钟进行使能,I.MX6U 的系统时钟参考《I.MX6UL 参考手册》的第 18 章“Chapter 18: Clock Controller Module(CCM)” 

     先不研究 I.MX6U 的时钟系统,只看一下 CCM 里面的外设时钟使能寄存器。 

     CMM CCM_CCGR0~CCM_CCGR6 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关


CCM_CCGR0 寄存器:CCM时钟门控寄存器(CCM Clock Gating Register 1

  CCM_CCGR0 是个 32 为寄存器,其中每 2 位控制一个外设的时钟 ,例如这里的bit31:30控制GPIO2的时钟

      技术图片

   其设置方法为:对于GPIO来说,开启时钟即两位都置1,关闭则两位都置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,设置输入/输出、是否使用中断、默认输出电平等。


硬件分析

    技术图片

   显然,低电平点灯,高电平灭灯。



  点灯程序编写

    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 组寄存器地址

        技术图片

        本实验中 GPIO1_IO03 是作为输出功能的,因此 GPIO1_GDIR bit3 要设置为 1,表示输出。
    5、控制 GPIO 的输出电平
        经过前面几步, GPIO1_IO03 已经配置好了,只需要向 GPIO1_DR 寄存器的 bit3 写入 0 即可控制 GPIO1_IO03 输出低电平,打开 LED,向 bit3 写入 1 可控制 GPIO1_IO03 输出高电平,关闭 LED


    具体程序 

 1 /*
 2  * @Author: qjx 
 3  * @Date: 2020-05-02 14:16:50 
 4  * @Last Modified by: qjx
 5  * @Last Modified time: 2020-05-02 14:48:38
 6  *   汇编点灯程序
 7  */
 8 
 9 /*全局标号 */
10 .global _start
11 
12 /*
13   *     _start函数,程序从这里开始
14   *                 分别完成: 1、时钟使能
15                                             2、IO复用
16                                             3、GPIO配置
17                                             4、控制GPIO.DR寄存器点灯
18   */
19 
20 _start:
21 /*第一步:使能所有时钟 */
22 
23 ldr r0,=0X020C4068 @CCGR0地址
24 ldr r1,=0XFFFFFFFF @设置的值(全1)
25 
26 str r1,[r0] @把r1存到r0指向的地址中
27 
28 ldr r0,=0X020C406C @CCGR1地址
29 str r1,[r0] @把r1存到r0指向的地址中
30 
31 ldr r0,=0X020C4070 @CCGR2地址
32 str r1,[r0] @把r1存到r0指向的地址中
33 
34 ldr r0,=0X020C4074 @CCGR3地址
35 str r1,[r0] @把r1存到r0指向的地址中
36 
37 ldr r0,=0X020C4078 @CCGR4地址
38 str r1,[r0] @把r1存到r0指向的地址中
39 
40 ldr r0,=0X020C407C @CCGR5地址
41 str r1,[r0] @把r1存到r0指向的地址中
42 
43 ldr r0,=0X020C4080 @CCGR6地址
44 str r1,[r0] @把r1存到r0指向的地址中
45 
46 /*第二步、设置GPIO1_IO03复用为GPIO_IO03 */
47 ldr r0,=0X020E0068 @SW_MUX_CTL_PAD_GPIO1_IO03的地址
48 ldr r1,=0X00000005 @设置低4位为0101
49 str r1,[r0] @把r1存到r0指向的地址中
50 
51 /*第三步、配置GPIO */
52 /*
53 *bit 16:0 HYS 关闭
54 *bit [15:14]: 00 默认下拉
55 *bit [13]: 0 kepper 功能
56 *bit [12]: 1 pull/keeper 使能
57 *bit [11]: 0 关闭开路输出
58 *bit [7:6]: 10 速度 100Mhz
59 *bit [5:3]: 110 R0/6 驱动能力
60 *bit [0]: 0 低转换率 
61 */
62 ldr r0,=0X020E02F4 @SW_PAD_CTL_PAD_GPIO1_IO03的地址
63 ldr r1,=0X000010B0 @设置对应属性
64 str r1,[r0] @把r1存到r0指向的地址中
65 
66 /*第四步、设置GPIO_IO03为输出 */
67 ldr r0,=0X0209C004 @GPIO1_GDIR的地址
68 ldr r1,=0X00000008 @设置bit3为1
69 str r1,[r0] @把r1存到r0指向的地址中
70 
71 /*第五步、打开LED  设置输出低电平 */
72 ldr r0,=0X0209C000  @GPIO1_DR的地址
73 ldr r1,=0X00000000 @全为0
74 str r1,[r0] @把r1存到r0指向的地址中
75 
76 loop:
77     b loop

 



 编译代码

  1arm-linux-gnueabihf-gcc 编译文件

1 arm-linux-gnueabihf-gcc -g -c led.s -o led.o

    上述命令就是将 led.s 编译为 led.o,其中“-g”选项是产生调试信息, GDB 能够使用这些调试信息进行代码调试。

    “-c”选项是编译源文件,但是不链接。“-o”选项是指定编译产生的文件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生成一个 led.o 文件
  2arm-linux-gnueabihf-ld 链接文件

1 arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf

 

     上述命令中-Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名,这里我们命名为 led.elf。上述命令执行完以后就会在工程目录下多一个 led.elf 文件
     注:这里的链接地址可以是0X80000000  为了和后面Uboot的地址统一 不容易记混 这里取了0X87800000
  3arm-linux-gnueabihf-objcopy 格式转换

1 arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin

    .elf不是最终烧写到SD的文件,需要转换为.bin文件

    上述命令中,“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息。


    到这里就已经具备烧录条件了,alpha需要SD卡启动,要把imxdownload拷贝到工程文件夹下(只能在linux下运行)

    给予它可执行权限

1 chmod 777 imxdownload

    确定要烧写的SD卡名称后进行烧录

1 ./imxdownload led.bin /dev/(对应的sd卡名称)

 

    然后把SD卡插入卡槽,调整拨码开关至 1 7开

    上电可以看到LED微闪一下,稍后正常点亮,说明烧录成功。


  续上:

  4arm-linux-gnueabihf-objdump 反汇编
    大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:

1 arm-linux-gnueabihf-objdump -D led.elf > led.dis    

    上述代码中的“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件


 


将上面的步骤做成Makefile

 

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



                                                  2020-05-02 15:38:07

 

以上是关于5.汇编实现裸机LED的主要内容,如果未能解决你的问题,请参考以下文章

ARM(IMX6U)裸机汇编LED驱动实验——驱动编写编译烧写bin文件到SD卡中并运行

ARM(IMX6U)裸机C语言版本LED驱动实验

微机原理汇编语言课程设计 中的LED 灯显示控制代码怎么写?

单片机裸机实用组件--LED

S3C2440-裸机篇-04 | ARM-THUMB子程序调用规则ATPCS

单片机裸机实用组件--LED