此节学习视频:https://www.bilibili.com/video/BV1yE411h7uQ?p=6&vd_source=432ba293ecfc949a4174ab91ccc526d6 在STM32上,.s就是汇编,cortex A一般不会提供汇编,必须自己写汇编文件,对于A系列,初始化ram,没办法用c初始化,必须先初始化一些外设,这些外设必须用汇编初始化,但是用到的汇编不多,一开始初始化一些重要的外设,紧接着初始化DDR,DDR初始化完成之后,SP设置好就可以运行C语言了。 Cortex A汇编作用: 1、初始化一些SOC外设 2、初始化DDR,此处DDR不需要汇编会初始化,IMX内部的96k ROM存放了自己编写的启动代码,这些启动代码可以读取DDR配置信息,并且完成DDR的初始化 3、设置SP指针,一般指向DDR,cortex A系列一般不会指向内部ram,ram比较小,所以指向DDR,sp指针设置好C语言运行环境 i.MX6ULL芯片的GPIO被分成5组,并且每组GPIO的数量不尽相同,GPIO的控制分为两部分控制,分别如下: IOMUX由其左侧的IOMUXC控制(C表示Controler),IOMUXC提供寄存器给用户进行配置, 它又分成MUX Mode(IO模式控制)以及Pad Settings(Pad配置)两个部分:
- MUX Mode配置
- Pad Settings配置
IOMUXC控制类型 | 寄存器名称 |
MUX Mode | IOMUXC_SW_MUX_CTL_PAD_XXXX |
Pad Settings | IOMUXC_SW_PAD_CTL_PAD_XXXX |
原理图分析:
从上图可以看出,LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。 总结LED初始化流程大致可分为以下三步:- 开启GPIO时钟。
- 设置引脚的复用功能以及引脚属性。
- 设置引脚方向以及输出电平。
.global _start /* 全局标号 */ /* * 描述: _start函数,程序从此函数开始执行此函数完成时钟使能、 * GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。 */ _start: /* 例程代码 */ /* 1、使能所有时钟 */ ldr r0, =0X020C4068 /* CCGR0 */ ldr r1, =0XFFFFFFFF str r1, [r0] ldr r0, =0X020C406C /* CCGR1 */ str r1, [r0] ldr r0, =0X020C4070 /* CCGR2 */ str r1, [r0] ldr r0, =0X020C4074 /* CCGR3 */ str r1, [r0] ldr r0, =0X020C4078 /* CCGR4 */ str r1, [r0] ldr r0, =0X020C407C /* CCGR5 */ str r1, [r0] ldr r0, =0X020C4080 /* CCGR6 */ str r1, [r0] /* 2、设置GPIO1_IO03复用为GPIO1_IO03 */ ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */ ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */ str r1,[r0] /* 3、配置GPIO1_IO03的IO属性 *bit 16:0 HYS关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper功能 *bit [12]: 1 pull/keeper使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度100Mhz *bit [5:3]: 110 R0/6驱动能力 *bit [0]: 0 低转换率 */ ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */ ldr r1, =0X10B0 str r1,[r0] /* 4、设置GPIO1_IO03为输出 */ ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */ ldr r1, =0X0000008 str r1,[r0] /* 5、打开LED0 * 设置GPIO1_IO03输出低电平 */ ldr r0, =0X0209C000 /*寄存器GPIO1_DR */ ldr r1, =0 str r1,[r0] /* * 描述: loop死循环 */ loop: b loop
代码编译:
- 使用arm-linux-gnueabihf-gcc将.c .s文件变成.o文件
- 将所有的.o文件链接为elf格式的可执行文件,链接就是将所有的.o文件链接在一起,并且链接到指定的地方
- 将elf文件转成bin文件便于烧录
- 将elf文件反汇编,进行查看学习
arm-linux-gnueabihf-gcc -g -c led.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链接的时候要指定链接起始地址 ,链接起始地址就是代码运行的起始地址 对于imx6ull来说,链接起始地址应该指向RAM地址。RAM分为内部RAM和外部RAM,也就是DDR,imx6ull内部RAM地址为0x900000-0x91ffff,也可以放在外部DDR中,对于alpha512字节开发板而言,DDR范围就是0x80000000-0x9fffffff。要使用DDR,那么必须初始化DDR,对于IMX来说bin文件不能直接运行,需要添加一个头部,这个头部信息包含了DDR的初始化参数,IMX系列SOC内部boot rom会从SD卡,EMMC等外置存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到链接地址处。
烧写bin文件:
imx6ull支持SD卡,EMMC、NAND、nor、spi flash等等启动。裸机例程选择烧写到sd卡中,ddr起始地址为0x87800000。 在ubuntu下向SD卡烧写裸机bin文件。烧写不是将bin文件拷贝到SD卡中,而是将bin文件烧写到SD卡绝对路径,需要借助imxdownload工具。imxdownload会向led.bin添加一个头部,这个头部信息会包含DDR的初始化参数,IMX系列SOC内部bootrom会从SD卡,EMMC等外部设备中读取头部信息,然后初始化DDR,并将bin文件拷贝到指定的地方。生成新的load.imx文件,这个load.imx文件就是最终烧写到SD卡里面去的。 Bin的运行地址一定要和链接起始地址一致。反编译文件解析:
反汇编文件(.dis文件)的理解-CSDN博客led.elf: file format elf32-littlearm // 表明这是由led.elf文件反汇编得到的dis文件 Disassembly of section .text: // 说明反汇编文件是text格式 87800000 <_start>: 87800000: e59f0068 ldr r0, [pc, #104] ; 87800070 <loop+0x4> // 指令地址 指令机器码 指令机器码反汇编到的指令标签:汇编,GPIO1,LED,r1,初始化,DDR,bringup,ldr,r0 From: https://www.cnblogs.com/lethe1203/p/18077689