ARM裸机篇(三)——i.MX6ULL第一个裸机程序
linux系列目录:
linux基础篇(一)——GCC和Makefile编译过程
linux基础篇(二)——静态和动态链接
ARM裸机篇(一)——i.MX6ULL介绍
ARM裸机篇(二)——i.MX6ULL启动过程
ARM裸机篇(三)——i.MX6ULL第一个裸机程序
ARM裸机篇(四)——重定位和地址无关码
ARM裸机篇(五)——异常和中断
linux系统移植篇(一)—— linux系统组成
linux系统移植篇(二)—— Uboot使用介绍
linux系统移植篇(三)—— Linux 内核使用介绍
linux系统移植篇(四)—— 根文件系统使用介绍
linux驱动开发篇(一)—— Linux 内核模块介绍
linux驱动开发篇(二)—— 字符设备驱动框架
linux驱动开发篇(三)—— 总线设备驱动模型
linux驱动开发篇(四)—— platform平台设备驱动
文章目录
- 一、汇编基础
- 二、点亮LED灯
一、汇编基础
- 处理器内部数据传输指令
- 存储器访问指令
- 压栈和出栈指令
- 跳转指令
- 算术运算指令
- 逻辑运算指令
二、点亮LED灯
实际开发过程中汇编用的很少,只有在C语言环境没有准备好的情况下,才必须使用汇编,比如初始化 DDR、设置堆栈指针 SP 等。
I.MX6U 内部的 Boot ROM 会读取 DCD 数据中的 DDR 配置参数然后完成 DDR 初始化的,所以我们编写的汇编就可以省了这一部分,只需要设置好栈指针就可以使用C语言了。
编写代码之前,先搞清楚以下几个要点:
- C函数为何要用栈
总的来说,栈的作用就是:保存现场/上下文,传递参数。
保存现场/上下文
现场,相当于案发现场,总有一些现场的情况,要记录下来的,否则被别人破坏掉之后,你就无法恢复现场了。而此处说的现场,就是指CPU运行的时候,用到的一些寄存器,比如R0~R3,LR等等,对于这些寄存器的值,如果你不保存而直接跳转到函数中去执行,那么很可能会被破坏了,因为函数执行需要用到这些寄存器。
因此在函数调用之前,应该将这些寄存器等现场,暂时保持起来,等调用函数执行完毕返回后,再恢复现场,这样CPU就可以正确的继续执行了。传递参数
当函数被调用并且参数大于4个时,(不包括第4个参数)第4个参数后面的参数就保存在栈中。
- 汇编如何调用C函数
- 当参数小于等下4个时,使用寄存器R0~R3来进行参数传递
- 当参数大于4个时,前四个参数按照上面方法传递,剩余参数传送到栈中,入栈的顺序与参数顺序相反,即最后一个参数先入栈
- C程序如何返回结果给汇编程序
- 结果为一个32位的整数时,通过寄存器R0返回
- 结果为一个64位整数时,通过R0和R1返回,依此类推.
- 结果为一个浮点数时,通过浮点运算部件的寄存器f0,d0或s0返回
- 结果为一个复合的浮点数时,通过寄存器f0-fN或者d0~dN返回
- 对于位数更多的结果,通过调用内存来传递
编写汇编部分:
.text //代码段
.align 2 //设置字节对齐
.global _start //定义全局变量_start: //程序的开始b reset //跳转到reset标号处reset:mrc p15, 0, r0, c1, c0, 0 /*读取CP15系统控制寄存器 */bic r0, r0, #(0x1 << 12) /* 清除第12位(I位)禁用 I Cache */bic r0, r0, #(0x1 << 2) /* 清除第 2位(C位)禁用 D Cache */bic r0, r0, #0x2 /* 清除第 1位(A位)禁止严格对齐 */bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支预测 */bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */mcr p15, 0, r0, c1, c0, 0 /* 将修改后的值写回CP15寄存器 */ldr sp, =0x80200000 //栈指针暂时设置到这个位置b main //跳转到main函数执行/*进入死循环*/
loop:b loop
编写C语言部分:
#define CCM_CCGR1 (volatile unsigned long*)0x20C406C //时钟控制寄存器
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 (volatile unsigned long*)0x20E006C//GPIO1_04复用功能选择寄存器
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 (volatile unsigned long*)0x20E02F8 //PAD属性设置寄存器
#define GPIO1_GDIR (volatile unsigned long*)0x0209C004 //GPIO方向设置寄存器(输入或输出)
#define GPIO1_DR (volatile unsigned long*)0x0209C000 //GPIO输出状态寄存器#define uint32_t unsigned int /*简单延时函数*/
void delay()
{static uint32_t delay_time = 0x1FFFF;do{delay_time --;}while(delay_time);delay_time = 0x1FFFF;
}int main()
{*(CCM_CCGR1) = 0xFFFFFFFF; //开启GPIO1的时钟*(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04) = 0x5;//设置PAD复用功能为GPIO*(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04) = 0x1F838;//设置PAD属性*(GPIO1_GDIR) = 0x10;//设置GPIO为输出模式*(GPIO1_DR) = 0x0; //设置输出电平为低电平while(1){*(GPIO1_DR) = 0x0;delay();*(GPIO1_DR) = 1<<4;delay();}return 0;
}
编写链接脚本:
链接脚本主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。
gcc的链接脚本后缀一般为lds,编写led.lds文件:
ENTRY(_start)
SECTIONS {. = 0X87800000;. = ALIGN(4);.text :{start.o (.text)*(.text)}. = ALIGN(4);.rodata : { *(.rodata) }. = ALIGN(4);.data : { *(.data) }. = ALIGN(4);__bss_start = .;.bss : { *(.bss) *(.COMMON) }__bss_end = .;
}
结合代码各部分讲解如下:
第1行,ENTRY(_start) 用于指定程序的入口,ENTRY( )是设置入口地址的命令, “_start”是程序的入口,本章的led程序的入口地址位于start.S的“_start”标号处。
第2行,定义SECTIONS。SECTIONS可以理解为是一块区域,我们在这块区域排布我们的代码, 链接时链接器就会按照这里的指示链接我们的代码。
第3行,“.”运算符代表当前位置。 我们在SECTION的最开始使用“.= 0X87800000”就是将链接起始地址设置为0X87800000。
第5行,设置字节对齐。这里同样用到了“.”运算符,它表示从当前位置开始执行四字节对齐。 假设当前位置为0x80000001,执行该命令后当前地址将会空出三个字节转到0x80000004地址处。
第6行,定义代码段。“.text :”用于定义代码段,固定的语法要求,我们按照要求写即可。 在“{}”中指定那些内容放在代码段。将start.o中的代码放到代码段的最前面。start.S是启动代码应当首先被执行, 所以通常情况下要把它放到代码段的最前面,其他源文件的代码按照系统默认的排放顺序即可, 通配符“*”在这里表示其他剩余所有的.o文件。
第7-10行,设置数据段。同设置代码段类似,首先设置字节对齐,然后定义代码段。在代码段里使用“*”通配符, 将所有源文件中的代码添加到这个代码段中。
第11-13行,设置BSS段。.bss 段的起始地址和结束地址保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者 C 文件里面使用这两个符号。
连接器脚本编写完成后,在链接指令中使用链接脚本替换-Ttext 0X87800000
arm-none-eabi-ld -Tled.lds $^ -o led.elf
编写makefile文件:
我们程序编写完成后需要依次输入编译、链接、格式转换 命令才能最终生成二进制文件。这种编译方式效率低、容易出错。可以使用makefile文件来干这件事。
makefile文件:
all: start.o led.o arm-none-eabi-ld -Tled.lds $^ -o led.elfarm-none-eabi-objcopy -O binary -S -g led.elf led.binarm-none-eabi-objdump -D -m arm led.elf > led.dis%.o : %.Sarm-none-eabi-gcc -g -c $^ -o start.o
%.o : %.carm-none-eabi-gcc -g -c $^ -o led.o.PHONY: clean
clean:rm *.o *.elf *.bin
链接脚本led.lds和makefile编写完成后,就可以直接使用make命令,来一键完成编译和链接了。
然后,只需要把led.bin通过烧录工具制作成镜像文件烧录到SD卡就可以启动了。
ARM裸机篇(三)——i.MX6ULL第一个裸机程序相关推荐
- 安卓篇-我自己的第一个安卓程序(附源码链接)
在上一期的教程中我们已经成功把安卓工作室(以下简称AS)安装到了我们的电脑上啦~那么这一期我们就要来创建属于我们自己的第一个安卓程序,并且运行到模拟器上. 上期内容传送门~~~ 如何安装Android ...
- 快速入门丨篇三:如何进行运动控制器ZPLC程序开发?
此前,正运动技术给大家讲了,运动控制器的"固件升级"以及"ZBasic程序开发",今天我们来学习一下运动控制器ZPLC程序开发. ZPLC是Zmotion运动控 ...
- ARM裸机篇(二)——i.MX6ULL启动过程
linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...
- ARM裸机篇(五)——异常和中断
linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...
- 开发板、原理图和数据手册-1.3.ARM裸机第三部分-朱有鹏-专题视频课程
开发板.原理图和数据手册-1.3.ARM裸机第三部分-7453人已学习 课程介绍 本期课程承上启下,主要目的是让大家把开发板玩起来.很多同学买了开发板接上线,串口都连不上,或者连刷系统 ...
- i.MX6ULL裸机篇(二)NXP官方SDK移植之踩坑 网口驱动
MX6ULL官方SDK移植之踩坑 网口驱动 一.引言 NXP MX6ULL 网口移植其实主要修改底层驱动程序,LWIP协议通用,一般不用修改,这里主要和大家分享在Linux环境下移植修改网口驱动程序. ...
- ARM裸机篇---启动代码分析
ARM裸机篇---启动代码分析 先搞清楚启动代码和Bootloader的区别,启动代码是指CPU复位后到进入C语言的main函数之前需要执行的那段汇编代码. 下面的代码先暂且这样吧,没啥注释的,时间关 ...
- 第三章 裸机_第一个裸机程序(上)
第一章 裸机调试入门知识 1.对于友善之臂Micro2440开发板进行裸机调试时,需要将启动FLASH选项开关拨动到NOR FLASH上.这样做的原因:裸机程序里面会包含很多的初始化,这些初始化一般都 ...
- docker omv 防火墙_我的软路由折腾之旅 篇三:在openmediavault上通过Docker实现OPENWRT旁路由功能...
我的软路由折腾之旅 篇三:在openmediavault上通过Docker实现OPENWRT旁路由功能 2020-06-20 11:28:38 57点赞 415收藏 74评论 你是AMD Yes党?还 ...
- MACE源码解析【ARM卷积篇(一) 】1*N和N*1卷积实现
MACE Mobile AI Compute Engine (MACE) 是一个专为移动端异构计算平台优化的神经网络计算框架,旨在深度神经网络部署在移动端,是一个SoC上的神经网络实现.主要涉及的硬件 ...
最新文章
- 大场景三维点云的语义分割综述
- 当开启了延迟加载的开关,对象是怎么变成代理对象的?
- 【翻译】GitHub Pages Basics 基本使用帮助【一】GitHub Pages 是什么?
- HTML5程序开发范例宝典 完整版 (韩旭等著) 中文pdf扫描版
- mysql的sql优化工具下载_MySQL SQL查询优化工具EverSQL
- python自动化读取和写入文件_基于Python的接口自动化读写excel文件的方法
- win11右键新建没有txt文本文档记事本怎么办
- et99php,加密锁/加密狗ET199(包含网络锁功能)
- 打造有身体的 AI丨记青源Workshop具身智能与机器人研讨会(2022年第8期)
- 如何制作一张3D复古拼贴海报 Part II
- 李清照最经典的10首诗词
- Vue:前端体系与前后端分离
- 13.14.4 文本文件到电子表格。python编程快速上手--让繁琐工作自动化 第2版。【美】阿尔·斯维加特 Al Sweigart 著 王海鹏 译 中国工信出版集团 人民邮电出版社
- Client can't access Jboss server, the port is not accessable.
- html5是什么语言,html5 css3是什么?
- Linux 灾难恢复
- 西门子PLC产生随机数
- 盛世昊通2022全新布局,九大生态板块赋能产业数字化
- 基于 FPGA 的模拟 I²C协议设计(中)
- Vue、VueCLI(脚手架)安装、使用详细步骤
热门文章
- 注册码系统V2的自动发卡功能使用流程
- @Formula的使用
- formula one不兼容win10
- esxi虚拟化是基于linux吗,虚拟化 – 如何在虚拟机中运行VMWare ESX或ESXi?
- 饭后小甜点leetcode——哈希表
- 程序员如何更好的提升自己
- 计算机硬件开关打开无线网络,如何打开电脑无线网络_如何打开无线网络连接...
- 计算机应用基础制作新春贺卡图片,WORD制作新年贺卡和个人简历大学计算机基础课程设计报告书...
- 再见,996!程序员开源考公指南火了:三人已成功上岸!
- 2020家用千兆路由器哪款好_家用千兆路由器哪款好(2020年千兆路由器排行榜)...