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.xz3. 将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硬件控制LED1. 查看原理图确定硬件连接关系目标 找到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位设置为0x10b0GPIO1_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.global _start ;上面一句可代替下面内容 area reset, code, readonly code32 entry.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指的是IRQf指FIQcps这个指令可以直接切换arm工作模式这个指令相当于在执行下面的内容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 表示控制域包含模式位、中断使能位等3. 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.elfled.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文件注意这里不要把代码写错了写成sdasda是系统磁盘会造成Ubuntu损坏记得烧录前给ubuntu拍快照代码已经烧写到了 SD卡中了接下来就是将 SD卡插到开发板的 SD卡槽中然后设置拨码开关为 SD卡启动拨码开关设置如图在板子上能够清晰的看到SD卡启动模式的拨码开关为10000010