为了后续使用C语言驱动LED,事先学习汇编代码驱动LED,有如下好处:
- 熟悉一些基本的汇编语法
- 了解驱动LED的基本流程
- 了解驱动LED需要用到哪些寄存器
- 作为一个初学者,可以锻炼自己阅读开发文档的能力
本文的主要目的是了解驱动LED的基本流程,以及要驱动LED需要使用哪些寄存器。
目录
一、驱动LED的基本流程
二、寄存器设置
1、时钟源初始化(CCGRx)
2、设置IO复用(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03)
3、初始化GPIO(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03)
4、GPIO 输出(GPIOx_GDIR、GPIOx_DR)
一、驱动LED的基本流程
stm32驱动LED的基本流程为:
- 使能指定GPIO时钟
- 设置IO复用,将其复用为GPIO。(GPIO只是IO众多功能的一种)
- 初始化GPIO。即设置电气属性,比如设置输入还是输出、上下拉、速度等
- 设置 GPIO 输出高电平还是低电平。
但是 imx6ull 驱动LED的过程是否也是如此呢?我们需要查看《IMX6ULL参考手册》的第28章 GPIO,虽然只有三步,这是因为把 stm32 的最后两点合并为了一步。
因此,对于 imx6ull 我们驱动 LED 可以采取和stm32一样的步骤
二、寄存器设置
接下来我们将针对每一步,详细了解寄存器的设置方法。
1、时钟源初始化(CCGRx)
led灯闪烁,说白了就是高低电平的转换,但是每隔多久转换一次,这就需要时钟来控制了。按照《IMX6ULL参考手册》的提示,我们要去第18章CCM了解时钟源的初始化。不同模块对应的时钟源是不一样的,我们可以在 18.4 找到GPIO模块对应的时钟源。
从下图我们可以看到,GPIO模块的时钟源使能由寄存器 CCGRx 控制,GPIO模块有 5 组。CCGR1 寄存器的 CG13(即第27-16 bit)控制着 GPIO1 的时钟源。(其他类似)
现在有两种做法,一种是只初始化某一个 GPIOx 的时钟,即只初始化某一个 CCGRx 寄存器。实际上不同时钟源要改的引脚都不一样,这样很是麻烦。
因此,我们采用另一种做法,索性初始化所有时钟源 (CCGR0~6) 的所有模块(CG0~15,即引脚 0-32)。(可以在参考手册的18.6.25 找到每个时钟源CCGRx对应的基地址)
时钟源(寄存器): CCGR0
起始地址: 0x20C4068
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR1
起始地址: 0x20C406C # 一个时钟源占32bit,即4字节(0x20C406C - 0x20C4068 = 4)
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR2
起始地址: 0x20C4070
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR3
起始地址: 0x20C4074
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR4
起始地址: 0x20C4078
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR5
起始地址: 0x20C407C
初始化值:0xFFFFFFFF
时钟源(寄存器): CCGR6
起始地址: 0x20C4080
初始化值:0xFFFFFFFF
2、设置IO复用(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03)
绕了这么一大圈,终于可以回到 28.4 小节了。接下来我们要指定 IO 引脚为GPIO功能,由28.4.3给的提示可以知道,要设为 GPIO 模式,我们要设置的是 IOMUXC 寄存器。所以我们要前往第32章 IOMUX。
我们可以看到第32章下有一小节为IOMUXC Memory,IOMUXC下有很多寄存器,这些寄存器分为了两类:
- 第一类 IOMUXC_SW_MUX_CTL_PAD_<module name>:用于设置对应模块的IO复用
- 第二类 IOMUXC_SW_PAD_CTL_PAD_<module name>:用于初始化对应模块,即设置电气属性。
我们现在要设置的是IO复用,那自然就要找跟GPIO相关的
从这里你会发现,这里只有GPIO1 可以设置IO复用,下面要确定的就是GPIO1 中的第几个引脚,连接着 LED。我们先在《imx6ull 底板原理图》上找到 LED 模块,底板上LED名称为 LED0。
然后我们在《核心板原理图》上找一下 LED0 连到了核心板的哪个引脚。我们发现是 GPIO_3,那么这里就表示的是 GPIO1_3,即 GPIO1 的第3个引脚。
我们在第32章的IOMUX下找到后缀为 GPIO1_IO03 的寄存器,我们只需要设置 MUX_MODE 字段,设置为 0101,其他字段保持不变。
由此可以总结得到
寄存器: IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
基地址: 0x20E0068
初始化值: 0x5 # 低四位为0101,其他位不变
3、初始化GPIO(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03)
上一步其实已经公布了接下来要做啥,没错,找到前缀为 IOMUXC_SW_PAD_CTL_PAD 而且跟GPIO相关的寄存器。(这个比较难找,在32.6.156,大概在1793页)接下来我们要逐一设置寄存器中的每一个字段。
0:SRE,表示电压转换速率,我们选择低转换速率,即设为 0
2-1:保留位
5-3:DSE,表示驱动能力,我们选择 R0/6,即设为 110
7-6:SPEED,表示速度。我们选择100M,即设为10
10-8:保留位
11:ODE,表示是否关闭开路输出,这里选择关闭,设为 0
12:PKE,pull / keeper 使能,这里选择打开,设为 1
13:PUE,选择 keeper功能,设为 0
15–14:PUS,选择上下拉,默认下拉,设为 00
16:HYS,禁用,设为 0(该字段用的比较少)
31-17:保留位
寄存器: IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
基地址: 0x020E02F4
初始值: 0x10B0
4、GPIO 输出(GPIOx_GDIR、GPIOx_DR)
我们回到第28章,GPIO一共有五组,每一组都有8个寄存器,每个寄存器对应着不同的功能(属性)。但是实际上我们只需要设置其中两个寄存器,即 GPIOx_DR 和 GPIOx_GDIR。
- GPIOx_DR:数据寄存器。用于输出或者读取数据,比如GPIO设置为输出,我们就可以使用该寄存器来让引脚输出高电平或者低电平。
- GPIOx_GDIR:方向寄存器。用于设置GPIO是输入还是输出。如果是输入,我们就可以从 GPIOx_DR 寄存器读取内容;如果是输出,我们就需要向 GPIOx_DR 寄存器输出内容。
- PSR:GPIO状态寄存器。读取相应的位即可获取对应的 GPIO 的状态。
- ICR1、ICR2:中断寄存器。用来配置中断的触发方式(上升沿触发、下降沿触发),ICR1 用于 IO0~15 的配置, ICR2 用于 IO16~31 的配置。
- IMR:中断使能寄存器。用于控制中断的使能和禁止。
- ISR:中断状态寄存器。
- EDGE_SEL:设置边缘中断。这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一 个 GPIO 对应一个位。如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降 沿(双边沿)触发
寄存器: GPIO1_GDIR
基地址: 0x209C004
初始值: 0x00000008 # 因为是GPIO1的第三个引脚设为输出,即第3 bit应为1(00001000)
寄存器: GPIO1_DR
基地址: 0x209C000
初始值: 0
LED0输出低电平的时候,二极管导通,此时灯会亮,所以寄存器 DR 的初始值应该为 0