赞
踩
上一篇介绍了芯片的基本资源,本篇就开始学习怎么编程去使用芯片了(裸机,非系统)。不过鉴于该芯片基本都使用linux开发,裸机仅介绍流程。
工具使用和开发方式可参考:IMX.6ULL_Linux_基础篇(5) linux开发工具与开发模式介绍_rm all的博客-CSDN博客
在安装各种服务之前可以先update一下,能更好的支持安装过程。
开启 Ubuntu 的 SSH 服务以后我们就可以在 Windwos 下使用终端软件登陆到 Ubuntu,比如使用 SecureCRT, Ubuntu 下使用如下命令开启 SSH 服务:
sudo apt-get install openssh-server
在开发的过程中需要频繁的在 Windows 和 Ubuntu 进行文件传输,比如需要将在 Win下的源码、工具传输的ubuntu,然后将Ubuntu中编译好的镜像,开发完成的源码打包传输至Win。 Windows 和 Ubuntu 下的文件互传可以使用FTP/SFTP等,设置方法如下:
- sudo apt-get install vsftpd
- sudo vi /etc/vsftpd.conf
-
- 将配置项中如下两项改为YES
- local_enable=YES
- write_enable=YES
-
- sudo /etc/init.d/vsftpd restart
SFTP服务不需要安装或设置,SSH启用的同时SFTP即可使用。
在win下安装FTP客户端,或者WinSCP等工具(工具使用自行搜索学习)
ubuntu 20.04 安装配置Samba服务_路漫漫其远,吾求索的博客-CSDN博客_ubuntu20.04安装samba
在win下安装SecureCRT 或 MobaXterm等等。
在X86的电脑上编译ARM架构的代码就需要ARM的编译器,同时这个工具还要运行在X86架构上,因此该工具取名交叉编译链。一般使用相应SOC的SDK开发包时,开发包自带编译工具。此处原子提供了编译器,我们直接使用原子提供的工具。
将交叉编译工具传输至服务器:
在 Ubuntu 中创建目录: /usr/local/arm,命令如下:
sudo mkdir /usr/local/arm
使用如下命令将交叉编译器复制到/usr/local/arm 中::
sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/ -f
解压:
sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
修改环境变量:
sudo vi /etc/profile
打开/etc/profile 以后,在最后面输入如下所示内容:
export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
重启。
以上流程是原子的教程,对于初学者来说,或者对于学习来说这样毫无问题,但是在工作时,通常需要使用多版本多架构编译器,这样做会导致编译器冲突,不能准确为源码绑定编译器。所以我们通常不会把他export到环境中去,而是将编译器与源码放置在同一路径下,通过给源码添加编译脚本或者Makefile去定位工具链,最终以绝对路径去调用编译器。详细方法和源码在uboot分析篇移植时介绍。
最终效果:
1.通过终端(CRT)的ssh服务链接服务器(ubuntu18.04)。此时服务器可以最小化,完全不操作,当作服务器使用,所有操作使用命令行在CRT完成。
2.通过FTP 客户端实现win <---->linux传文件
3.使能samba服务后,在win的磁盘管理下可以添加网络驱动器,即将Linux的存储区映射到win下,可以通过和win下相同的操作去浏览相关文件。(不建议互传文件使用)
4.使用vscode或其他编辑器,新建工程,源码路径则选择通过samba映射过来的网络磁盘,这样工程就存在于win下,源码在linux下,此时进行编辑,通过CRT命令行进行编译。
IMX.6ULL是单核32位Cortex-A7架构。可以简读以下相关架构资料。
架构:
《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》
《Cortex-A7 Technical ReferenceManua.pdf》
《ARM Cortex-A(armV7)编程手册 V4.0.pdf》GIC中断控制器:
《ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf》
《ARM Generic Interrupt Controller(ARM GIC控制器)V3.0与V4.0.pdf》
截至目前,作者在开发中要接触多种架构的soc,除了最常用的arm还包括mips,ppc,x86。每一种不同的架构都有自己的指令集和特性。说起汇编,大家可能都学过51单片机的汇编编程设计,但是不同的架构除了汇编思想可以参考,具体指令要针对具体平台使用,编译器不同也会影响,不可混用。
使用汇编写代码的目的:现代的cpu在上电后并不能直接开始运行C语言环境(C语言运行的时候涉及函数跳转,现场保存等,这段内存由SP指针控制访问,但是上电后SP指针拔剑四顾心茫然,它不知道指向哪,所以C语言也就无法运行,对于A系列的控制器,大多数需要外部DRAM例如DDR作为内存运行区域,所以要初始化栈指针指向ram,在这以前一般还要初始化DDR控制器)那这一段时间cpu怎么运行呢?没错,使用汇编直接操作cpu。通常情况下,不论是做mcu单片机还是跑linux,在整个工程建立之初,已经有人帮我们写好这一段用于启动的代码,但是当我们想自己从头做起时,汇编启动是第一步要编写的代码,这也是为什么要了解ARM架构和ARM汇编指令的原因。
Cortex-A7处理器运行模式:
除了User,其他都是特权模式。
ARM提供了16个32位通用寄存器供软件使用,前 15 个(R0~R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。 ARM 还提供了一个当前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR, SPSR 寄存器就是 CPSR 寄存器的备份。这 18 个寄存器如图所示:
对应9种模式的寄存器组:
*灰色标注寄存器多模式共用
CPSR寄存器:
所有的处理器模式都共用一个 CPSR 物理寄存器,因此 CPSR 可以在任何模式下被访问。CPSR 是当前程序状态寄存器,该寄存器包含了条件标志位、中断禁止位、当前处理器模式标志等一些状态位以及一些控制位。所有的处理器模式都共用一个 CPSR 必然会导致冲突,为此,除了 User 和 Sys 这两个模式以外,其他 7 个模式每个都配备了一个专用的物理状态寄存器,叫做 SPSR(备份程序状态寄存器),当特定的异常中断发生时, SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 SPSR 中保存的值来恢复 CPSR。因为 User 和 Sys 这两个模式不是异常模式,所以并没有配备 SPSR,因此不能在 User 和Sys 模式下访问 SPSR,会导致不可预知的结果。由于 SPSR 是 CPSR 的备份,因此 SPSR 和CPSR 的寄存器结构相同。
N(bit31):当两个补码表示的有符号整数运算的时候, N=1 表示运算对的结果为负数, N=0表示结果为正数。
Z(bit30): Z=1 表示运算结果为零, Z=0 表示运算结果不为零,对于 CMP 指令, Z=1 表示进行比较的两个数大小相等。
C(bit29):在加法指令中,当结果产生了进位,则 C=1,表示无符号数运算发生上溢,其它情况下 C=0。在减法指令中,当运算中发生借位,则 C=0,表示无符号数运算发生下溢,其它情况下 C=1。对于包含移位操作的非加/减法运算指令, C 中包含最后一次溢出的位的数值,对于其它非加/减运算指令, C 位的值通常不受影响。
V(bit28): 对于加/减法运算指令,当操作数和运算结果表示为二进制的补码表示的带符号数时, V=1 表示符号位溢出,通常其他位不影响 V 位。
Q(bit27): 仅 ARM v5TE_J 架构支持,表示饱和状态, Q=1 表示累积饱和, Q=0 表示累积不饱和。
IT[1:0](bit26:25): 和 IT[7:2](bit15:bit10)一起组成 IT[7:0],作为 IF-THEN 指令执行状态。
J(bit24): 仅 ARM_v5TE-J 架构支持, J=1 表示处于 Jazelle 状态,此位通常和 T(bit5)位一起表示当前所使用的指令集
J T 描述 0 0 ARM 0 1 Thumb 1 1 ThumbEE 1 0 Jazelle GE[3:0](bit19:16): SIMD 指令有效,大于或等于。
IT[7:2](bit15:10): 参考 IT[1:0]。
E(bit9): 大小端控制位, E=1 表示大端模式, E=0 表示小端模式。
A(bit8): 禁止异步中断位, A=1 表示禁止异步中断。
I(bit7): I=1 禁止 IRQ, I=0 使能 IRQ。
F(bit6): F=1 禁止 FIQ, F=0 使能 FIQ。
T(bit5): 控制指令执行状态,表明本指令是 ARM 指令还是 Thumb 指令,通常和 J(bit24)一起表明指令类型,参考 J(bit24)位。
M[4:0]: 处理器模式控制位
M[4:0] 处理器模式 10000 User 模式 10001 FIQ 模式 10010 IRQ 模式 10011 Supervisor(SVC)模式 10110 Monitor(MON)模式 10111 Abort(ABT)模式 11010 Hyp(HYP)模式 11011 Undef(UND)模式 11111 System(SYS)模式
MOV指令:
MOV R0, R1 @数据传输,即赋值R0=R1
MOV R0, #0X55 @常量赋值,R0=0X12
MRS指令:
MRS R0, CPSR @将CPSR中的数据传递给R0,即R0=CPSR
注:MRS是用来读取特殊寄存器(例如:CPSR/SPSR)
MSR指令:
MSR CPSR, R0 @将R0中的数据传递给CPSR,即CPSR=R0
注:MSR是用来写入特殊寄存器(例如:CPSR/SPSR)
LDR指令:
LDR R0, =0X80000000 @将地址0x80000000赋值给R0,即R0=0X80000000
LDR R1, [R0] @将R0中的数据,即地址0x80000000偏移#0的数据读出并赋值给R1
注:LDR语法:LDR Rd, [Rn, #offect] 从存储器 Rn+offset 的位置读取数据存放到 Rd 中
STR指令:
LDR R0, =0X80000000 @将地址0x80000000赋值给R0,即R0=0X80000000
LDR R1, =0X80000004 @将地址0x80000004赋值给R1,即R0=0X80000004
STR R1, [R0] @将R1中的数据写入到R0保存的地址中
注:STR语法:STR Rd, [Rn, #offset] 将 Rd 中的数据写入到存储器中的 Rn+offset 位置
PUSH/POP指令:
PUSH {R0~R5} @将R0~R5寄存器压入栈中保存
POP {R0~R5} @恢复R0~R5寄存器,出栈
跳转指令:
B <label> @跳转到lable,如果跳转范围查出2K,可以使用B.W
BX <Rm> @Rm存放地址,跳转到该地址处,并切换指令集
BL <label> @跳转至lable,并将返回地址保存在LR中
BLX <Rm> @跳转到Rm保存的地址处,并将返回地址保存在LR中,并切换指令集
算数运行指令:
逻辑运算指令:
涉及芯片外设相关信息需要参考如下手册,可以在原子提供的资料包中找到。参考资料:
《IMX6ULL参考手册.pdf》
《IMX6ULL数据手册(工业级).pdf》
GPIO的基础知识可以参考STM32,建议先学习STM32了解基础外设的原理、控制方式和工作方式。
对于芯片来说GPIO不等于一个Pin,应该说GPIO可能是一个Pin的功能。在芯片设计过程中,要保证在有限的封装大小支持更多的功能,除了在封装技术上下功夫之外,Pin复用是非常有用的设计,这样就可以做到一个Pin多个功能。所以如果我们想使用一个GPIO,则要关注对应的Pin是否支持GPIO,并且使用时要配置该Pin是GPIO功能。GPIO的中文是通用输入输出,所以GPIO在物理上的表现为Pin的电压为低电平(一般为0v)或高电平(一般为芯片供电电压)。
我们先看一下6ULL的GPIO是如何定义的:
通过查看手册目录,我们可以发现第28章是GPIO控制器的描述,第32章是IO复用功能控制器的描述。
看28章可以发现,该章节仅仅是对GPIO的控制所定义的,并没有引脚定义:
同样的,查看32章的细节,发现用户可以使用的Pin相关寄存器都在此处有定义:
但是我们仔细看的时候会发现同样后缀名的寄存器有两个??? 比如:
两个寄存器名仅仅是中间有区别,一个是MUX,一个是PAD,那有什么区别呢,我们可以在上图中看到MUX的寄存器是怎么定义的。可以看出来,寄存器配置了复用模式,当选择ALT5模式时,该Pin是GPIO5_IO01。那我们再看看PAD寄存器是干什么的:
如果对GPIO特性熟悉的同学,仅看上图这一部分寄存器定义即可确定该功能,图中我们标记了3处,分别是开漏输出功能、IO速度、IO驱动能力。所以该寄存器是IO的电气属性配置寄存器。
根据以上分析,想使用一个IO的流程:IOMUXC_xxx_SW_MUX_CTL_PAD_xxx_xxx配置复用功能--->IOMUXC_xxx_SW_PAD_CTL_PAD_xxx_xxx配置引脚属性--->使用GPIO控制器定义的寄存器控制电平和读取电平
流程:
1.汇编初始化部分必要的外设
2.DDR初始化(由后面的学习可知,DDR的初始化参数在镜像的头部所包含,bootrom代码会进行信息的读取和DDR初始化,所以裸机代码不需要做这一步)
3.设置栈指针,指向RAM,一般为外部DDR(设置好C语言运行环境)
原子开发板的LED灯链接的IO是GPIO1_IO03,接下来我们分析该IO的配置和使用:
1.使能时钟,包含GPIO控制器的时钟。在IMX6ULL的时钟章节有CCGR0-CCGR6共7个时钟控制寄存器,将寄存器均置1可以打开所有时钟,但事实上该寄存器复位后默认为1,其实不设置也是可以的。
2.设置IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器的复用功能,复用为GPIO模式(ALT5)
3.设置IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器的电气属性:
SRE[0] :指电平跳变的速度,速度越快,波形越陡峭,越接近方波。该设置应该是当IO作为高速接口的时候优化输出波形的(猜测)。
DSE[5:3] :驱动能力,数值越大,电阻除以的分母越大,电阻变小,电流变大,驱动能力增强,在3.3V的时候,最大驱动电流=3.3/(260/7)=88mA
SPEED[7:6] :输出速度,单纯IO翻转应该是达不到200M的,最大速度应该也是为某些外设准备的。后续可以测试一下!
ODE[11]:开漏输出功能。
PKE[12]:使能或禁止上下拉保持器功能
PUE[13] :设置IO输入时使用上下拉还是保持器,保持器在外部电路断电后仍然保持以往的状态。
PUS[15:14]:设置上下拉阻值大小
HYS[16]:迟滞比较器,一般数字信号会经过施密特触发器来整形波形,但是模拟信号输入的时候不能整形,猜测是用于控制数字输入和模拟输入的(例如ADC)
4.配置GPIO控制寄存器:
前面我们提到过在28章是GPIO控制器章节,经过以上的步骤,此时这个Pin已经是一个GPIO了,接下来就要使用GPIO控制器去控制这个GPIO。前面GPIO介绍的时候已经看到了GPIO寄存器的组成:
数据寄存器:DR 该寄存器每一位控制一个IO,写1/0控制端口输出高/低
方向寄存器:GDIR 该寄存器每一位控制一个IO,为0时输入,为1时输出
状态寄存器:PSR 该寄存器每一位负责一个IO,在IO为输入时,读取输入状态。
中断配置寄存器:ICR1、ICR2 每两位控制一个IO。0:低电平触发;1:高电平触发;2:上升沿触发;3:下降沿触发
中断掩码寄存器:IMR 该寄存器每一位控制一个IO。0:关闭中断;1:打开中断
中断状态寄存器:ISR 该寄存器每一位负责一个IO。触发中断后该位置1,可以通过往该位写1来清楚中断标志位
边沿选择寄存器:EDGE_SEL 该寄存器每一位控制一个IO,该寄存器为ICR1、ICR2的补充,如果想用双边沿触发,那么使用该寄存器。
同时我们能看到寄存器映射表不知一组GPIO控制寄存器,加上我没截图在内的,一共有5组GPIO对应寄存器。
在本实验中:设置GPIO1_GDIR寄存器IO03为输出,设置DR寄存器控制电平高低
- .global _start /* 全局标号 */
-
- /*
- * 描述: _start函数,程序从此函数开始执行此函数完成时钟使能、
- * GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。
- * 汇编默认入口标号为_start,也可以在链接脚本中使用ENTRY来指定。
- */
- _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
Makefile:
- led.bin:led.s
- 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
- clean:
- rm -rf *.o led.bin led.elf led.dis
1.将.c/.s文件编译成.o arm-xxx-gcc -g(产生调试信息,用于GDB调试)-c(编译不链接)-o(指定产生文件名)
2.将所有的.o链接为elf文件 arm-xxx-ld 链接地址指向RAM中,即外部DDR
3.将elf文件转为bin文件 arm-xxx-obicopy 将elf文件中一些头部和编译有关的信息去除,得到bin文件
4.可选,反汇编
编译完成,我们将得到bin文件!
流程分析:
设置处理器模式->SVC(使用程序状态寄存器CPSR设置)
设置SP指针(指向DDR,Cortex-A7栈为向下增长)
跳转至C
start.s汇编:
- .global _start /* 全局标号 */
-
- /*
- * 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
- * 运行环境。
- */
- _start:
-
- /* 进入SVC模式 */
- mrs r0, cpsr
- bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
- orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
- msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
-
- ldr sp, =0X80200000 /* 设置栈指针 */
- b main /* 跳转到main函数 */
C main:
- #include "main.h"
-
- /*
- * @description : 使能I.MX6U所有外设时钟
- * @param : 无
- * @return : 无
- */
- void clk_enable(void)
- {
- CCM_CCGR0 = 0xffffffff;
- CCM_CCGR1 = 0xffffffff;
- CCM_CCGR2 = 0xffffffff;
- CCM_CCGR3 = 0xffffffff;
- CCM_CCGR4 = 0xffffffff;
- CCM_CCGR5 = 0xffffffff;
- CCM_CCGR6 = 0xffffffff;
- }
-
- /*
- * @description : 初始化LED对应的GPIO
- * @param : 无
- * @return : 无
- */
- void led_init(void)
- {
- /* 1、初始化IO复用 */
- SW_MUX_GPIO1_IO03 = 0x5; /* 复用为GPIO1_IO03 */
-
- /* 2、、配置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 低转换率
- */
- SW_PAD_GPIO1_IO03 = 0X10B0;
-
- /* 3、初始化GPIO */
- GPIO1_GDIR = 0X0000008; /* GPIO1_IO03设置为输出 */
-
- /* 4、设置GPIO1_IO03输出低电平,打开LED0 */
- GPIO1_DR = 0X0;
- }
-
- /*
- * @description : 打开LED灯
- * @param : 无
- * @return : 无
- */
- void led_on(void)
- {
- /*
- * 将GPIO1_DR的bit3清零
- */
- GPIO1_DR &= ~(1<<3);
- }
-
- /*
- * @description : 关闭LED灯
- * @param : 无
- * @return : 无
- */
- void led_off(void)
- {
- /*
- * 将GPIO1_DR的bit3置1
- */
- GPIO1_DR |= (1<<3);
- }
-
- /*
- * @description : 短时间延时函数
- * @param - n : 要延时循环次数(空操作循环次数,模式延时)
- * @return : 无
- */
- void delay_short(volatile unsigned int n)
- {
- while(n--){}
- }
-
- /*
- * @description : 延时函数,在396Mhz的主频下
- * 延时时间大约为1ms
- * @param - n : 要延时的ms数
- * @return : 无
- */
- void delay(volatile unsigned int n)
- {
- while(n--)
- {
- delay_short(0x7ff);
- }
- }
-
- /*
- * @description : mian函数
- * @param : 无
- * @return : 无
- */
- int main(void)
- {
- clk_enable(); /* 使能所有的时钟 */
- led_init(); /* 初始化led */
-
- while(1) /* 死循环 */
- {
- led_off(); /* 关闭LED */
- delay(500); /* 延时大约500ms */
-
- led_on(); /* 打开LED */
- delay(500); /* 延时大约500ms */
- }
-
- return 0;
- }
-
-
- main.h :
-
- #ifndef __MAIN_H
- #define __MAIN_H
-
- /*
- * CCM相关寄存器地址
- */
- #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068)
- #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C)
-
- #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)
- #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074)
- #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078)
- #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C)
- #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)
-
- /*
- * IOMUX相关寄存器地址
- */
- #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
- #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)
-
- /*
- * GPIO1相关寄存器地址
- */
- #define GPIO1_DR *((volatile unsigned int *)0X0209C000)
- #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)
- #define GPIO1_PSR *((volatile unsigned int *)0X0209C008)
- #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)
- #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)
- #define GPIO1_IMR *((volatile unsigned int *)0X0209C014)
- #define GPIO1_ISR *((volatile unsigned int *)0X0209C018)
- #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)
-
- #endif
-
下载运行就要涉及到芯片的启动方式和启动流程了,这点我们下一篇再讲。通常在测试或学习阶段,会将SD卡作为代码存储位置(方便连接电脑烧录程序)。IMX6ULL也不例外,是支持从SD卡启动的。烧录SD卡并不是简单的copy,在前面linux常用命令章节,我们介绍过一个dd命名,该命令可以按照指定格式将数据输出到设备,事实上我们就要使用这个命令。同时,我们前面也提到了IMX6ULL的DDR初始化方式,是在镜像的头部添加了一段数据,这段数据包含了DDR初始化所必要的数据,所以裸机程序要运行还要添加这段数据。
所幸原子帮我们做好了这么一个工具,在原子的资料开发工具中包含裸机烧写软件imxdownload,并且开放了源代码:
- imxdownload.c
-
- #include "stdio.h"
- #include "stdlib.h"
- #include "string.h"
- #include "imxdownload.h"
-
- #define SHELLCMD_LEN (200)
- #define BIN_OFFSET (3072)
-
- /* 此宏指明是否打印u-boot.imx的IVT DCD表信息,不同的开发板其IVT和DCD
- * 表的数据是不同的,因此需要获取所使用的开发板的IVT和DCD表信息,最
- * 简单的方法就是读取开发板配套资料里面的u-boot.imx的前1KB数据,理论上
- * 应该读取3KB的数据,但是表信息远远没有3K这么多,因此读1KB即可
- */
- #define PRINT_TAB 0
- /*
- * 介绍: 此软件是针对NXP的IMX6U系列芯片的,软件用来烧写bin文件到SD卡里面,
- * 本软件会自动添加IVT、DCD等信息到原始的bin文件里面,主要用于裸机和uboot的烧写。
- * 使用方法: 1、编译好原始的二进制bin文件,如,u-boot.bin等,并将编译好的.bin文件和本
- * 软件放置到同一个目录下!!!!
- * 2、执行命令sudo ./imxdownload <soucre_bin> <sd_device>
- * 如烧写u-boot.bin到/dev/sdd中即可使用如下所示命令:
- * sudo ./imxdownload u-boot.bin /dev/sdd
- */
- /*
- * 输出一些信息
- */
- void message_print(void)
- {
- printf("I.MX6ULL bin download software\r\n");
- printf("Edit by:zuozhongkai\r\n");
- printf("Date:2019/6/10\r\n");
- printf("Version:V1.1\r\n");
- printf("log:V1.0 initial version,just support 512MB DDR3\r\n");
- printf(" V1.1 and support 256MB DDR3\r\n");
- }
-
- int main(int argc, char *argv[])
- {
- FILE *fp;
- unsigned char *buf;
- unsigned char *cmdbuf;
- int nbytes, filelen;
- int i = 0, j = 0;
- int ddrsize = 0; /* 0为512MB,1为256MB,2为128MB...... */
-
- message_print();
-
- if((argc != 3) && (argc != 4)){
- printf("Error Usage! Reference Below:\r\n");
- printf("sudo ./%s <-512m or -256m> <source_bin> <sd_device>\r\n", argv[0]);
- return -1;
- }
-
- /* 查找参数,获取DDR容量 */
- for(i = 0; i < argc; i++)
- {
- char *param = argv[i];
- if(param[0] != '-')
- continue;
- if(strcmp(param, "-256m") == 0) /* 256MB */
- ddrsize = 1;
- else if(strcmp(param, "-512m") == 0) /* 512MB */
- ddrsize = 0;
- }
- if(argc == 3) /* 三个参数,也就是不输入DDR容量的话默认为512MB */
- ddrsize = 0;
-
- /* 打开bin文件 */
- fp = fopen(argv[1], "rb"); /* 以二进制只读方式打开bin文件 */
- if(fp == NULL){
- printf("Can't Open file %s\r\n", argv[1]);
- return -1;
- }
-
- /* 获取bin文件长度 */
- fseek(fp, 0L, SEEK_END);
- filelen = ftell(fp);
- fseek(fp, 0L, SEEK_SET);
- printf("file %s size = %dBytes\r\n", argv[1], filelen);
-
- /* 读取bin文件到缓冲区buf中 */
- buf = malloc(filelen + BIN_OFFSET);
- if(buf == NULL){
- printf("Mem Malloc Failed!\r\n");
- fclose(fp);
- return -1;
- }
- memset(buf, 0, filelen + BIN_OFFSET); /* 清零 */
- /* 读取bin源码文件 */
- fread(buf + BIN_OFFSET, 1, filelen, fp);
-
- /* 关闭文件 */
- fclose(fp);
-
- #if PRINT_TAB
- printf("IVT DCD Table:\r\n");
- for(i = 0; i < 1024/32; i++){
- for(j = 0; j < 8; j++)
- {
- printf("0X%08X,",*(int *)(buf + BIN_OFFSET + (((i * 8) + j) * 4)));
- }
- printf("\r\n");
- }
- free(buf);
- return 0;
- #endif
-
- /* 添加IVT DCD等表信息到bin文件里面 */
- if(ddrsize == 0) { /* 512MB */
- printf("Board DDR SIZE: 512MB\r\n");
- memcpy(buf, imx6_512mb_ivtdcd_table, sizeof(imx6_512mb_ivtdcd_table));
- }
- else if (ddrsize == 1) { /* 256MB */
- printf("Board DDR SIZE: 256MB\r\n");
- memcpy(buf, imx6_256mb_ivtdcd_table, sizeof(imx6_256mb_ivtdcd_table));
- }
-
- /* 现在我们已经在buf中构建好了可以用于下载的bin文件,将buf中的数据保存到
- * 到一个文件中,文件命名为load.imx
- */
- printf("Delete Old load.imx\r\n");
- system("rm -rf load.imx"); /* 先删除旧的load.imx文件 */
-
- printf("Create New load.imx\r\n");
- system("touch load.imx"); /* 创建新的load.imx文件 */
- fp = fopen("load.imx", "wb"); /* 打开laod.imx */
- if(fp == NULL){
- printf("Cant't Open load.imx!!!\r\n");
- free(buf);
- return -1;
- }
- nbytes = fwrite(buf, 1, filelen + BIN_OFFSET, fp);
- if(nbytes != (filelen + BIN_OFFSET)){
- printf("File Write Error!\r\n");
- free(buf);
- fclose(fp);
- return -1;
- }
- free(buf);
- fclose(fp);
-
- /* 构建烧写的shell命令 */
- cmdbuf = malloc(SHELLCMD_LEN);
- sprintf(cmdbuf, "sudo dd iflag=dsync oflag=dsync if=load.imx of=%s bs=512 seek=2",argv[2]);
- printf("Download load.imx to %s ......\r\n", argv[2]);
-
- /* 执行上面的shell命令 */
- system(cmdbuf);
- free(cmdbuf);
- return 0;
- }
可以看到,软件把裸机bin文件读到buf中,然后根据DDR的容量从.h文件中取对应的数据放在bin前面,最终合并成一个文件。
在代码的最后,可以看到如下命令:
这条命令就是我们提到的烧录的根本方式!
- imxdownload.h
-
- #ifndef _IMXDOWNLOAD_H
- #define _IMXDOWNLOAD_H
- /* IMX6U IVT DCD表信息 暂时定义为1K Bytes,此表是读取的u-boot.imx前1K Bytes
- * imx6_ivedcd_table[9]是指明代码长度的,本应该根据实际的代码长度来修改
- * 这里为了方便,就直接定义为2M Bytes,即
- */
- const int imx6_512mb_ivtdcd_table[256] = {
- 0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
- 0X877FF000,0X00200000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
- 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
- 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
- 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
- 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
- 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
- 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
- 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X0B000300,0X3C081B02,
- 0X44014801,0X48081B02,0X302C4040,0X50081B02,0X343E4040,0X1C081B02,0X33333333,0X20081B02,
- 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
- 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF3526B67,0X10001B02,
- 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
- 0XD2260000,0X30001B02,0X23106B00,0X40001B02,0X4F000000,0X00001B02,0X00001884,0X90081B02,
- 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
- 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
- 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000
- };
-
- const int imx6_256mb_ivtdcd_table[256] = {
- 0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
- 0X877FF000,0X00076000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
- 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
- 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
- 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
- 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
- 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
- 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
- 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X04000000,0X3C081B02,
- 0X3C013C01,0X48081B02,0X38324040,0X50081B02,0X28304040,0X1C081B02,0X33333333,0X20081B02,
- 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
- 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF352433F,0X10001B02,
- 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
- 0XD2260000,0X30001B02,0X23104300,0X40001B02,0X47000000,0X00001B02,0X00001883,0X90081B02,
- 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
- 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
- 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
- };
-
- #endif
imxdownload.h 文件存放的数据就是要添加到裸机bin文件头部的。这些数据的具体含义我们在下节介绍!
将SD卡插入读卡器,接入电脑,在使用虚拟机的情况下vmware会询问你接入主机还是虚拟机,这里选择虚拟机,就算不慎选错,在虚拟机的右下角可以将移动存储设备连接到虚拟机。在终端中使用ls /dev/sd*命令查看SD卡是哪个设备,接着使用如下命令烧录!
注意:原子的裸机教程还是很丰富的,并且对于不熟悉这类系统级处理器的soc朋友来说裸机是一种铺垫,是一种基础,但是笔者并没有打算完整的记录裸机开发的每个实验,因为对于大多数人来说工作用不到,而且裸机其实也并不简单,需要对外设基本原理特别熟悉,否则连寄存器定义都看不明白。如果是打算从事驱动工作的朋友,系统玩的再6,也跑不了跟寄存器打交道,不用急于一时死磕一个东西,如果是想从事应用的朋友,除非打算做全栈,否则别提寄存器了,系统的底层你都接触不到。所以本系列笔记的裸机部分仅从理论出发做了解!想详细学习的朋友请参考“正点原子”教程!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。