在STM32中,厂家通常提供好了start_up.s文件来完成芯片的上电配置,C语言运行环境初始化。而MPU厂商不一般不会提供。

使用MPU外设的基本步骤:

  • 需要使用汇编来初始化SOC 外设;
  • 需要使用汇编初始化DDR内存(i.MX6ull不需要);
  • 设置SP指针,一般是指向DDR内存中的地址,为C语言提供运行环境。

点亮LED(使用GPIO)基本步骤:

使能时钟: CCGR0-CCGR7寄存器控制所有外设的时钟使能。

配置IO复用寄存器:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;将Bit[3:0]设置为0101。

配置电气属性:上下拉,压摆率,速度,开漏推挽,驱动能力…需要配置(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00)寄存器

配置GPIO属性:输入或输出等:GPIO direction register (GPIOx_GDIR)寄存器bit置1,配置为输出模式。GPIO data register (GPIOx_DR)bit3写入0或1,使其输出高低电平。

汇编代码编写:

汇编的基本语法:

label:
     instruction @comment

label代表标号也是程序的地址,instruction 是具体指令操作,comment是注释内容。

add:
    MOVS R0,#0x12 @给R0寄存器赋值0x12

汇编程序的默认入口是_start:相当于C中的main()函数,也可以在链接脚本中使用ENTRY手动指定程序入口点。

在程序中ARM内核可以直接读写R0-R15通用寄存器,但无法直接读写RAM中的数据,需要使用LDR,STR指令,

举例:读写0x20000000地址的数据:

LDR R0,=0x20000000 @把32bit地址数据0x20000000加载到R0寄存器中
LDR R1,[R0]        @读取0x20000000地址的数据保存在R1寄存器中
LDR R1,=0xAAAA5555 @把数据0xAAAA5555加载到R1寄存器中
STR R1,[R0]        @把0xAAAA5555保存到0x20000000地址中

编写代码点亮一个LED灯:

根据原理图,LED0阴极连接到I.MX6ULL芯片的GPIO3管脚,要点亮LED,则需要GPIO3推挽输出低电平。

.global _start @.global是伪操作,表示_start是一个全局标号,类似C语言中的全局变量

_start:
    LDR R0, =0x20c4068 @CCGR0的寄存器地址
    LDR R1, =0xffffffff
    STR R1, [R0]        @CCGR0寄存器写入0xffffffff

    LDR R0, = 0X020C406C
    STR R1, [R0]        @CCGR1寄存器写入0xffffffff

    LDR R0, = 0X020C4070
    STR R1, [R0]        @CCGR2寄存器写入0xffffffff

    LDR R0, = 0X020C4074
    STR R1, [R0]        @CCGR3寄存器写入0xffffffff

    LDR R0, = 0X020C4078
    STR R1, [R0]        @CCGR4寄存器写入0xffffffff

    LDR R0, = 0X020C407C
    STR R1, [R0]        @CCGR5寄存器写入0xffffffff

    LDR R0, = 0X020C4080
    STR R1, [R0]        @CCGR6寄存器写入0xffffffff
    @完成时钟使能

    @设置GPIO3的复用功能
    LDR R0, = 0X020E0068 @IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器的地址
    LDR R1, = 0X5
    STR R1, [R0]        @ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1

    @配置电气属性
    LDR R0, = 0X020E02F4 @IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器的地址
    @BIT0->0 设置低压摆速率
    @BIT[5:3]->110 设置R0/6的驱动能力
    @BIT[7:6]->10  设置100MHz速度
    @BIT11->0 关闭开漏输出
    @BIT12->1 使能pull/kepper
    @BIT[15:14]->00 设置100k下拉
    @BIT16->0 关闭HYS

    LDR R1, = 0X10B0
    STR R1, [R0]        

    @设置GPIO的GDIR寄存器和DR寄存器
    LDR R0, = 0X0209C004 @GDIR寄存器的地址
    LDR R1, = 0X8
    STR R1, [R0] @GDIR寄存器bit3配置为1

    LDR R0, =0X0209C000 @DR寄存器地址
    LDR R1, =0
    STR R1, [R0]

loop:
    b loop @让程序进入循环

编译链接转换:

程序的编译步骤一般为: .c .s源代码编译为.o;.o链接为.elf可执行文件。对上述代码进行编译链接:

arm-linux-gnueabihf-gcc -g -c led.s -o led.o
生成了led.o文件

接下来将.o文件链接为elf可执行程序,注意:在链接时,需要指定链接的起始地址,链接的起始地址就是代码运行的起始地址。

对于MPU,一般要将链接起始地址指向RAM空间(内部RAM和外部DDR内存空间皆可)。若要使用外部DDR空间,则需要添加头部,IMX6ULL芯片根据头部中的信息初始化DDR存储器。

IMX6ULL在上电后会运行芯片内部的Boot ROM程序,从SD卡,EMMC等外部设备读取头部信息,再进行DDR初始化。之后再将bin文件拷贝到指定地址。

arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf

其中-Ttext参数指定的是链接地址。

将.elf转换为.bin文件:

arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin

烧写运行

将.bin文件烧录到SD卡而不是拷贝到SD卡,烧录过程中,程序被写入到SD卡的绝对地址上。此外还需要借助imxdownload工具添加头部。

将工具拷贝到工程目录下,发现只有RW权限,没有'X'执行权限,因此需要输入:

chmod 777 imxdownload

插入一张16GB SD卡,使用lsblk命令可知SD卡的设备名称是sdb,由于linux中存储设备都是以“/dev/sd”开头的,也可以使用ls /dev/sd*命令查找。

使用 imxdownload 向 SD 卡烧写 led.bin 文件,命令格式为:

./imxdownload <.bin file> <SD Card>

此处的指令为:

./imxdownload led.bin /dev/sdb

将开发板配置为SD卡启动:

插入SD卡,上电启动:

上电后可以观察到DS0由灭到亮。


懒惰是进步的催化剂,也是失败的引擎。