IMX6ULL的IO分为两类:SNVS域和通用,这两类IO本质上是一样的。以IOMUXC_SW_MUC_CTL_PAD_GPIO1_IO01为例:后面的GPIO1_IO01就是GPIO命名
SW_MUX_CTL寄存器是用来配置IO复用功能的
- MUX_MODE(bit0~bit3):用来设置GPIO1_IO00的复用功能
SW_PAD_CTL寄存器是用来配置IO口参数的
- HYS:使能或禁止迟滞比较器
- PUS:设置上下拉电阻
- PUE:设置IO使用上下拉还是状态保持器(IO作为输入)
- PKE:使能或禁止上下拉/状态保持器功能
- ODE:使能或禁止开路输出(IO作为输出)
- SPEED:设置IO速度(IO作为输出)
- DSE:设置IO的驱动能力(IO作为输出)
- SRE:设置压摆率
上面两种类型的寄存器是配置IO的,不是GPIO,GPIO是IO口众多复用功能中的一种。如果我们要使用GPIO来点灯或作为按键输入功能,在将IO复用为GPIO之后,还需要对GPIO的功能进行配置
GPIO结构图如下示
由上图可以看出使用GPIO功能,除了配置IO复用和IO属性寄存器外,还需要设置DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR、ISR这八个寄存器
当设置为输出模式,向指定的位写入数据,相应的IO就会输出相应的高低电平
当设置为输入模式,比如GPIO1_IO00引脚接地的话,GPIO1.DR的bit0就是0
若要设置GPIO1_IO00为输入,则GPIO1.GDIR = 0;反之GPIO1.GDIR = 1
功能类似于输入状态下的DR寄存器
例如设置GPIO1_IO15为上升沿触发中断,则GPIO1.ICR1 = 2 << 30
例如要使能GPIO1_IO00的中断,则GPIO1.MIR = 1
可通过读取ISR寄存器来判断GPIO中断是否发生,这些位相当于中断标志位
处理完中断后,必须清除中断标志位
IMX6U的每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。CCM有CCM_CCGR0~CCM_CCGR6这7个外设时钟使能寄存器,这7个寄存器控制着IMX6U的所有外设时钟开关
以CCM_CCGR0为例,其结构体如下图示:
CCM_CCGR0是个32位寄存器,每2位控制一个外设时钟,两个位就有四种操作方式
例如打开GPIO2的外设时钟,设置CCM_CCGR0 = 3<<30 即可
IMX6U的IO最为GPIO使用,需要进行以下几个步骤:
开发板LED0接到了GPIO_3上,且上拉到3.3V上,因此GPIO输出高电平时,LED0不会被点亮,输出低电平时,LED0被点亮
要使用GPIO1_IO03,需按如下步骤进行配置
/***汇编文件led.s***/
.global _start /* 全局标号 */
/*_start函数,程序从此函数开始执行 */
_start:
/* 1、使能所有时钟 */
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]
/* 2、设置GPIO1_IO03复用为GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */
ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
str r1,[r0]
/* 3、配置GPIO1_IO03的IO属性 */
ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]
/* 4、设置GPIO1_IO03为输出 */
ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */
ldr r1, =0X0000008
str r1,[r0]
/* 5、打开LED0,设置GPIO1_IO03输出低电平 */
ldr r0, =0X0209C000 /*寄存器GPIO1_DR */
ldr r1, =0
str r1,[r0]
/* loop死循环 */
loop:
b loop
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
//-g 产生调试信息
//-c 编译源文件,但是不链接
//-o 指定编译产生的文件名字
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
//-Ttext 指定链接地址
//-o 指定链接生成的elf文件名
//用于将.o文件链接到一个指定的链接位置
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
//-O 指定输出格式,binary表示以二进制格式输出
//-S 不要复制源文件中的重定位和符号信息
//-g 不复制源文件中的调试信息
//用于将.elf文件转换为.bin文件
arm-linux-gnueabihf-objdump -D led.elf > led.dis
//-D 表示反汇编所有的段
//用于将.elf文件反汇编,并生成.dis文件
使用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
arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
rm -rf *.o led.bin led.elf led.dis
chmod 777 imxdownload
ls /dev/sd* #查看当前电脑的存储文件
./imxdownload led.bin /dev/sdcard
//烧写完成后会生成一个load.imx文件
//该文件是imxdownload在.bin文件前面添加一些数据后生成的
//最终烧写到SD卡里面的就是这个文件
代码烧写到SD卡后,将SD卡插到开发板卡槽中,设置拨码开关为SD卡启动,启动后如果代码运行正常的话LED0会被点亮
正点原子阿尔法开发板的拨码开关如下图所示,不同的拨码位置表示不同的启动方式:
01xxxxxx 串行下载,可通过USB烧写镜像文件
10000010 SD卡启动
10100110 EMMC启动
10001001 NAND FLASH启动
IMX6U不能直接烧写编译生成的BIN文件,需要在BIN文件前面添加一些头信息构成最终可烧写文件,该文件组成如下:
IVT包含了镜像程序的入口点、指向DCD的指针和一些其他用途的指针。IVT要求laod.imx在烧写的时候放到指定的位置,不同的启动设备位置不同,整个位置都是相对于存储设备的起始地址的偏移
Boot Data包含了整个load.imx的起始地址、镜像大小等
芯片复位后所有的寄存器都会复位为默认值,但往往这些值不是我们需要的值,并且有的外设必须要在使用之前初始化(比如时钟、初始化DDR等)。DCD就是寄存器地址和对应的配置集合,Boot ROM会使用这个集合来初始化相应的寄存器
因此使用 imxdownload 将编译出来的.bin文件烧写到SD卡里的load.imx文件,其实就是 IVT + Boot data + DCD + .bin 组成的