ARM-启动代码及裸机环境搭建
学完ARM内核汇编的循环、函数调用之后,不再只对着模拟器敲代码,今天正式接触外设,在正点原子i.MX6ULL‑Mini开发板上,用纯汇编完成LED点亮实验。裸机开发没有操作系统帮忙,寄存器全部要自己手动配置。
先认识我们的硬件:i.MX6ULL‑Mini开发板
这块板子分为核心板+底板两部分,核心板是六层板,把CPU、DDR内存、eMMC存储都集成好了,通过排针插到底板上面。
CPU:NXP i.MX6ULL,Cortex‑A7内核,主频分工业级528MHz和商业级800MHz;
内存512MB DDR3L,存储8GB eMMC;
底板上面我们这次要用的LED:红色是用户可控LED,蓝色是电源指示灯,不能软件控制;板上串了510Ω限流电阻,防止电流过大烧坏LED灯珠。
这里先声明:Cortex‑A7是ARM给出的内核设计;i.MX6ULL是NXP做的SOC芯片;IMX6ULL‑Mini是正点原子基于这个芯片做出来的成品开发板。
环境搭建:工具链、VSCode、文件传输
裸机开发不是在Windows下直接编译,需要交叉编译工具链,因为我们电脑是x86架构,开发板是ARM架构,普通gcc编译出来的程序跑不到ARM板子上。
1.交叉编译器Linaro安装
把gcc‑linaro‑4.9.4压缩包放到Ubuntu的/usr/local/arm目录,解压之后修改.bashrc环境变量,把编译器bin目录加入PATH,执行reboot重启虚拟机,输入arm‑linux‑gnueabihf‑gcc -v,能输出版本号就代表安装成功。
2.代码编辑器VSCode
安装VSCode,装上Arm Assembly插件,写.S汇编文件的时候可以语法高亮。这里注意后缀是大写S,不是小写s,大写S编译时会做预处理,后面写宏的时候会用到。
新建工程文件夹,比如D:\IMX6ULL\led_asm,创建我们的启动文件start.S。
先明确裸机点亮LED,到底要干哪些事?
没有Linux系统,没有库函数,想要让LED亮,本质就是直接读写芯片的外设寄存器。
看手册主要参考两份文档:底板原理图PDF,还有i.MX6ULL官方参考手册。
点亮LED三步:
-
引脚复用配置:i.MX6ULL的引脚是多功能的,同一个引脚既可以做GPIO,也可以做串口、SPI等其他功能。我们要把引脚配置成普通GPIO输出模式。对应寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03。
-
电气属性配置:配置引脚上下拉、驱动能力、速率等电气参数,操作IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器。
-
GPIO方向设置:把该引脚设置成输出模式,操作GPIOx_GDIR方向寄存器。
配置完成之后,操作GPIO数据寄存器GPIOx_DR,往寄存器写对应电平,就可以控制LED灯亮或者熄灭。
汇编里面访问内存/寄存器,核心就是ldr和str指令。
ldr:从内存(寄存器地址)把数据读到通用寄存器;
str:把通用寄存器的数据写回到内存(外设寄存器地址)。
举个简单示例:
ldr r0, =0x020C4068 ;把寄存器地址加载到r0
ldr r1, =0xFFFFFFFF ;准备要写入的值
str r1, [r0] ;把r1的值写入r0指向的寄存器
这里要区分两种ldr,伪指令ldr r0,=地址,和内存加载指令ldr r1,[r0]
另外启动代码开头要写.global _start,把_start标记为全局入口,链接器才知道程序从哪里开始跑。i.MX6ULL的ROM起始地址是0x80000000,大小512M。
还接触到CPS指令,用来快速开关中断、切换处理器工作模式,比如cpsid i关闭IRQ中断,设置栈指针sp,后面再用cpsie i打开中断。
完整编译、链接、生成bin、烧写SD卡流程
写好汇编start.S之后不能直接丢给开发板,要经过完整的工具链流程:
- 编译器gcc:只汇编不链接,把.S汇编文件编译成目标文件.o
arm‑linux‑gnueabihf‑gcc -c start.S -o start.o -g - 链接器ld:把目标文件链接,指定程序运行的内存地址,生成elf可执行文件
arm‑linux‑gnueabihf‑ld -Ttext 0x87800000 start.o -o start.elf - 格式转换objcopy:elf里面带符号、调试信息,开发板不认,转成纯粹二进制bin文件
arm‑linux‑gnueabihf‑objcopy -O binary -S -g start.elf start.bin - 反汇编objdump:把elf反汇编输出到dis文件,方便我们阅读、调试代码
arm‑linux‑gnueabihf‑objdump -D start.elf > start.dis
得到start.bin之后,不能直接复制粘贴进SD卡,i.MX6ULL有它的头部校验要求,要用imxdownload工具烧写。
SD卡读卡器接入Ubuntu,确认设备名如/dev/sdb;
给imxdownload添加可执行权限,执行./imxdownload start.bin /dev/sdb。
注意:如果烧写速度显示几MB每秒,大概率是失败的,需要拔掉读卡器重启Ubuntu再重试。
烧写完把SD卡取出来,插到开发板TF卡槽,拨码开关设置为SD卡启动模式,上电,就能看到LED的变化。
每次编译输入一大段指令太麻烦,写Makefile,一条make就完成全部编译。
Makefile核心三要素:目标、依赖、执行规则。
我们目标是生成start.bin,依赖源文件start.S,后面写编译链接转换的命令。
还可以写伪目标:
make clean:清理所有生成的中间文件;
make load:直接调用imxdownload把bin烧写到SD卡。
写的时候还可以定义变量,把交叉编译器前缀抽出来,后续换编译器只需要改一处,代码整洁很多。
COMPLITER = arm‑linux‑gnueabihf‑
CC = $(COMPLITER)gcc
LD = $(COMPLITER)ld
OBJCOPY = $(COMPLITER)objcopy
OBJDUMP = $(COMPLITER)objdump
start.bin : start.S
$(CC) -c start.S -o start.o -g
$(LD) -Ttext 0x87800000 start.o -o start.elf
$(OBJCOPY) -O binary -S -g start.elf start.bin
$(OBJDUMP) -D start.elf > start.dis
clean:
rm start.o start.elf start.bin start.dis -f
load:
./imxdownload start.bin /dev/sdb
今天算是真正踏入裸机开发的大门。没有操作系统给你做封装,引脚是什么功能、地址是多少,全部要翻厚厚的芯片手册。
工具链也一堆新概念,交叉编译、elf、bin、Makefile,刚接触很容易记混。多动手跑一遍完整流程,看每一步生成出来什么文件,慢慢就能理顺整个流程。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)