I.MX6ULL——点亮一个LED

i-mx6ull

我们今天分享如何用裸机开发,点亮I.MX6ULL;我使用的是正点原子阿尔法IMX6ULL开发板,EMCC版本,目前还是处于学习中。

作为点灯大师,万里长征第一步从点灯开始。

如何点亮LED

我们在编写STM32点灯代码时候,第一步要做的是了解需要点亮哪一个灯,查看原理图找到LED的连接引脚和什么样的输出状态才能让LED点亮。

我们找到正点原子的原理图,找到LED的部分,发现其的LED是与GPIO0_IO03连接,当属于引脚输出低电平时候点亮。

配置引脚

我们对比STM32使用标准库对一个GPIO引脚初始化的代码,

void LED_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能 PB 端口时钟
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PB5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度
 GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化 GPIOB.5
 
 GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}

可以发现STM32初始化引脚分为

  • 初始化时钟

  • GPIO配置

  • GPIO是否使用复用功能

  • GPIO输出电平

那么很显然,我们要用的IMX6ULL和STM32的步骤是相同的,下面我们就开始照着官方参考手册一行行写汇编。

配置部分

初始化时钟

查找官方参考手册,寻找“Clock Controller Module”(时钟控制模块),在该目录下有我们所用的寄存器

我们就拿**CCM Clock Gating Register 0 (CCM_CCGR0)**进行分析,在手册中是这样介绍的“These bits are used to turn on/off the clock to each module independently. The following table details the possible clock activity conditions for each module”大概跟大家翻译一下,这些位都是可以独立控制模块时钟的使能的,下面就是它的详细介绍。

可以看到我们有3种状态,2个位表示:

  • 00:时钟使能的

  • 01:时钟只在运行的时候启动,等待或停止模式下是不使能的

  • 10:无意义

  • 11:除了停止模式,其他时候都是运行的

我们可以选择11来使能时钟,我们使用的是GPIO1,我们找到,GPIO1的时钟使能是通过CCM_CCGR1的27-26位控制的。

我们只需要让CCM_CCGR1中的27-26位为1就可以了,或者我们直接让所有位均为1。

GPIO复用配置

我们点亮LED使用的肯定是输出模式,控制GPIO复用模式的寄存器为“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”,我们需要将它设置为0x5。

GPIO电气属性

设置GPIO电气属性的寄存器为“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03

0 SRE:设置压摆率,当此位为0,是低压摆率;当为1时,是高压摆率。这里压摆率就是IO电平跳变的时间,电平跳变的时间越小波形越陡,说明压摆率越高,反之则压摆率越低。如果此IO用于高速通信,建议使用高压摆率。

5:3 DSE:用于设置驱动能力,共有8个选择:

7:6 SPEED:设置IO的速率

  • 00:低速 50M

  • 01/10:中速 100M

  • 11:最大速度 200M

11 ODE:设置IO禁止或者使能开漏输出,0为禁止开漏输出,1为使能开漏输出。

12 PKE:设置IO禁止或者使能上下拉/状态保持器功能,1为使能上下拉和状态保持器,0为禁止上下拉/状态保持器。

13 PUR:设置使用上下拉还是使用状态保存期,0是用状态保存期,q的使用上下拉。

15:14 PUS:设置上下拉电阻

16 HYS:用来使能迟滞比较器(不了解)

现在我们已经分析完了电气属性中每个位的功能,没有分析到的就是无用的位,我们现在开始编写汇编代码。

配置GPIO功能

配置GPIO的输入或输出的寄存器是”GPIO_x__GDIR“,我们使用的GPIO_1_对应的地址为0x0209C004,该寄存器中的每一个位对应一个IO,0为输入,1为输出。

设置电平状态

这是电平状态的寄存器为”GPIO_x_DR“,我们使用的为”GPIO1DR“

汇编代码

.global _start @全局标号

_start:
      /* 使能时钟
      * 使能所有时钟
      */
     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]

      /* 配置GPIO
        * IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器地址为0x02E0068
        * 设置  IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03为5
        */
      ldr r0, =0x02E0068
      ldr r1, =0x5
      str r1,[r0]

       /*设置电气属性
        * IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03)寄存器地址为0x020E02F4
        * 0  压摆率:0
        * 5:3 驱动能力:110
        * 7:6 速率:10
        * 11 开路输出: 0
        * 12 上下拉/状态保存使能:1
        * 13 上下拉/状态保存设置:0
        * 14:15 上下拉电阻:00
        *  迟滞比较器使能:0
        */
     ldr r0, =0x020E02F4
     ldr r1, =0x10B0
     str r1, [r0]
    
     /*设置GPIO功能
       * GPIO1_GDIR寄存器地址为0x0209C004
       * 设置GPIO1_GDIR的bit3为1
       */
       ldr r0, =0x0209C004
       ldr r1, =0x8
       str r1, [r0]

       /* 开灯,设置GPIO0_IO3为0
         * GPIO1DR寄存器的地址为0x0209C000
         */
     ldr r0, =0x0209C000
     ldr r1, =0x0
     str r1, [r0]

loop:
    b loop

编译程序

将.c.s文件变为.o、

需要使用交叉编译器arm-linux-gnueabihf-gcc来编译,工作空间的文件目录下输入指令:

//arm-linux-gnueabihf-gcc -g -c 汇编文件 -o 转换后的文件名.o
arm-linux-gnueabihf-gcc -g -c led.s -o led.o

“-g”选项是产生调试信息,GND能够使用这些调试信息进行代码调试;“-c”选项是编译源文件,而不是链接;“-o”选项是指定编译产生的文件名称。

将所有.o文件连接为elf格式的可执行文件

需要使用交叉编译器arm-linux-gnueabihf-ld来连接,将多个.o文件连接到一个指定的链接位置(地址)。所以我们在链接的时候要指定链接起始地址,起始地址就是代码运行的起始地址。

6ULL链接地址应指向RAM,因为Cortex-A没有内部flash。RAM范围为(0x9000000x91FFFF)。或者放到外部DDR中。(正点原子阿尔法开发板的512MB)DDR的地址范围为(0x800000000x9FFFFFFF)。

正点原子使用文档中,使用的是0x87800000,是与Uboot统一使用,不容易记混。

我们使用命令

arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
  • -Ttext 表示链接地址

  • -o 表示指定链接生成的elf文件

我们此时工作空间就多了一个elf文件,但是我们最后烧录的是bin文件,所以还需要将elf转为bin文件。

将elf文件转为bin文件

使用命令“arm-linux-gnueabihf-objcopy

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

  • -O指定什么格式输出

  • binary 二进制格式输出

  • -S表示不要复制源文件中的重定位信息和符号信息

  • -g表示不复制源文件的调试信息

将elf文件转为汇编(反汇编)

arm-linux-gnueabihf-objdump -D led.elf > led.dis

上述代码中的“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前工作空间下出现一个名为 led.dis 文件。

烧录程序

烧录使用的是SD卡方式,(SD卡必须支持FAT32模式),32G一下默认为FAT32模式。

在Ubuntu下向SD卡烧写程序,烧写程序为将bin文件烧写到SD卡绝对地址上。 而且对于IMX6ULL而言,不能直接烧录bin文件,比如要在bin文件添加头部。这个部分我使用的是正点原子提供的imxdownload软件。

将imxdownload复制到工作空间中

将imxdownload复制到工作空间,并基于执行权限,这里的777是给予全部权限,然后可以看到我们的imxdownload会变成绿色。

chmod 777 imxdownload

查看SD卡位置

使用_ls /dev/_sd* -l查找,我们在拔出时候查看一次,插入之后再查看一次,新增的就是我们的SD卡位置。

可以看到,我的虚拟机上每次插拔,sdb会重新出现,同时伴随着sdb1。

烧写

烧写的命令是

// ./imxdownload 烧写的文件 烧写的地址
./imxdownload led.bin /dev/sdb

烧写完成后,他会显示时间和内存大小,与STM32类似。然后我们可以发现,此时我们工作空间中多了一个load.imx文件,那么load.imx就是我们要最后烧录的SD卡的文件。

插板

我们将SD卡插入到板子中,按复位键后(将拨码拨到SD卡模式),1-2秒后LED点亮。

为什么会延时:因为内部Uboot运行,首先检测从什么地方启动代码,从SD卡中读代码,SD卡头部中又有一些配置信息。然后先把我们代码放到我们的链接地址中,然后才是运行我们的代码,所以会有1-2s的延时。

恭喜你,成功称为I.MX6ULL的点灯大师!!!

在Linux中体现编译烧录的过程,岂不快哉?

如果你看到这了,那么我再讲一点吧

快速的编译烧录

我们编译的时候是在一直输命令,这样太麻烦。我们最好的方法就是创建makefile。

我们在工作空间下新建文件“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

并在最后清零工程

总体下来就是:

led.bin:led.s
    arm-linux-gnueabihf-gcc -g -c led.s -o led.o  //(前面的缩进一定要用Tab进行,否则报错)
    arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
    arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
clean:
    rm -rf *.o led.bin led.elf

使用:

在工作空间中执行make指令;清理的话就是make clean。

相关文章

nfs下载镜像报错File lookup fail

学习正点原子的I.MX6ULL时,按照官方提供的开发文档使用nfs获取Ubuntu中的镜像的时候,发现会出问题 **最后,发现是nfs版本问题,正点原子官方提供的uboot中的nfs版本太老,而我们Ubuntu新下载的nfs是新版本,解决方法就是降级** 安装nfs 首先要确认你的Ubuntuy虚拟机已经正确的安装了NFS,如果没有的话可以参考正点原子的驱动开发文档中的内容,或参考我的...

i-mx6ull

Uboot命令

Uboot命令是可以配置的,需要什么命令使能什么命令就可以。我们输入help或者?即可查看命令的详细用法。help <查找的命令> 信息查询命令 常用的包括:bdinfo、printenv、version bdinfo **bdinfo可以查到板子信息**:DRAM(动态随机存储器)的起始地址和大小、启动参数保存起始地址、波特率、sp(堆指针)起始地址等信息 printenv...

i-mx6ull

合宙主流通信模组梳理

合宙作为我第一家实习公司,其在行业上是一位佼佼者,我们合宙推出多种产品,满足了几乎所有客户对通信模组的需求。 并且,合宙有一个特色`Luatos`,是一种基于`lua`的开发脚本。它是一种针对嵌入式的脚本运行框架。针对资源较少的嵌入式环境进行了优化,极大提升了运行效率。 >但是对于习惯C的嵌入式开发者,就需要拿出时间去学习、熟悉该脚本 我一直认为`一个公司的销售不了解产品那一定会让去问开...

flushbonading