一.LED灯的点亮——从简单的汇编开始了解
首先,我们一步步从汇编开始点亮板子上的LED灯!
为什么要先从汇编开始呢?一般来说我们只用C写程序就行了,但是系统上电以后要对SOC的外设、DDR进行初始化(我们用的是I.MX6U,不需要这一步),设置堆栈指针(一般指向DDR),设置好C语言的环境关闭看门狗等流程,但这些功能用C是无法实现的,必须通过汇编来做。好在只需要简单几个指令就可以了。
下面我们从开发板的LED硬件来分析下原理:
先看下硬件原理图:
灯通过3.3V供电,经过510欧的限流电阻和控制口LED0相连,通过搜索查询,我们发现LED0节点和GPIO_3相连(应该是GPIO1_3)
以前搞过STM32的伙伴们应该知道,要对IO进行初始化,需要下面的流程:
- 使能GPIO时钟
- 设置IO复用,将其设置为GPIO
- 配置GPIO的电气属性
- 使用GPIO,输出高低电平
对于IMX6ULL来说,流程基本是一样的,我们一步一步来设置
一.使能GPIO时钟:
查询IMX6ULL的芯片手册,CCM_CCGR0-6一共7个寄存器控制了6UL上所有的外设时钟的使能(第18.6.23章 CCM Clock Gating Register 0 (CCM_CCGR0)),而gpio1对应的时钟管理是归属于CCM_CCGR1的26、27两个bit管理,要想使能这个时钟,将其按照要求设置就可以了
设置值得要求
简单化,只要把两个比特值都置一就行了。更简单,吧CCGR0-6都设为0xFFFFFFFF,相当于使能所有外设时钟。
二.IO复用设置
复用设置的管理属于IOMUX Controller管理,查询芯片手册,我们要设置的是GPIO1的IO3,要设置的是IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03(SW_MUX_CTL_PAD_GPIO1_IO03 SW MUX Control Register )
下面有个表是对其进行说明的
复用模式(MUX_MODE)一共有7种模式,对应3-0bit,我们是要使用GPIO功能,也就是对应0101=5。
三.设置电气属性
下面要对GPIO1_IO03的电气属性进行设置。可以查下目录,管理其属性的寄存器是IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03(32.6.156 SW_PAD_CTL_PAD_GPIO1_IO03 SW PAD Control Register)注意跟复用的区别,一个是PAD,一个是MUX
这个电气属性的内容比较多
Field | Description |
0 ODE_0_Open_Drain_Disabled — Open Drain Disabled 1 ODE_1_Open_Drain_Enabled — Open Drain Enabled |
|
31–17 |
This field is reserved. |
16 |
Hyst. Enable Field 0 HYS_0_Hysteresis_Disabled — Hysteresis Disabled |
15–14 |
Pull Up / Down Config. Field 00 PUS_0_100K_Ohm_Pull_Down — 100K Ohm Pull Down |
13 |
Pull / Keep Select Field Select one out of next values for pad: GPIO1_IO03 0 PUE_0_Keeper — Keeper |
12 |
Pull / Keep Enable Field 0 PKE_0_Pull_Keeper_Disabled — Pull/Keeper Disabled |
11 |
Open Drain Enable Field |
10–8 - |
This field is reserved. Reserved |
7–6 SPEED |
Speed Field Select one out of next values for pad: GPIO1_IO03 00 SPEED_0_low_50MHz_ — low(50MHz) 01 SPEED_1_medium_100MHz_ — medium(100MHz) 10 SPEED_2_medium_100MHz_ — medium(100MHz) 11 SPEED_3_max_200MHz_ — max(200MHz) |
5–3 DSE |
Drive Strength Field 000 DSE_0_output_driver_disabled_ — output driver disabled; |
2–1 - |
This field is reserved. Reserved |
0 |
Slew Rate Field 0 SRE_0_Slow_Slew_Rate — Slow Slew Rate |
也就是说寄存器IOMUSXC_SW_PAD_CTL_PAD_GPIO1_IO03是用来配置GPIO_IO03的,包括速度、驱动能力压摆率等设置,下面挨个分析下这几个参数
- SRE(bit0):压摆率,指IO电平反转所需要的时间,时间越小波形就越抖,即压摆率高。0的时候为低压摆率,1时为高。当我们的IO做高速IO时要设置为1
- DSE(bit5-3):当IO作为输出时的驱动能力。共8个选项,0为关闭驱动,其余对应表格查询即可
- SPEED(bit7-6):IO用作输出时的IO速度,按需求依据表格设置
- ODE(bit11):IO作为输出时,是否使能开路输出,0时禁止开路输出,1时使能
- PKE(bit12):用来使能或禁止上下拉、状态保持器的功能:0时禁止上下拉/状态保持器,1时使能
- PUE(bit13):当IO为输如的时候,用来设置IO使用上下拉还是状态保持器:1时为上下拉,0时为状态保持器,即外部电路断电后此IO口可保持以前的状态。
- PUS(bit15-14):设置上下拉电阻阻值,按需设置
- HYS(bit16):使能迟滞比较器,当IO作为输入功能时有效,用于设置输入接收器的施密特触发器是否使能,如果需要对输入波形进行整形的时候可以使能此位,0时禁止,1时使能
整个功能图如图所示
四.GPIO配置
这里还有一个要配置是在STM32里没有的:配置GPIO,包含输入/输出,数据等等
手册里有一章是专门讲GPIO的(Chapter28) ,查寄存器目录发现有8个寄存器是我们需要设置的
GPIO data register(GPIOx_DR)
GPIO数据寄存器,一共32位,对应了IO的32个点,当GPIO配置为输出到时候时,0为低电平,1为高电平。当GPIO为输出到模式时,每个位对应一个GPIO,比方GPIO1_IO00引脚接地时,GPIO.DR的bit0值为0
GPIO direction register (GPIOx_GDIR)
方向寄存器(GPIO direction register (GPIOx_GDIR),28.5.2)
同样也是32位,表明32个IO点工作的方向,0为输入,1为输出
我们需要的是将GPIO1_IO03设置为输出模式,就要将GPIO1_GDIR的第3个bit置1
剩下的还有PSR,ICR1,ICR2,IMR,ISR,EDGE_SEL我们暂时用不到,这里就先不讲了,总之,GPIO的整体配置流程如下图所示
代码编写
这里使用汇编来实现,汇编语言的介绍我们放在另一章来讲。反正这里要用到就是读取、写入寄存器。
就按照前面的流程,先使能GPIO时钟,这里为了简化操作,把所有Soc和GPIO1一个寄存器控制的所有外设都设为使能,也就是将CCM_CCGR1全都使能。
CCM_CCGR1的地址为20C_4000h base + 6Ch offset = 20C_406Ch(0x020c406c)
/*使能所有外设时钟 */ ldr r0,=0x020c4068 @CCGR0 ldr r1,=0xffffffff @向CCGR0待写入的数据 str r1,[r0] @将r1里的数据写入r0地址的寄存器
第二步就是设置复用,对应寄存器为IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03,地址为0x020e0068,应讲MUX_MODE设置为ALT5
@设置复用IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5 ldr r0,=0x020e0068 ldr r1,=0x5 str r1,[r0]
第三步是设置电气属性,设置寄存器
- bit 16:0 HYS关闭
- bit [15:14]: 00 默认下拉
- bit [13]: 0 kepper功能
- bit [12]: 1 pull/keeper使能
- bit [11]: 0 关闭开路输出
- bit [10-8]: 000 空
- bit [7:6]: 10 速度100Mhz
- bit [5:3]: 110 R0/6驱动能力
- bit [2:1]: 00 空
- bit [0]: 0 低转换率
ldr r0,=0x020e02f4 ldr r1,=0x10b0 str r1,[r0]
第四步是将GPIO设置为输出模式,也就是将GPIO1_GDIR的第2个bit置1,换算值为0x8,GPIO1_GDIR的地址为0x209c004
ldr r0,=0x0209c004 ldr r1,=0x8 str r1,[r0]
下一步就是给GPIO的数据寄存器赋值,注意看前面的硬件原理图,我们的LED要想点亮的话控制端需要为低电平,所以直接把GPIO1的值置0就好,GPIO1_DR的地址为0x209c000
ldr r0,=0x0209C000 ldr r1,=0 str r1,[r0]
最后放个死循环,让程序跑起来就行了
loop: b loop
最后放上所有的代码
.global _start @全局标号 _start: @ 开启所有时钟CCM_CCGR0-6 /*使能所有外设时钟 */ ldr r0,=0x020c4068 @CCGR0 ldr r1,=0xffffffff @向CCGR0待写入的数据 str r1,[r0] @将r1里的数据写入r0地址的寄存器内 @设置复用IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5 ldr r0,=0x020e0068 ldr r1,=0x5 str r1,[r0] @设置电气属性IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 地址0x020E02F4 /* @ *bit 16:0 HYS关闭 @ *bit [15:14]: 00 默认下拉 @ *bit [13]: 0 kepper功能 @ *bit [12]: 1 pull/keeper使能 @ *bit [11]: 0 关闭开路输出 @ *bit [10-8]: 000 空 @ *bit [7:6]: 10 速度100Mhz @ *bit [5:3]: 110 R0/6驱动能力 @ *bit [2:1]: 00 空 @ *bit [0]: 0 低转换率 @ 00001 0000 1011 0000>0x10B0 */ ldr r0,=0x020e02f4 ldr r1,=0x10b0 str r1,[r0] /*设置GPIO1_IO03为输出, GPIO1_GDIR地址为 0x209_C004 IO03对应第2bit,值为0x8 */ ldr r0,=0x0209C004 ldr r1,=0x8 str r1,[r0] /*设置GPIO输出高电平 GPIO1_DR,地址为0x209_C000 IO03对应第3bit,低电平时灯点亮,值为0 */ ldr r0,=0x0209C000 ldr r1,=0 str r1,[r0] loop: b loop
整个代码的流程就完了,因为代码已经跑过,我这里就不讲调试的过程了,下一章再仔细讲一下如何编译代码