嵌入式学习 day47 LED
一、搭建环境
(1)切换到irq模式
mrs r0,cpsr
bic r0,r0,#(0x1f << 0)
bic r0,r0,#(1 << 7)
orr r0,r0,#(0x12 << 0)
msr cpsr,r0
ldr sp,=0x82000000
(2)切换到system模式
mrs r0,cpsr
orr r0,r0,#(0x1f << 0)
msr cpsr,r0
ldr sp,=0x84000000
注:cps #<code>:可切换到相应模式
cpsid i
cps #0x12
ldr sp, =0x82000000
cps #0x1F
ldr sp, =0x84000000
cpsie i
二、步骤
1、查看原理图
2、步骤
(1)打开时钟门:查手册CCM,把CCM_CCGR0-CCM_CCGR6的位全部置1
enable_clock:
ldr r0, =0x020C4068
mov r1, #0xFFFFFFFF
str r1, [r0]
ldr r0, =0x020C406C
str r1, [r0]
ldr r0, =0x020C4070
str r1, [r0]
ldr r0, =0x020C4074
str r1, [r0]
ldr r0, =0x020C4078
str r1, [r0]
ldr r0, =0x020C407C
str r1, [r0]
ldr r0, =0x020C4080
str r1, [r0]
bx lr
(2)设置复用模式
ldr r0, =0x020E0068
mov r1, #0x05
str r1, [r0]
(3)调电器属性
ldr r0, =0x020E02F4
ldr r1, =0x10B0
str r1, [r0]
(4)配置GPIO
ldr r0, =0x0209C004
mov r1, #0
orr r1, r1, #(1 << 3)
str r1, [r0]
三、代码
控制led灯的亮灭
.global _start
_start:
ldr pc, =_reset_handler
ldr pc, =_undefined_handler
ldr pc, =_svc_handler
ldr pc, =_prefetch_handler
ldr pc, =_data_abort_handler
ldr pc, =_not_user_handler
ldr pc, =_irq_handler
ldr pc, =_fiq_handler
_undefined_handler:
ldr pc, =_undefined_handler
_svc_handler:
ldr pc, =_svc_handler
_prefetch_handler:
ldr pc, =_prefetch_handler
_data_abort_handler:
ldr pc, =_data_abort_handler
_not_user_handler:
ldr pc, =_not_user_handler
_irq_handler:
ldr pc, =_irq_handler
_fiq_handler:
ldr pc, =_fiq_handler
_reset_handler:
cpsid i
cps #0x12
ldr sp, =0x82000000
cps #0x1F
ldr sp, =0x84000000
cpsie i
bl enable_clock
bl init_led
b finished
led_on:
ldr r0, =0x0209C000
ldr r1, [r0]
bic r1, r1, #(1 << 3)
str r1, [r0]
bx lr
led_off:
ldr r0, =0x0209C000
ldr r1, [r0]
orr r1, r1, #(1 << 3)
str r1, [r0]
bx lr
asm_delay:
ldr r0, =0x7FFFF
loop:
sub r0, r0, #1
cmp r0, #0
bgt loop
bx lr
init_led:
ldr r0, =0x020E0068
mov r1, #0x05
str r1, [r0]
ldr r0, =0x020E02F4
ldr r1, =0x10B0
str r1, [r0]
ldr r0, =0x0209C004
mov r1, #0
orr r1, r1, #(1 << 3)
str r1, [r0]
bx lr
enable_clock:
ldr r0, =0x020C4068
mov r1, #0xFFFFFFFF
str r1, [r0]
ldr r0, =0x020C406C
str r1, [r0]
ldr r0, =0x020C4070
str r1, [r0]
ldr r0, =0x020C4074
str r1, [r0]
ldr r0, =0x020C4078
str r1, [r0]
ldr r0, =0x020C407C
str r1, [r0]
ldr r0, =0x020C4080
str r1, [r0]
bx lr
finished:
bl led_on
bl asm_delay
bl led_off
bl asm_delay
b finished
四、编译
1、交叉编译环境
与Keil下不同,Keil下是不存在I.MX这个器件的。飞思卡尔现在属于NXP,之前的飞思卡尔被NXP收购了。但是在LMX推出的时候,还没有完成这个收购。我们需要在Ubuntu(PC)下编译出I.MX(ARM)可以运行的程序,这种编译环境称为交叉编译环境。使用的编译工具称为交叉工具链,总结一下
就是:
1)它肯定是一个 GCC 编译器;
2)这个 GCC 编译器是运行在 X86 架构的 PC 上的;
3)这个GCC 编译器是编译ARM架构代码的,也就是编译出来的可执行文件是在ARM芯片上运行的。
2、交叉编译环境的搭建
使用FileZillaClient将交叉工具链压缩文件gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-
gnueabihf.tar.xz上传到Ubuntu的家自录下,并创建自录/usr/local/arm。将压缩文件拷贝到这个新建的文件夹下并解压缩。
sudo mkdir /usr/local/arm
sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/ -f
sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
到目前我们已经按照好了交叉编译工具链的必要文件,,由于我们使用的Ubuntu之前安装过更古老版本的交叉工真链,我们必须先把原来那个屏蔽掉。方法是打开家目录下的.bashrc文件,找到exportPATH=$PATH:/home/linux/tools/opt/FriendlyARM/toolschain/4.4.3/bin并删除这一行。再将我们新安装的工具链启用起来,方法是:
sudo vi /etc/profile
· 在文件末尾添加export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
重启Ubuntu,输入arm-linux-gnueabihf-gcc-V,能看到4.9.4字样,说明新工具链安装成功。
注:ftp工具:
接下来的工作我们经常要把文件从Windows拷贝到Ubuntu去,这里我们使用一个称为FileZilla
Client的工真。它是一个Windows系统下免费的ftp工真,但是需要手动配置Ubuntu的ftp服务。步骤如下:
1) sudo apt-get install vsftpd ;
2)下载完成后用Vl 命令打开/etc/vsftpd.conf;
3)找到local_enable=YES write_enable=YES确保这两行之前没有 #;
4)最后重启ftp服务,sudo /etc/init.d/vsftpd restart 。
首先使用FileZilla_Client填入Ubuntu的IP地址、用户名和密码,端口可不填。之后就可以方便地在Windows和Ubuntu之间进行互传文件了。接下来就可以把之前写号的ledasm.s文件上传到Ubuntu上了,这里强烈建议各位为每个实验创建独立的文件夹,例如在Ubuntu下的家目录中创建一个IMX文件夹,从Windows上传的led.s提前放入一个Led_asm文件夹中,之后将该文件夹整体上传到Ubuntu。
3、编译流程
(1) 使用arm-linux-gnueabihf-gcc编译文件
有了工具链,就可以编译我们led_asm.s文件了,命令为:
arm-linux-gnueabihf-gcc -g -c start.s -o start.o
其中“-g”选项是产生调试信息,GDB 能够使用这些调试信息进行代码调试。“-c”选项是编译源
文件,但是不链接。“-o”选项是指定编译产生的文件名字,这里我们指定 start.s 编译完成以后的文件名字为 start.o。执行上述命令以后就会编译生成一个 start.o 文件。
(2)使用arm-linux-gnueabihf-ld 链接文件
上面的led.o 文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的 C文件和汇编文件都会编译生成一个对应的.o 文件,我们需要将这.o 文件链接起来组合成可执行文件。确定了链接地址以后就可以使用 arm-linux-gnueabihf-ld 来将前面编译出来的 led.o 文件链接到
0X87800000 这个地址,使用如下命令:
arm-linux-gnueabihf-ld -Ttext 0X87800000 start.o -o start.elf
上述命令中-Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名,这里我们命名为 start.elf。上述命令执行完以后就会在工程目录下多一个 start.elf 文件。
①链接:我们使用的Keil是一种IDE,这种集成开发环境其实组合了编译和链接为一体,不需要手动操作。
②存储地址:就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。
③运行地址:就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错
比如:I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将代码从 SD 卡、EMMC 或者NAND 中拷贝到其运行地址(链接地址)处。
注:“存储地址”和“运行地址”可能是一样的,比如STM32 的存储起始地址和运行起始地址都是0X08000000。
(3)用arm-linux-gnueabihf-objcopy 格式转换
led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还需要将 led.elf 文件转换为.bin 文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy 这个工具
了。 arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 start.elf 文件转换为
start.bin 文件,命令如下:
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
上述命令中,“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息。上述命令执行完成以后,就会在工程目录下多一个 start.bin 文件。至此我们终于等到了想要的东西—start.bin 文件。
(4)使用arm-linux-gnueabihf-objdump 反汇编
大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,当然这个工作目前对于我们不是必须的。一般来说可以将 elf 文件反汇编,比如如下命令:
arm-linux-gnueabihf-objdump -D start.elf > start.dis
上述代码中的“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件。我们可以打开这个文件,查看其中的内容可以发现这里面都是汇编代码。注意通过 led.dis 这个反汇编文件可以明显的看出我们的代码已经链接到了以 0X87800000 为起始地址的区域。
注:Makefile:
COMPILER = arm-linux-gnueabihf-
CC = $(COMPILER)gcc
LD = $(COMPILER)ld
OBJCOPY = $(COMPILER)objcopy
OBJDUMP = $(COMPILER)objdump
start.bin:start.S
$(CC) -g -c start.S -o start.o
$(LD) -Ttext 0x87800000 start.o -ostart.elf
$(OBJCOPY) -O binary -S -g start.elf start.bin
$(OBJDUMP) -D start.elf > start.dis
clean:
rm start.elf start.o start.bin start.dis -f
4、烧写程序
(1)原因:我们学习 STM32 等其他的单片机的时候,编译完代码以后可以直接通过 Keil下载到内部的 flash中。但是 I.MX6U 虽然内部有 96K 的 ROM,但是这 96K 的 ROM 是 NXP自己用的,不向用户开放。所以相当于说 I.MX6U 是没有内部 flash 的,但是我们的代码得有地方存放啊,为此,I.MX6U 支持从外置的 NOR Flash、NAND Flash、SD/EMMC、SPI NOR Flash和 QSPI Flash 这些存储介质中启动,所以我们可以将代码烧写到这些存储介质中中。在这些存储介质中,除了 SD 卡以外,其他的一般都是焊接到了板子上的,我们没法直接烧写。但是 SD卡是活动的,是可以从板子上插拔的,我们可以将 SD 卡插到电脑上,在电脑上使用软件将.bin文件烧写到 SD 卡中,然后再插到板子上就可以了。其他的几种存储介质是我们量产的时候用到的,量产的时候代码就不可能放到 SD 卡里面了,毕竟 SD 是活动的,不牢固,而其他的都是焊接到板子上的,很牢固。 因此,我们在调试裸机和 Uboot 的时候是将代码下载到 SD 中。
(2)步骤:
①首先将工具拷贝到工程目录下并添加运行权限。之后准备一张新的 SD(TF)卡,确保 SD 卡里面没有有用的数据,因为我们在烧写代码的时候可能会格式化 SD 卡。
②Ubuntu 下所有的设备文件都在目录“/dev”里面,所以插上 SD 卡以后也会出现在“/dev”里面,其中存储设备都是以“/dev/sd”开头的。我们要先看一下不插 SD 卡的时候电脑都有哪些存储设备,以防插入 SD 卡以后分不清谁是谁。输入如下所示命令:ls /dev/sd* 。例如我这里看到的结果是/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 四个文件,分别代表Ubuntu下的磁盘和三个分区。使用读卡器将 SD 卡插到电脑,一定要确保 SD 卡是挂载到了 Ubuntu 系统中,而不是 Windows下。
③再次使用上述命令,我这里看到的是/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb /dev/sdb1 多出的两个文件就是sd卡及其分区了。
④我们要做的就是把led.bin文件烧写到sd卡里去。使用命令./imxdownload led.bin /dev/sdb 。注意千万不能写成./imxdownload led.bin/dev/sda,sda是系统磁盘,会造成Ubuntu损坏的!
烧写完成以后会在当前工程目录下生成一个 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 卡,一般出现这种情况,重新插拔 SD 卡基本没啥用,只有重启Ubuntu。
5、运行程序
代码已经烧写到了 SD 卡中了,接下来就是将 SD 卡插到开发板的 SD 卡槽中,然后设置拨
码开关为 SD 卡启动,拨码开关设置如图。之后打开电源,可以看到用户Led已经被点亮。