zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    MyBatis映射文件中用#和$传递参数的特点
    使用谷歌浏览器进行Web开发技巧
    YYYY-mm-dd HH:MM:SS 备忘录
    java通过UUID生成16位唯一订单号
    idea如何设置类头注释和方法注释
    如何用符号构建人的思维系统?
    临界点思维模型
    复利思维模型-拥抱人生的指数增长
    提升自我认知的有效方式
    如何去培养顶尖的思维模型?
  • 原文地址:https://www.cnblogs.com/zhaipanger/p/12818321.html
Copyright © 2011-2022 走看看