IMX6ULL裸机开发
从裸机到系统
第一章 ARM汇编基础,寄存器及汇编基础指令讲解
第二章 IMX6LL裸机点亮一颗LED
IMX6LL裸机点亮一颗LED
前言
励志做一名合格的嵌入式Linux软件工程师,努力学习驱动和应用开发。
一、添加启动文件
启动文件一般使用汇编语言编写,对cortex-A寄存器CPSR(程序状态寄存器)进行设置,进入SVC模式,初始化栈指针,跳转到main()函数执行。
新建 start.s文件
.global _start
_start:
mrs r0, cpsr
bic r0, r0, #0x1f //将r0的低5bit清0
orr r0, r0, #0x13 //使用svc模式
msr cpsr, r0 //将r0的数据写到cpsr中
ldr sp, =0X80200000 //设置栈指针
b main //跳转到main函数
二、原理图分析
可以看出LED0连接在GPIO_3引脚上,同时接了一个上拉电阻将电平拉高,所以当GPIO_3拉低时,灯亮,反之灯灭。
三、查找IMX6ULL参考手册
找到第32章Chapter 32: IOMUX Controller (IOMUXC)
32.6小节:IOMUXC Memory Map/Register Definition
IOMUXC
找到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03此寄存器设置IO复用,此处复用为GPIO1_IO03
找到IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03此寄存器设置GPIO1_IO03的电气属性
1、 输出缓冲区
当输出缓冲区使能时,引脚被配置为输出模式。在输出缓冲区中,又包含了如下的属性配置:
• DSE 驱动能力
DSE 可以调整芯片内部与引脚串联电阻 R0 的大小,从而改变引脚的驱动能力。例如,R0 的初始值为 260 欧姆,在 3.3V 电压下其电流驱动能力为 12.69mA,通过 DSE 可以把 R0 的值配置为原值的 1/2、1/3⋯1/7 等。
• SRE 压摆率配置
• SPEED 带宽配置
• ODE 开漏输出配置
2、 输入缓冲区
• HYS 滞后使能
• PUS 上下拉配置
PUS 可配置项可选为 100K 欧下拉以及 22K 欧、47K 欧及 100K 欧上拉。
• PUE 上下拉、保持器选择
上下拉功能和保持器功能是二选一的,可以通过 PUE 来选择。
• PKE 上下拉、保持器配置
上下拉功能和保持器还通过 PKE 来控制是否使能。
注意,当引脚被配置为输出模式时,不管上下拉、保持器是什么配置,它们都会被关闭。
找到具体的GPIO后,我们还得要打开时钟驱动GPIO工作
找到第18章Chapter 18: Clock Controller Module (CCM)
第18.6小节CCM Memory Map/Register Definition
CCM
找到CCM_CCGR1 使能gpio 1 clock
四、代码编写
经过上面的分析,找到我们需要用到的寄存器地址:
CCM_CCGR1 0X020C406C
SW_MUX_GPIO1_IO03 0X020E0068
SW_PAD_GPIO1_IO03 0X020E02F4
GPIO1_DR 0X0209C000
GPIO1_GDIR 0X0209C004
GPIO1_PSR 0X0209C008
GPIO1_ICR1 0X0209C008
GPIO1_ICR2 0X0209C008
GPIO1_IMR 0X0209C008
GPIO1_ISR 0X0209C008
GPIO1_EDGE_SEL 0X0209C008
新建led.h定义寄存器
#ifndef _LED_H_
#define _LED_H_
/*
* CCM 相关寄存器地址
*/
#define CCM_CCGR0 *((volatile unsigned int *)0X020C4068)
#define CCM_CCGR1 *((volatile unsigned int *)0X020C406C)
#define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)
#define CCM_CCGR3 *((volatile unsigned int *)0X020C4074)
#define CCM_CCGR4 *((volatile unsigned int *)0X020C4078)
#define CCM_CCGR5 *((volatile unsigned int *)0X020C407C)
#define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)
/*
* IOMUX 相关寄存器地址
*/
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)
/*
* GPIO1 相关寄存器地址
*/
#define GPIO1_DR *((volatile unsigned int *)0X0209C000)
#define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)
#define GPIO1_PSR *((volatile unsigned int *)0X0209C008)
#define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)
#define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)
#define GPIO1_IMR *((volatile unsigned int *)0X0209C014)
#define GPIO1_ISR *((volatile unsigned int *)0X0209C018)
#define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)
#endif // !_LED_H_
新建led.c
#include "led.h"
void clk_enable(void)
{
CCM_CCGR1 = 0xffffffff;
}
void led_init(void)
{
//复用IO为GPIO1——IO03
SW_MUX_GPIO1_IO03 = 0x5;
/* 2、配置 GPIO1_IO03 的 IO 属性
*bit 16:0 HYS 关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper 功能
*bit [12]: 1 pull/keeper 使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度 100Mhz
*bit [5:3]: 110 R0/6 驱动能力
*bit [0]: 0 低转换率
*/
SW_PAD_GPIO1_IO03 = 0x10B0;
//设置IO为输出
GPIO1_GDIR = 0x00000008;
//默认输出低电平
GPIO1_DR = 0x0;
}
void led_on(void)
{
//清零输出低电平
GPIO1_DR &= ~(1<<3);
}
void led_off(void)
{
//置位输出高电平
GPIO1_DR |= (1<<3);
}
void delay_short(volatile unsigned int n)
{
while(n--){}
}
void delay(volatile unsigned int n)
{
while (n--)
{
delay_short(0x7ff);
}
}
int main(void)
{
clk_enable();
led_init();
while (1)
{
/* code */
led_off();
delay(1000);
led_on();
delay(1000);
}
return 0;
}
新建Makefile
objs := start.o led.o
led.bin:$(objs)
arm-linux-gnueabihf-ld -Timx6ul.lds -o led.elf $^
arm-linux-gnueabihf-objcopy -O binary -S led.elf $@
arm-linux-gnueabihf-objdump -D -m arm led.elf > led.dis
%.o:%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o led.bin led.elf led.dis
$^ 依赖集合
$@ 目标集合
$< 依赖集合的第一个文件
总结
裸机就是使用操作寄存器的方法,控制GPIO等外设,当然也需要启动文件。启动文件一般是汇编写的,用于设置堆栈指针等