接下来一段时间将要使用NXP的i.mx6ull芯片来进行学习,i.MX 6ULL是一个高功效、高性价比应用处理器系列,采用单个ARM Cortex-A7内核的先进实施,运行速度高达528 MHz。
当我们学习C语言的时候,我们会写个Hello程序,那当我们写ARM程序,也该有一个简单的程序引领我们入门,这个程序就是点亮LED。怎样去点亮一个LED呢?分为三步:
1.看原理图,确定控制LED的引脚;
2.看主芯片的芯片手册,确定如何设置控制这个引脚;
3.写程序。
GPIO操作基础
首先需要先了解i.mx6ull的GPIO操作方法,下图为GPIO的控制框图。要想使能一个特定GPIO口,需要对以下模块进行配置:
CCM:Clock Controller Module(时钟控制模块) ;
IOMUXC:IOMUX Controller,IO复用控制器 ;
GPIO:General-purpose input/output,通用的输入输出口。
接下来需要确定想要操作的GPIO口,这需要查看硬件电路图,电路图中不仅提供了GPIO的端口信息还提供了GPIO操作方式。例如,此篇我希望操作GPIO点亮LED,如下图:
这个LED用到了GPIO5_3引脚。在芯片手册里,这引脚的名字是GPIO5_IO03,可以根据名字搜到对应的寄存器。 当引脚输出低电平时,对应的LED被点亮,输出高电平时,LED熄灭。
寄存器操作
1. 使能GPIO5,操作CCM_CGR1寄存器,设置[31:30]为0b11即可使能GPIO5的时钟。
2.设置GPIO5_IO03为GPIO模式,操IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3寄存器。
3.设置GPIO5_IO03为输出引脚,设置其输出电平;设置方向寄存器,把引脚设置为输出引脚,设置数据寄存器,设置引脚的输出电平。
代码分析
led.c
#include "led.h"
static volatile unsigned int *CCM_CCGR1 ;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR ;
static volatile unsigned int *GPIO5_DR ;
void led_init(void)
{
unsigned int val;
CCM_CCGR1 = (volatile unsigned int *)(0x20C406C);
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = (volatile unsigned int *)(0x2290014);
GPIO5_GDIR = (volatile unsigned int *)(0x020AC000 + 0x4);
GPIO5_DR = (volatile unsigned int *)(0x020AC000);
/* GPIO5_IO03 */
/* a. 使能GPIO5
* set CCM to enable GPIO5
* CCM_CCGR1[CG15] 0x20C406C
* bit[31:30] = 0b11
*/
*CCM_CCGR1 |= (3<<30);
/* b. 设置GPIO5_IO03用于GPIO
* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3
* to configure GPIO5_IO03 as GPIO
* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014
* bit[3:0] = 0b0101 alt5
*/
val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
val &= ~(0xf);
val |= (5);
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;
/* c. 设置GPIO5_IO03作为output引脚
* set GPIO5_GDIR to configure GPIO5_IO03 as output
* GPIO5_GDIR 0x020AC000 + 0x4
* bit[3] = 0b1
*/
*GPIO5_GDIR |= (1<<3);
}
void led_ctl(int on)
{
if (on) /* on: output 0*/
{
/* d. 设置GPIO5_DR输出低电平
* set GPIO5_DR to configure GPIO5_IO03 output 0
* GPIO5_DR 0x020AC000 + 0
* bit[3] = 0b0
*/
*GPIO5_DR &= ~(1<<3);
}
else /* off: output 1*/
{
/* e. 设置GPIO5_IO3输出高电平
* set GPIO5_DR to configure GPIO5_IO03 output 1
* GPIO5_DR 0x020AC000 + 0
* bit[3] = 0b1
*/
*GPIO5_DR |= (1<<3);
}
}
main.c
#include "led.h"
void delay(volatile unsigned int d)
{
while(d--);
}
int main()
{
led_init();
while(1)
{
led_ctl(1);
delay(1000000);
led_ctl(0);
delay(1000000);
}
return 0;
}
start.s
.text
.global _start
_start:
//设置栈
//将 0x80200000 赋值给寄存器 sp,即设置栈地址,因为 C 语言函数调用时,保存现场/上
//下文和传递参数需要用到栈
ldr sp,=0x80200000
bl clean_bss
//进入 C 语言的 main()函数,并将 b halt 指令地址存储到寄存器 lr 中
bl main
halt:
b halt
clean_bss:
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =__bss_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
bne clean
mov pc, lr
Makefile:
PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump
led.img : start.S led.c main.c
$(CC) -nostdlib -g -c -o start.o start.S
$(CC) -nostdlib -g -c -o led.o led.c
$(CC) -nostdlib -g -c -o main.o main.c
$(LD) -T imx6ull.lds -g start.o led.o main.o -o led.elf
$(OBJCOPY) -O binary -S led.elf led.bin
$(OBJDUMP) -D -m arm led.elf > led.dis
./tools/mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80100000 -d led.bin led.imx
dd if=/dev/zero of=1k.bin bs=1024 count=1
cat 1k.bin led.imx > led.img
clean:
rm -f led.dis led.bin led.elf led.imx led.img *.o
操作步骤
使用mobaxtrm登录虚拟机中,进行编译,将编译得到的可执行文件通过filezilla回传至windows端,使用烧录工具烧录至板载EMMC中进行运行。
https://www.zhihu.com/video/1243253652417740800