上一篇介绍了芯片的基本资源,本篇就开始学习怎么编程去使用芯片了(裸机,非系统)。不过鉴于该芯片基本都使用linux开发,裸机仅介绍流程。

开发环境准备

        服务器(ubuntu环境)

   

        在安装各种服务之前可以先update一下,能更好的支持安装过程。

        SSH服务

        开启 Ubuntu 的 SSH 服务以后我们就可以在 Windwos 下使用终端软件登陆到 Ubuntu,比如使用 SecureCRT, Ubuntu 下使用如下命令开启 SSH 服务:

sudo apt-get install openssh-server

         FTP/SFTP服务

        在开发的过程中需要频繁的在 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 编译android ubuntu 编译ram溢出_ubuntu 编译android

        Samba服务

        终端

        在win下安装SecureCRT 或 MobaXterm等等。

        交叉编译链

        在X86的电脑上编译ARM架构的代码就需要ARM的编译器,同时这个工具还要运行在X86架构上,因此该工具取名交叉编译链。一般使用相应SOC的SDK开发包时,开发包自带编译工具。此处原子提供了编译器,我们直接使用原子提供的工具。

将交叉编译工具传输至服务器:

ubuntu 编译android ubuntu 编译ram溢出_ubuntu 编译android_02

         在 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命令行进行编译。

ubuntu 编译android ubuntu 编译ram溢出_编译器_03

ARM架构

        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》

ARM汇编

        截至目前,作者在开发中要接触多种架构的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处理器运行模式:

ubuntu 编译android ubuntu 编译ram溢出_linux_04

         除了User,其他都是特权模式。

        ARM提供了16个32位通用寄存器供软件使用,前 15 个(R0~R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。 ARM 还提供了一个当前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR, SPSR 寄存器就是 CPSR 寄存器的备份。这 18 个寄存器如图所示:

ubuntu 编译android ubuntu 编译ram溢出_编译器_05

         对应9种模式的寄存器组:

ubuntu 编译android ubuntu 编译ram溢出_编译器_06

         *灰色标注寄存器多模式共用

  CPSR寄存器:

        所有的处理器模式都共用一个 CPSR 物理寄存器,因此 CPSR 可以在任何模式下被访问。CPSR 是当前程序状态寄存器,该寄存器包含了条件标志位、中断禁止位、当前处理器模式标志等一些状态位以及一些控制位。所有的处理器模式都共用一个 CPSR 必然会导致冲突,为此,除了 User 和 Sys 这两个模式以外,其他 7 个模式每个都配备了一个专用的物理状态寄存器,叫做 SPSR(备份程序状态寄存器),当特定的异常中断发生时, SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 SPSR 中保存的值来恢复 CPSR。因为 User 和 Sys 这两个模式不是异常模式,所以并没有配备 SPSR,因此不能在 User 和Sys 模式下访问 SPSR,会导致不可预知的结果。由于 SPSR 是 CPSR 的备份,因此 SPSR 和CPSR 的寄存器结构相同。

ubuntu 编译android ubuntu 编译ram溢出_arm_07

         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)位一起表示当前所使用的指令集

JT描述00ARM01Thumb11ThumbEE10Jazelle

        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]处理器模式10000User 模式10001FIQ 模式10010IRQ 模式10011Supervisor(SVC)模式10110Monitor(MON)模式10111Abort(ABT)模式11010Hyp(HYP)模式11011Undef(UND)模式11111System(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中,并切换指令集

         算数运行指令:

ubuntu 编译android ubuntu 编译ram溢出_编译器_08

         逻辑运算指令:

ubuntu 编译android ubuntu 编译ram溢出_ubuntu 编译android_09

GPIO控制(汇编)

GPIO了解

        涉及芯片外设相关信息需要参考如下手册,可以在原子提供的资料包中找到。参考资料:

        《IMX6ULL参考手册.pdf》

        《IMX6ULL数据手册(工业级).pdf》

        GPIO的基础知识可以参考STM32,建议先学习STM32了解基础外设的原理、控制方式和工作方式。

        对于芯片来说GPIO不等于一个Pin,应该说GPIO可能是一个Pin的功能。在芯片设计过程中,要保证在有限的封装大小支持更多的功能,除了在封装技术上下功夫之外,Pin复用是非常有用的设计,这样就可以做到一个Pin多个功能。所以如果我们想使用一个GPIO,则要关注对应的Pin是否支持GPIO,并且使用时要配置该Pin是GPIO功能。GPIO的中文是通用输入输出,所以GPIO在物理上的表现为Pin的电压为低电平(一般为0v)或高电平(一般为芯片供电电压)。

        我们先看一下6ULL的GPIO是如何定义的:

ubuntu 编译android ubuntu 编译ram溢出_ubuntu 编译android_10

         通过查看手册目录,我们可以发现第28章是GPIO控制器的描述,第32章是IO复用功能控制器的描述。

        看28章可以发现,该章节仅仅是对GPIO的控制所定义的,并没有引脚定义:

ubuntu 编译android ubuntu 编译ram溢出_ubuntu 编译android_11

 同样的,查看32章的细节,发现用户可以使用的Pin相关寄存器都在此处有定义:

ubuntu 编译android ubuntu 编译ram溢出_arm_12

        但是我们仔细看的时候会发现同样后缀名的寄存器有两个??? 比如:

ubuntu 编译android ubuntu 编译ram溢出_编译器_13

         两个寄存器名仅仅是中间有区别,一个是MUX,一个是PAD,那有什么区别呢,我们可以在上图中看到MUX的寄存器是怎么定义的。可以看出来,寄存器配置了复用模式,当选择ALT5模式时,该Pin是GPIO5_IO01。那我们再看看PAD寄存器是干什么的:

ubuntu 编译android ubuntu 编译ram溢出_ubuntu 编译android_14

         如果对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)

ubuntu 编译android ubuntu 编译ram溢出_arm_15

ubuntu 编译android ubuntu 编译ram溢出_编译器_16

        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对应寄存器。

ubuntu 编译android ubuntu 编译ram溢出_linux_17

         在本实验中:设置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文件! 

GPIO控制(C)

流程分析:

        设置处理器模式->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前面,最终合并成一个文件。

在代码的最后,可以看到如下命令:

ubuntu 编译android ubuntu 编译ram溢出_编译器_18

 这条命令就是我们提到的烧录的根本方式!

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卡是哪个设备,接着使用如下命令烧录!

ubuntu 编译android ubuntu 编译ram溢出_arm_19

注意:原子的裸机教程还是很丰富的,并且对于不熟悉这类系统级处理器的soc朋友来说裸机是一种铺垫,是一种基础,但是笔者并没有打算完整的记录裸机开发的每个实验,因为对于大多数人来说工作用不到,而且裸机其实也并不简单,需要对外设基本原理特别熟悉,否则连寄存器定义都看不明白。如果是打算从事驱动工作的朋友,系统玩的再6,也跑不了跟寄存器打交道,不用急于一时死磕一个东西,如果是想从事应用的朋友,除非打算做全栈,否则别提寄存器了,系统的底层你都接触不到。所以本系列笔记的裸机部分仅从理论出发做了解!