这里继续介绍arm裸机的编程,从点亮led灯开始,今天将会分别使用汇编和C语言来实现点亮led灯。里面涉及到的一些arm基础知识可以参考前面的文章arm(1)| 基础知识arm(2)| 汇编指令和伪指令
在点灯之前,要了解IO的复用功能,在这里应该设置为普通的GPIO模式,另外还要设置引脚的属性,例如驱动能力,是否使用上下拉电阻,是否使用保持器,是否使用开漏模式以及使用施密特模式还是CMOS模式等。不过这一部分对于点灯来说,使用默认即可。
然后还要从原理图上找出led对应的管脚,比如本次实验红色led对应的管脚是GPIO1_IO04
一、汇编方式
汇编方式点灯总结为以下几点:
(1) 开启 GPIO 时钟。
CCM_CCGR1(0x20c406c),用来开启时钟,比如要开启gpio1的时钟,就要配置CCM_CCGR1寄存器的bit27~bit26位,具体配置如下图:
(2) 设置引脚的复用功能
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04(0x20e006c),配置引脚为GPIO模式。从图上可以看出,将bit3~bit0配置为0101(二进制),就是gpio模式。
(3) 设置引脚属性。
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04(0x20e02f8),用于配置引脚的属性,例如驱动能力,是否使用上下拉电阻,是否使用保持器,是否使用开漏模式以及使用施密特模式还是CMOS模式等。(暂时使用默认值,所以不配置这一项)
(4) 设置引脚方向
GPIO1_GDIR(0x209c004),0是输入,1是输出。在这里,设置bit4为1。
(5)设置输出电平。
GPIO1_DR(0x209c000),0是低电平,1是高电平。所以可以设置bit4为0。
实例代码:
代码语言:javascript复制.global _start
_start:
@寄存器宏定义
#define CCM_CCGR1 0x20c406c
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 0x20e006c
#define GPIO1_GDIR 0x209c004
#define GPIO1_DR 0x209c000
@开启时钟
ldr r0,=CCM_CCGR1
ldr r1,=0xffffffff
str r1,[r0]
@设置引脚的复用功能
ldr r0,=IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
ldr r1,=0x5
str r1,[r0]
@设置引脚方向
ldr r0,=GPIO1_GDIR
ldr r1,=0x10
str r1,[r0]
@设置输出电平
ldr r0,=GPIO1_DR
ldr r1,=0x0
str r1,[r0]
loop:
b loop
写好代码之后,就要进行编译、链接、格式转化工作,对于iMX.6ull芯片来说,还需要添加头,这里暂时使用现成脚本。
1、编译:
代码语言:javascript复制arm-none-eabi-gcc -g -c led.S -o led.o
2、链接:
代码语言:javascript复制arm-none-eabi-ld -Ttext 0x80000000 led.o -o led.elf
-Ttext 0x80000000 选项,设置程序代码段的起始地址为 0x80000000。0x80000000 是外部内存的起始地址。这个地址是由芯片本身决定的。
3、格式转化:
代码语言:javascript复制arm-none-eabi-objcopy -O binary -S -g led.elf led.bin
上一步链接生成的.elf 文件是带有地址信息的文件,不能放在存储器中执行,要使用格式转换命令转化为二进制文件。
编译成功后会在当前文件夹下生成.bin 文件,这个.bin 文件也不能直接放到开发板上运行,这次是因为需要在.bin 文件缺少启动相关信息。这一部分暂时使用现成的脚本文件来添加,暂时不深究。
最后,bin文件烧写到SD卡当中,然后将SD卡插在开发板上,通过拨码开关设置开发板的启动方式为SD卡启动,上电之后就可以看到led灯被点亮。
二、C语言方式
C语言点亮led灯和汇编差不多,只不过要添加一个启动文件和链接脚本,然后再写一个Makefile方便管理工程。
首先写一个启动文件start.S,这个启动文件用来设置栈指针,以及跳转到C代码的main函数中
代码语言:javascript复制.text
.align 2
.global _start
_start:
ldr sp,=0x80001000
b main
然后写一个led.c文件,内容和汇编差不多,只不过这里是用指针的方式来写寄存器。
代码语言:javascript复制#define CCM_CCGR1 *(volatile unsigned long*)0x20c406c
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
*(volatile unsigned long*)0x20e006c
#define GPIO1_GDIR *(volatile unsigned long*)0x209c004
#define GPIO1_DR *(volatile unsigned long*)0x209c000
void delay(int i);
int main(void)
{
CCM_CCGR1=0xffffffff;
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04=5;
GPIO1_GDIR=(1<<4);
while (1)
{
GPIO1_DR=0;
delay(5000000);
GPIO1_DR=(1<<4);
delay(5000000);
}
return 0;
}
void delay(int i)
{
while (i--)
{
}
}
之后写一个链接脚本led.lds,用来指定链接的地址。链接脚本的作用就是告诉编译器怎么链接这些文件,比如那个文件放在最前面,程序的代码段、数据段、bss段分别放在什么位置等等。注意"."后面有空格,否则会报错。
代码语言:javascript复制ENTRY(_start)
SECTIONS
{
. =0x80000000;
.=ALIGN(4);
.text :
{
start.o (.text)
*(.text)
}
. =ALIGN(4);
.data :
{
*(.data)
}
. =ALIGN(4);
.bss :
{
*(.bss)
}
}
最后写一个Makefile用来管理工程。关于Makefile内容,可以参考前面的文章Linux笔记(9)| 一步步深入Makefile
代码语言:javascript复制all:start.o led.o
arm-none-eabi-ld -Tled.lds $^ -o led.elf
arm-none-eabi-objcopy -O binary -S -g led.elf led.bin
%.o:%.c
arm-none-eabi-gcc -g -c $^ -o led.o
%.o:%.S
arm-none-eabi-gcc -g -c $^ -o start.o
.PHONY:clean
clean:
rm *.o *.elf *.bin
最后将生成的bin文件烧录到SD卡当中,然后将SD卡插在开发板上,通过拨码开关设置开发板的启动方式为SD卡启动,上电之后就可以看到led灯闪烁。