当前位置:   article > 正文

I.MX6ULL_Linux_基础篇(7) 裸机开发流程_正点原子imx6ull裸机开发

正点原子imx6ull裸机开发

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

开发环境准备

        服务器(ubuntu环境)

        工具使用和开发方式可参考:IMX.6ULL_Linux_基础篇(5) linux开发工具与开发模式介绍_rm all的博客-CSDN博客

        在安装各种服务之前可以先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等,设置方法如下:

  1. sudo apt-get install vsftpd
  2. sudo vi /etc/vsftpd.conf
  3. 将配置项中如下两项改为YES
  4. local_enable=YES
  5. write_enable=YES
  6. sudo /etc/init.d/vsftpd restart

         SFTP服务不需要安装或设置,SSH启用的同时SFTP即可使用。

        在win下安装FTP客户端,或者WinSCP等工具(工具使用自行搜索学习)

        Samba服务

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

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处理器运行模式:

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

JT描述
00ARM
01Thumb
11ThumbEE
10Jazelle

        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中,并切换指令集

         算数运行指令:

         逻辑运算指令:

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是如何定义的:

         通过查看手册目录,我们可以发现第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寄存器控制电平高低

代码编写

  1. .global _start /* 全局标号 */
  2. /*
  3. * 描述: _start函数,程序从此函数开始执行此函数完成时钟使能、
  4. * GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。
  5. * 汇编默认入口标号为_start,也可以在链接脚本中使用ENTRY来指定。
  6. */
  7. _start:
  8. /* 例程代码 */
  9. /* 1、使能所有时钟 */
  10. ldr r0, =0X020C4068 /* CCGR0 */
  11. ldr r1, =0XFFFFFFFF
  12. str r1, [r0]
  13. ldr r0, =0X020C406C /* CCGR1 */
  14. str r1, [r0]
  15. ldr r0, =0X020C4070 /* CCGR2 */
  16. str r1, [r0]
  17. ldr r0, =0X020C4074 /* CCGR3 */
  18. str r1, [r0]
  19. ldr r0, =0X020C4078 /* CCGR4 */
  20. str r1, [r0]
  21. ldr r0, =0X020C407C /* CCGR5 */
  22. str r1, [r0]
  23. ldr r0, =0X020C4080 /* CCGR6 */
  24. str r1, [r0]
  25. /* 2、设置GPIO1_IO03复用为GPIO1_IO03 */
  26. ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */
  27. ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
  28. str r1,[r0]
  29. /* 3、配置GPIO1_IO03的IO属性
  30. *bit 16:0 HYS关闭
  31. *bit [15:14]: 00 默认下拉
  32. *bit [13]: 0 kepper功能
  33. *bit [12]: 1 pull/keeper使能
  34. *bit [11]: 0 关闭开路输出
  35. *bit [7:6]: 10 速度100Mhz
  36. *bit [5:3]: 110 R0/6驱动能力
  37. *bit [0]: 0 低转换率
  38. */
  39. ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */
  40. ldr r1, =0X10B0
  41. str r1,[r0]
  42. /* 4、设置GPIO1_IO03为输出 */
  43. ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */
  44. ldr r1, =0X0000008
  45. str r1,[r0]
  46. /* 5、打开LED0
  47. * 设置GPIO1_IO03输出低电平
  48. */
  49. ldr r0, =0X0209C000 /*寄存器GPIO1_DR */
  50. ldr r1, =0
  51. str r1,[r0]
  52. /*
  53. * 描述: loop死循环
  54. */
  55. loop:
  56. b loop

编译

Makefile:

  1. led.bin:led.s
  2. arm-linux-gnueabihf-gcc -g -c led.s -o led.o
  3. arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
  4. arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
  5. arm-linux-gnueabihf-objdump -D led.elf > led.dis
  6. clean:
  7. 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汇编:

  1. .global _start /* 全局标号 */
  2. /*
  3. * 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
  4. * 运行环境。
  5. */
  6. _start:
  7. /* 进入SVC模式 */
  8. mrs r0, cpsr
  9. bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
  10. orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
  11. msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
  12. ldr sp, =0X80200000 /* 设置栈指针 */
  13. b main /* 跳转到main函数 */

 C main:

  1. #include "main.h"
  2. /*
  3. * @description : 使能I.MX6U所有外设时钟
  4. * @param : 无
  5. * @return : 无
  6. */
  7. void clk_enable(void)
  8. {
  9. CCM_CCGR0 = 0xffffffff;
  10. CCM_CCGR1 = 0xffffffff;
  11. CCM_CCGR2 = 0xffffffff;
  12. CCM_CCGR3 = 0xffffffff;
  13. CCM_CCGR4 = 0xffffffff;
  14. CCM_CCGR5 = 0xffffffff;
  15. CCM_CCGR6 = 0xffffffff;
  16. }
  17. /*
  18. * @description : 初始化LED对应的GPIO
  19. * @param : 无
  20. * @return : 无
  21. */
  22. void led_init(void)
  23. {
  24. /* 1、初始化IO复用 */
  25. SW_MUX_GPIO1_IO03 = 0x5; /* 复用为GPIO1_IO03 */
  26. /* 2、、配置GPIO1_IO03的IO属性
  27. *bit 16:0 HYS关闭
  28. *bit [15:14]: 00 默认下拉
  29. *bit [13]: 0 kepper功能
  30. *bit [12]: 1 pull/keeper使能
  31. *bit [11]: 0 关闭开路输出
  32. *bit [7:6]: 10 速度100Mhz
  33. *bit [5:3]: 110 R0/6驱动能力
  34. *bit [0]: 0 低转换率
  35. */
  36. SW_PAD_GPIO1_IO03 = 0X10B0;
  37. /* 3、初始化GPIO */
  38. GPIO1_GDIR = 0X0000008; /* GPIO1_IO03设置为输出 */
  39. /* 4、设置GPIO1_IO03输出低电平,打开LED0 */
  40. GPIO1_DR = 0X0;
  41. }
  42. /*
  43. * @description : 打开LED灯
  44. * @param : 无
  45. * @return : 无
  46. */
  47. void led_on(void)
  48. {
  49. /*
  50. * 将GPIO1_DR的bit3清零
  51. */
  52. GPIO1_DR &= ~(1<<3);
  53. }
  54. /*
  55. * @description : 关闭LED灯
  56. * @param : 无
  57. * @return : 无
  58. */
  59. void led_off(void)
  60. {
  61. /*
  62. * 将GPIO1_DR的bit31
  63. */
  64. GPIO1_DR |= (1<<3);
  65. }
  66. /*
  67. * @description : 短时间延时函数
  68. * @param - n : 要延时循环次数(空操作循环次数,模式延时)
  69. * @return : 无
  70. */
  71. void delay_short(volatile unsigned int n)
  72. {
  73. while(n--){}
  74. }
  75. /*
  76. * @description : 延时函数,在396Mhz的主频下
  77. * 延时时间大约为1ms
  78. * @param - n : 要延时的ms数
  79. * @return : 无
  80. */
  81. void delay(volatile unsigned int n)
  82. {
  83. while(n--)
  84. {
  85. delay_short(0x7ff);
  86. }
  87. }
  88. /*
  89. * @description : mian函数
  90. * @param : 无
  91. * @return : 无
  92. */
  93. int main(void)
  94. {
  95. clk_enable(); /* 使能所有的时钟 */
  96. led_init(); /* 初始化led */
  97. while(1) /* 死循环 */
  98. {
  99. led_off(); /* 关闭LED */
  100. delay(500); /* 延时大约500ms */
  101. led_on(); /* 打开LED */
  102. delay(500); /* 延时大约500ms */
  103. }
  104. return 0;
  105. }
  106. main.h :
  107. #ifndef __MAIN_H
  108. #define __MAIN_H
  109. /*
  110. * CCM相关寄存器地址
  111. */
  112. #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068)
  113. #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C)
  114. #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)
  115. #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074)
  116. #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078)
  117. #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C)
  118. #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)
  119. /*
  120. * IOMUX相关寄存器地址
  121. */
  122. #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
  123. #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)
  124. /*
  125. * GPIO1相关寄存器地址
  126. */
  127. #define GPIO1_DR *((volatile unsigned int *)0X0209C000)
  128. #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)
  129. #define GPIO1_PSR *((volatile unsigned int *)0X0209C008)
  130. #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)
  131. #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)
  132. #define GPIO1_IMR *((volatile unsigned int *)0X0209C014)
  133. #define GPIO1_ISR *((volatile unsigned int *)0X0209C018)
  134. #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)
  135. #endif

下载运行

        下载运行就要涉及到芯片的启动方式和启动流程了,这点我们下一篇再讲。通常在测试或学习阶段,会将SD卡作为代码存储位置(方便连接电脑烧录程序)。IMX6ULL也不例外,是支持从SD卡启动的。烧录SD卡并不是简单的copy,在前面linux常用命令章节,我们介绍过一个dd命名,该命令可以按照指定格式将数据输出到设备,事实上我们就要使用这个命令。同时,我们前面也提到了IMX6ULL的DDR初始化方式,是在镜像的头部添加了一段数据,这段数据包含了DDR初始化所必要的数据,所以裸机程序要运行还要添加这段数据。

        所幸原子帮我们做好了这么一个工具,在原子的资料开发工具中包含裸机烧写软件imxdownload,并且开放了源代码:

  1. imxdownload.c
  2. #include "stdio.h"
  3. #include "stdlib.h"
  4. #include "string.h"
  5. #include "imxdownload.h"
  6. #define SHELLCMD_LEN (200)
  7. #define BIN_OFFSET (3072)
  8. /* 此宏指明是否打印u-boot.imx的IVT DCD表信息,不同的开发板其IVT和DCD
  9. * 表的数据是不同的,因此需要获取所使用的开发板的IVT和DCD表信息,最
  10. * 简单的方法就是读取开发板配套资料里面的u-boot.imx的前1KB数据,理论上
  11. * 应该读取3KB的数据,但是表信息远远没有3K这么多,因此读1KB即可
  12. */
  13. #define PRINT_TAB 0
  14. /*
  15. * 介绍: 此软件是针对NXP的IMX6U系列芯片的,软件用来烧写bin文件到SD卡里面,
  16. * 本软件会自动添加IVT、DCD等信息到原始的bin文件里面,主要用于裸机和uboot的烧写。
  17. * 使用方法: 1、编译好原始的二进制bin文件,如,u-boot.bin等,并将编译好的.bin文件和本
  18. * 软件放置到同一个目录下!!!!
  19. * 2、执行命令sudo ./imxdownload <soucre_bin> <sd_device>
  20. * 如烧写u-boot.bin到/dev/sdd中即可使用如下所示命令:
  21. * sudo ./imxdownload u-boot.bin /dev/sdd
  22. */
  23. /*
  24. * 输出一些信息
  25. */
  26. void message_print(void)
  27. {
  28. printf("I.MX6ULL bin download software\r\n");
  29. printf("Edit by:zuozhongkai\r\n");
  30. printf("Date:2019/6/10\r\n");
  31. printf("Version:V1.1\r\n");
  32. printf("log:V1.0 initial version,just support 512MB DDR3\r\n");
  33. printf(" V1.1 and support 256MB DDR3\r\n");
  34. }
  35. int main(int argc, char *argv[])
  36. {
  37. FILE *fp;
  38. unsigned char *buf;
  39. unsigned char *cmdbuf;
  40. int nbytes, filelen;
  41. int i = 0, j = 0;
  42. int ddrsize = 0; /* 0512MB,1256MB,2128MB...... */
  43. message_print();
  44. if((argc != 3) && (argc != 4)){
  45. printf("Error Usage! Reference Below:\r\n");
  46. printf("sudo ./%s <-512m or -256m> <source_bin> <sd_device>\r\n", argv[0]);
  47. return -1;
  48. }
  49. /* 查找参数,获取DDR容量 */
  50. for(i = 0; i < argc; i++)
  51. {
  52. char *param = argv[i];
  53. if(param[0] != '-')
  54. continue;
  55. if(strcmp(param, "-256m") == 0) /* 256MB */
  56. ddrsize = 1;
  57. else if(strcmp(param, "-512m") == 0) /* 512MB */
  58. ddrsize = 0;
  59. }
  60. if(argc == 3) /* 三个参数,也就是不输入DDR容量的话默认为512MB */
  61. ddrsize = 0;
  62. /* 打开bin文件 */
  63. fp = fopen(argv[1], "rb"); /* 以二进制只读方式打开bin文件 */
  64. if(fp == NULL){
  65. printf("Can't Open file %s\r\n", argv[1]);
  66. return -1;
  67. }
  68. /* 获取bin文件长度 */
  69. fseek(fp, 0L, SEEK_END);
  70. filelen = ftell(fp);
  71. fseek(fp, 0L, SEEK_SET);
  72. printf("file %s size = %dBytes\r\n", argv[1], filelen);
  73. /* 读取bin文件到缓冲区buf中 */
  74. buf = malloc(filelen + BIN_OFFSET);
  75. if(buf == NULL){
  76. printf("Mem Malloc Failed!\r\n");
  77. fclose(fp);
  78. return -1;
  79. }
  80. memset(buf, 0, filelen + BIN_OFFSET); /* 清零 */
  81. /* 读取bin源码文件 */
  82. fread(buf + BIN_OFFSET, 1, filelen, fp);
  83. /* 关闭文件 */
  84. fclose(fp);
  85. #if PRINT_TAB
  86. printf("IVT DCD Table:\r\n");
  87. for(i = 0; i < 1024/32; i++){
  88. for(j = 0; j < 8; j++)
  89. {
  90. printf("0X%08X,",*(int *)(buf + BIN_OFFSET + (((i * 8) + j) * 4)));
  91. }
  92. printf("\r\n");
  93. }
  94. free(buf);
  95. return 0;
  96. #endif
  97. /* 添加IVT DCD等表信息到bin文件里面 */
  98. if(ddrsize == 0) { /* 512MB */
  99. printf("Board DDR SIZE: 512MB\r\n");
  100. memcpy(buf, imx6_512mb_ivtdcd_table, sizeof(imx6_512mb_ivtdcd_table));
  101. }
  102. else if (ddrsize == 1) { /* 256MB */
  103. printf("Board DDR SIZE: 256MB\r\n");
  104. memcpy(buf, imx6_256mb_ivtdcd_table, sizeof(imx6_256mb_ivtdcd_table));
  105. }
  106. /* 现在我们已经在buf中构建好了可以用于下载的bin文件,将buf中的数据保存到
  107. * 到一个文件中,文件命名为load.imx
  108. */
  109. printf("Delete Old load.imx\r\n");
  110. system("rm -rf load.imx"); /* 先删除旧的load.imx文件 */
  111. printf("Create New load.imx\r\n");
  112. system("touch load.imx"); /* 创建新的load.imx文件 */
  113. fp = fopen("load.imx", "wb"); /* 打开laod.imx */
  114. if(fp == NULL){
  115. printf("Cant't Open load.imx!!!\r\n");
  116. free(buf);
  117. return -1;
  118. }
  119. nbytes = fwrite(buf, 1, filelen + BIN_OFFSET, fp);
  120. if(nbytes != (filelen + BIN_OFFSET)){
  121. printf("File Write Error!\r\n");
  122. free(buf);
  123. fclose(fp);
  124. return -1;
  125. }
  126. free(buf);
  127. fclose(fp);
  128. /* 构建烧写的shell命令 */
  129. cmdbuf = malloc(SHELLCMD_LEN);
  130. sprintf(cmdbuf, "sudo dd iflag=dsync oflag=dsync if=load.imx of=%s bs=512 seek=2",argv[2]);
  131. printf("Download load.imx to %s ......\r\n", argv[2]);
  132. /* 执行上面的shell命令 */
  133. system(cmdbuf);
  134. free(cmdbuf);
  135. return 0;
  136. }

 可以看到,软件把裸机bin文件读到buf中,然后根据DDR的容量从.h文件中取对应的数据放在bin前面,最终合并成一个文件。

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

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

  1. imxdownload.h
  2. #ifndef _IMXDOWNLOAD_H
  3. #define _IMXDOWNLOAD_H
  4. /* IMX6U IVT DCD表信息 暂时定义为1K Bytes,此表是读取的u-boot.imx前1K Bytes
  5. * imx6_ivedcd_table[9]是指明代码长度的,本应该根据实际的代码长度来修改
  6. * 这里为了方便,就直接定义为2M Bytes,即
  7. */
  8. const int imx6_512mb_ivtdcd_table[256] = {
  9. 0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
  10. 0X877FF000,0X00200000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
  11. 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
  12. 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
  13. 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
  14. 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
  15. 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
  16. 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
  17. 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X0B000300,0X3C081B02,
  18. 0X44014801,0X48081B02,0X302C4040,0X50081B02,0X343E4040,0X1C081B02,0X33333333,0X20081B02,
  19. 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
  20. 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF3526B67,0X10001B02,
  21. 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
  22. 0XD2260000,0X30001B02,0X23106B00,0X40001B02,0X4F000000,0X00001B02,0X00001884,0X90081B02,
  23. 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
  24. 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
  25. 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
  26. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  27. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  28. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  29. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  30. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  31. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  32. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  33. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  34. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  35. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  36. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  37. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  38. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  39. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  40. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000
  41. };
  42. const int imx6_256mb_ivtdcd_table[256] = {
  43. 0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
  44. 0X877FF000,0X00076000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
  45. 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
  46. 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
  47. 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
  48. 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
  49. 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
  50. 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
  51. 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X04000000,0X3C081B02,
  52. 0X3C013C01,0X48081B02,0X38324040,0X50081B02,0X28304040,0X1C081B02,0X33333333,0X20081B02,
  53. 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
  54. 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF352433F,0X10001B02,
  55. 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
  56. 0XD2260000,0X30001B02,0X23104300,0X40001B02,0X47000000,0X00001B02,0X00001883,0X90081B02,
  57. 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
  58. 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
  59. 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
  60. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  61. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  62. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  63. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  64. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  65. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  66. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  67. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  68. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  69. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  70. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  71. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  72. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  73. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  74. 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
  75. };
  76. #endif

imxdownload.h 文件存放的数据就是要添加到裸机bin文件头部的。这些数据的具体含义我们在下节介绍!

将SD卡插入读卡器,接入电脑,在使用虚拟机的情况下vmware会询问你接入主机还是虚拟机,这里选择虚拟机,就算不慎选错,在虚拟机的右下角可以将移动存储设备连接到虚拟机。在终端中使用ls /dev/sd*命令查看SD卡是哪个设备,接着使用如下命令烧录!

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/406123
推荐阅读
相关标签
  

闽ICP备14008679号