ARM-03-点亮led

本篇使用得开发板是IMX6ULL------MINI

一、交叉编译工具链安装(通用方法)

  1. 从arm官网下载交叉编译功能,将gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz压缩包拷贝到ubuntu下

  2. 解压 tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

  3. 将arm-linux-gnueabihf-gcc的路径添加进系统PATH下 --- 在家目录下的 .bashrc 文件最后加一句export PATH=$PATH:/home/linux/tools/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/

  4. 查看arm-linux-gcc的版本, arm-linux-gnueabihf-gcc -v , 如果是4.9则成功


二、imx6ull硬件控制(LED)

1. 查看原理图,确定硬件连接关系

  • 目标: 找到LED灯在开发板上连接的具体GPIO引脚。

  • 分析结果

    • LED0连接到引脚 GPIO1_IO03

    • 工作逻辑 : 当GPIO1_IO03输出 低电平(0) ​ 时,LED0导通并发光;当输出 **高电平(1)**​ 时,LED0截止,熄灭。

    • 结论: 通过控制GPIO1_IO03的电平状态(0或1)即可实现LED的亮灭控制。

2. 配置

---选择引脚功能(MUXIO_SW)

---配置引脚电气特性(PAD_SW)

---配置引脚方向(DIR)

---输出高低电平(DR)

  • (1) 选择引脚功能 (MUXIO_SW)

    查看IMX6ULL参考手册可以找到GPIO3的选择引脚功能寄存器(SW_MUX)

  • 由图不难看出,将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器的低4位设置为0x0005,即将GPIO1_IO3引脚功能设置为GPIO。

  • (2) 配置引脚电气特性 (PAD_SW)

  • 查看IMX6ULL参考手册可以找到GPIO3的配置引脚电气特性寄存器(SW_PAD)

将IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器的低4位设置为0x10b0,GPIO1_IO3引脚使用默认的电气属性

  • (3) 配置引脚方向和电平 (GDIR和DR)

    GDIR (GPIO Direction Register) : 设置引脚方向。写 1为输出模式,写 0为输入模式。

    • DR (Data Register) : 设置引脚输出电平。写 0输出低电平,写 1输出高电平。

三、写代码

1. 整体代码

  • 会IMX6ULL的硬件控制原理后,这里就开始写代码,在unbuntu下使用交叉编译器arm-linux-gnueabihf-gcc就无须像keil4上面一样那么繁琐:

    在GNU中可以直接用**.global _start**
    *

    cs 复制代码
       .global _start
        ;上面一句可代替下面内容
        area reset, code, readonly
    	code32
    	entry
    cs 复制代码
    .global _start
     
    _start:
        ldr pc, =_reset_handler
        ldr pc, =_software_handler
        ldr pc, =_undef_handler
        ldr pc, =_prefetch_abort_handler
        ldr pc, =_data_abort_handler
        nop
        ldr pc, =_irq_handler
        ldr pc, =_fiq_handler
     
    _software_handler:
        b _software_handler
     
    _undef_handler:
        b _undef_handler
     
    _prefetch_abort_handler:
        b _prefetch_abort_handler
     
    _data_abort_handler:
        b _data_abort_handler
     
    _irq_handler:
        b _irq_handler
     
    _fiq_handler:
        b _fiq_handler
     
    _reset_handler:
        /*DDR    0x80000000 ~ 0X9FFFFFFF*/
        cpsid i             /*disable irq*/
        ldr sp, =0x81000000 /*init system mode  stack  16M   */
        cps #0x12           /*change to irq mode */
        /* 
        mrs r0, cpsr
        bic r0, r0, #0x1f
        orr r0, r0, #0x12
        msr cpsr_c, r0
        */
        ldr sp, =0x82000000 /*init irq mode stack  16M */
     
        cps #0x1f           /* change to system mode */
        /* 
        mrs r0, cpsr
        orr r0, r0, #0x1f
        msr cpsr_c, r0
        */
        cpsie i     /* enable  irq */
     
        bl led_init
    led:
        bl led_on
        bl led_delay
        bl led_off
        bl led_delay
        b led
     
    code_end:
        b code_end
     
    led_init:
        ldr r0, =0x20e0068
        ldr r1, [r0]
        bic r1, r1, #0x1f
        orr r1, r1, #0x05
        str r1, [r0]
     
        ldr r0, =0x20e02f4
        ldr r1, [r0]        /*unuse*/
        ldr r1, =0x10b0
        str r1, [r0]
     
        ldr r0, =0x209c004
        ldr r1, [r0]
        orr r1, r1, #(1 << 3)
        str r1, [r0]
     
        ldr r0, =0x209c000
        ldr r1, [r0]
        orr r1, r1, #(1 << 3)
        str r1, [r0]
        bx lr
     
    led_on:
        ldr r0, =0x209c000
        ldr r1, [r0]
        bic r1, r1, #(1 << 3)
        str r1, [r0]
        bx lr
     
    led_off:
        ldr r0, =0x209c000
        ldr r1, [r0]
        orr r1, r1, #(1 << 3)
        str r1, [r0]
        bx lr
     
    led_delay:
        ldr r0, =0x80001
     
    loop_delay:
        sub r0, r0, #1
        cmp r0, #0
        bge loop_delay
        bx lr

    注意:

    1.这里有些函数有下划线有些没有,是用来区分 异常处理程序、启动代码、系统内部函数 和 普通应用程序逻辑 的函数的

    2.cpsid i / cpsie i:禁止IRQ中断/使能IRQ中断(i指的是IRQ,f指FIQ),cps这个指令可以直接切换arm工作模式

    这个指令相当于在执行下面的内容

    cs 复制代码
        cps #0x12 
        ;上面的等价于下面的代码
    	ldr sp, =0x81000000		 ;初始化栈指针
    	mrs r0, cpsr				  ;将当前程序状态寄存器(CPSR)的值读取到通用寄存器 r0
    	bic r0, r0, #0x1f			   ;将 r0 的低 5 位清零(0x1F = 31)。低 5 位是 CPSR 的模式位(M[4:0]),这一步是清除当前模式
    	orr r0, r0, #0x10			   ;将 r0 的低 5 位设置为 10000(二进制),即 用户模式(User Mode)的编码。
    	msr cpsr_c, r0					;将 r0 的值写入 CPSR 的控制域(c 表示控制域,包含模式位、中断使能位等)
    1. bx lr 相当于mov pc, lr,但比后者更好,更推荐使用

    4.看手册DDR内存是0x8000 0000开始,这个板子上的DDR是一个512M的内存,所以总共大小是

    0x8000 0000~0x9FFFFFFF(512MB换成十六进制是0x2000 0000)

    所以SP栈寄存器的地址这里初始化为0x8100 0000(满减栈),delay函数的数值是随便设置的


四、上板(烧写)

1、使用arm-linux-gnueabihf-gcc编译文件

有了工具链,就可以编译我们led_asm.s文件了,命令为:arm-linux-gnueabihf-gcc-g-cled.s-oled_.o 。其中"-g"选项是产生调试信息,GDB能够使用这些调试信息进行代码调试。"-c"选项是编译源文件,但是不链接。"-o"选项是指定编译产生的文件名字,这里我们指定 led.s编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生成一个 led.o文件。

2、使用arm-linux-gnueabihf-ld链接文件

上面的led.o文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的 C文件和汇编文件都会编译生成一个对应的.o文件,我们需要将这.o文件链接起来组合成可执行文件。那么连接到底是个什么概念呢?之前我们使用Keil的时候也没有这个步骤呀?事实上我们使用的Keil是一种IDE,这种集成开发环境其实组合了编译和链接为一体,只是不需要我们手动操作罢了。

这里我们要区分"存储地址"和"运行地址"这两个概念,"存储地址"就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。"运行地址"就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。比如 I.MX6U支持 SD卡、EMMC、NAND启动,因此代码可以存储到 SD卡、EMMC或者 NAND中,但是要运行的话就必须将代码从 SD卡、EMMC或者NAND中拷贝到其运行地址(链接地址)处。需要注意的是"存储地址"和"运行地址"可能是一样的,比如STM32的存储起始地址和运行起始地址都是 0X08000000。

本篇中,我们烧写到 SD卡中,上电以后 I.MX6U的内部 bootrom程序会将可执行文件拷贝到链接地址处,这个链接地址可以在 I.MX6U的内部 128KBRAM中(0X900000~0X91FFFF),也可以在外部的 DDR中。我们例程的链接地址都在 DDR中,链接起始地址为 0X87800000。I.MX6U-ALPHA开发板的 DDR容量有两种:512MB和256MB,起始地址都为 0X80000000,只不过 512MB的终止地址为 0X9FFFFFFF,而 256MB容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000这个地址是因为后面要讲的 Uboot其链接地址就是 0X87800000,这样我们统一使用 0X87800000这个链接地址,不容易记混。

确定了链接地址以后就可以使用 arm-linux-gnueabihf-ld来将前面编译出来的 led.o文件链接到 0X87800000这个地址,使用如下命令:arm-linux-gnueabihf-ld-Ttext0X87800000led.o-oled.elf

上述命令中-Ttext就是指定链接地址,"-o"选项指定链接生成的 elf文件名,这里我们命名为 led.elf。上述命令执行完以后就会在工程目录下多一个 led.elf文件。

3、用arm-linux-gnueabihf-objcopy格式转换

led.elf文件也不是我们最终烧写到 SD卡中的可执行文件,我们要烧写的.bin文件,因此还需要将 led.elf文件转换为.bin文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy这个工具

arm-linux-gnueabihf-objcopy更像一个格式转换工具,我们需要用它将 led.elf文件转换为led.bin文件,命令如下:arm-linux-gnueabihf-objcopy-Obinary-S-gled.elfled.bin。上述命令中,"-O"选项指定以什么格式输出,后面的"binary"表示以二进制格式输出,选项"-S"表示不要复制源文件中的重定位信息和符号信息,"-g"表示不复制源文件中的调试信息。上述命令执行完成以后,就会在工程目录下多一个 led.bin文件。至此我们终于等到了想要的东西---led.bin文件。

4、使用arm-linux-gnueabihf-objdump反汇编(调试用的)

大多数情况下我们都是用 C语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,当然这个工作目前对于我们不是必须的。一般来说可以将 elf文件反汇编,比如如下命令:arm-linux-gnueabihf-objdump-Dled.elf>led.dis 。上述代码中的"-D"选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis文件。我们可以打开这个文件,查看其中的内容可以发现这里面都是汇编代码。注意通过 led.dis这个反汇编文件可以明显的看出我们的代码已经链接到了以 0X87800000为起始地址的区域。

5.总结命令

总结一下刚才的流程,为了编译一个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

如果我们改动哪怕一点点代码,这个流程都需要重新来一遍,很明显,这里需要创建一个Makefile

高级一点就这样:

6.烧写到SD卡中

bin文件准备就绪后,插入sd卡连接到ubuntu

把imxdownload放入当前文件夹,然后写入bin文件

注意:这里不要把代码写错了,写成sda(sda是系统磁盘,会造成Ubuntu损坏!),记得烧录前,给ubuntu拍快照

代码已经烧写到了 SD卡中了,接下来就是将 SD卡插到开发板的 SD卡槽中,然后设置拨

码开关为 SD卡启动,拨码开关设置如图(在板子上能够清晰的看到SD卡启动模式的拨码开关为10000010)

相关推荐
somi78 小时前
ARM-04-蜂鸣器
arm开发·单片机·嵌入式硬件
EnglishJun9 小时前
ARM嵌入式学习(九)--- C语言应用:点亮led
c语言·arm开发·学习
程序员一点12 小时前
第24章:openEuler 内核与模块管理
arm开发·openeuler
sayang_shao1 天前
ARM架构运行模式学习笔记
arm开发·学习·架构
坤坤藤椒牛肉面1 天前
arm基础IMX6ULL----点亮led
arm开发
Flamingˢ1 天前
基于ARM的裸机程序设计和开发(四):硬件编程原理与GPIO控制思路
arm开发
aseity1 天前
Debian10 ARM KVM 虚拟机安装记录
arm开发
li星野1 天前
RTOS面试完整模拟题(嵌入式系统方向)
arm开发·面试·职场和发展
路溪非溪1 天前
BLE的广播、扫描和连接等工作机制总结
linux·arm开发·驱动开发