通过上一篇基础篇2.1基本了解使用汇编程序编写点亮一个LED等的思想和步骤,接下来具体实现步骤。
硬件原理分析
LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03 输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发 光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03 的输出电平,输出 0 就亮,输出 1 就灭。
MAX6ULL IO口初始化流程:
参考手册章节30
1.使能时钟,CCGR0-CCGR6这七个寄存器控制这6ULL所有外设时钟的使能,为了简单化,设置CCGR0-CCGR6这7个寄存器全部为0XFFFFFFFF ,相当于使能所有外设时钟
2.IO复用,将寄存器的IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的bit3-0设置为0101=5,此时GPIO_IO03配置为复用功能
3.将寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03是设置GPIO_IO03的电气属性
包括压摆率,速度,驱动能力,开漏,上下拉等。
4.配置GPIO功能,设置输入输出,设置GPIO_GDIR寄存器bit3为1,即设置为输出模式
5.设置GPIO1_DR寄存器的bit3,为1表示输出高电平,为0表示输出低电平。
创建VS Code工程
vscode 文件夹里面存放 VSCode 的工程文件,新建汇编文件led.s , led.s 这个文件中编写汇编程序。使用 VSCode 打开 1_leds 这个文件夹
VS Code内编写代码实现:
.global _start @全局标号
_start:
/*使能所有外设时钟 */
ldr r0,=0x020c4068 @CCGR0地址
ldr r1,=0xffffffff @要向CCGR0写入的数据
str r1, [r0] @将0xffffffff写入到CCGR0中
ldr r0,=0x020c406C @CCGR1地址
str r1, [r0] @将0xffffffff写入到CCGR1中
ldr r0,=0x020c4070 @CCGR2地址
str r1, [r0] @将0xffffffff写入到CCGR2中
ldr r0,=0x020c4074 @CCGR3地址
str r1, [r0] @将0xffffffff写入到CCGR3中
ldr r0,=0x020c4078 @CCGR4地址
str r1, [r0] @将0xffffffff写入到CCGR4中
ldr r0,=0x020c407c @CCGR5地址
str r1, [r0] @将0xffffffff写入到CCGR5中
ldr r0,=0x020c4080 @CCGR6地址
str r1, [r0] @将0xffffffff写入到CCGR6中
/*配置GPIO_IO03 PIN的复用为GPIO,即
*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03=5
*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器地址为0X020E0068
*/
ldr r0,=0x020e0068 @IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
ldr r1,=0x5 @要写入的数据
str r1, [r0] @将5写入到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中
/*配置GPIO_IO03 的电气属性 即寄存器
*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址为0x020e02f4
*bit0: 0 低速率
*bit 5:3 : 110 R0/6驱动能力
*bit 7:6 : 10 100Mhz速度
*bit11: 0 关闭开路输出
*bit12: 1 使能pull/kepper
*bit13: 0 kepper
*bit15:14: 00 100K下拉
*bit16: 0 关闭hys
计算得出十六进制为10B0*/
ldr r0,=0x020e02f4 @IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
ldr r1,=0x10b0 @要写入的数据
str r1, [r0] @将5写入到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中
/* 设置GPIO
设置GPIO1_GDIR寄存器,设置GPIO1_GPIO03为输出
设置GPIO1_GDIR寄存器0x0209c004,设置GPIO1_GDIR寄存器bit3为1
即设置GPIO1_IO03为输出
*/
ldr r0,=0x0209c004 @设置GPIO1_GDIR寄存器
ldr r1,=0x8 @要写入的数据
str r1, [r0]
/*打开LED,即设置GPIO1_IO03为0
GPIO1_DR寄存器地址为0x0209c000
*/
ldr r0,=0x0209c000 @设置GPIO1_GDIR寄存器
ldr r1,=0 @要写入的数据
str r1, [r0]
loop:
b loop
程序编译步骤:
1.arm-linux-gnueabihf-gcc 编译文件,将.c.s文件变为.o --
终端输入例:arm-linux-gnueabihf-gcc -g -c leds.s -o led.o
2.arm-linux-gnueabihf-ld 链接文件,将所有的.o文件连接为elf格式的可执行文件
终端输入例:arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
注意:led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还 需要将 led.elf 文件转换为.bin 文件
3.arm-linux-gnueabihf-objcopy 格式转换,将elf文件转为bin文件
终端输入例:arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
4.arm-linux-gnueabihf-objdump 反汇编,将elf文件转为汇编,反汇编
终端输入例:arm-linux-gnueabihf-objdump -D led.elf > led.dis
逐行输入指令较麻烦,建议直接创建Makefile运行:
led.bin : leds.s
arm-linux-gnueabihf-gcc -g -c leds.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
clean:
rm -rf*.o led.bin led.elf led.dis
链接的解释:
链接就是将所有.o文件链接在一起,并且链接到指定的地方。 本实验链接时要指定链接起始地址。链接起始地址就是代码运行的起始地址。
对于6ULL来说,链接起始地址应该指向RAM地址,RAM分为内部RAM和外部RAM也就是DDR,6ULL内部的RAM地址范围0X900000~0X91FFFF。也可以放在外部DDR中,对于I.MAX-ALPHA开发板,512MB字节DDR版本的核心板,DDR范围就是0X80000000-0X9FFFFFFF。对于256MB的DDR核心板,范围为0X80000000-0X8FFFFFFF
裸机代码链接起始地址为0X87800000(便于后续uboot链接地址)。要使用DDR,必须要初始化DDR,bin文件不能直接烧写到SD卡、EMMC、NAND等外置储存中,然后从外置储存中启动运行。而不是bin文件不能直接运行,使用JLINK将bin文件直接下载到内部RAM中还是可以运行。
i.max系列SOC内部boot rom会从SD卡、EMMC等外置存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到指定的地方。
Bin的运行地址一定要和链接起始地址一致,位置无关代码除外。
烧写bin文件步骤:
STM32 烧写到内部FLASH, 6ULL支持SD卡、EMMC、NAND、nor、SPI flash等启动,裸机例程选择烧写到SD卡里面。
在Ubuntu下向SD卡烧写裸机bin文件。烧写不是将bin文件拷贝到SD卡中,而是将bin文件烧写到SD卡的绝对地址上。对于I.MAX而言,不能直接烧写bin文件,比如先在bin文件前面添加头部,完成改工作,需要正点原子提供imxdownload软件
1.将 imxdownload 拷贝到工程根目录下
2.给予 imxdownload 可执行权限
直接将软件 imxdownload 从 Windows 下复制到 Ubuntu 中以后,imxdownload 默认是 没有可执行权限的,给予imxdownload可执行权限:chmod 777 imxdownload
3.确定SD卡:
Ubuntu 下所有的设备文件都在目录“/dev”里面,所以插上 SD 卡以后也会出现在“/dev” 里面,其中存储设备都是以“/dev/sd”开头的。将SD卡接入读卡器再接入Ubuntu系统,输入命令,通过插拔对比多出的设备即为SD卡外设。
因此SD卡设备名称为/dev/sdb
4.向 SD 卡烧写 bin 文件
烧写命令:./imxdownload led.bin /dev/sdb // <led.bin为烧写的bin文件;/dev/sdb为烧写的SD卡 >
该操作后:Imxdownload会向led.bin添加一个头部,生成新的load.imx文件,这个load.imx文件就是最终烧写到SD卡里面。
代码验证:
烧写完成,准备就绪后,从读卡器取出SD卡,插入开发板中,并将拨码开关设置为SD卡启动模式。
LED0灯成功被点亮,代码运行正常!!最难点灯,跨出一大步~
目前遇到的最难点灯步骤,那么能否通过JLINK直接烧写程序?
答:不可以,6ULL支持JTAG,因为没有烧写算法,所以无法烧写,但可以通过JTAG将bin文件下载到内部ram,6ULL的JTAG口与SAI复用,SAI连接了WM8960音频DAC。在嵌入式Linux开发中基本不用JLNK