嵌入式Linux学习之旅(4)— imx6ull裸机点亮第一个LED灯

imx6ull裸机点亮第一个LED灯

首先,创建nonos/01_led/文件夹存放裸机点亮LED的代码。

一、编写汇编启动代码

CPU刚启动时没有设置栈,不能运行C语言,所以需要通过汇编来完成C语言的初始化,然后跳转到C语言区执行,一般称之为汇编启动文件,新建start.S汇编启动文件,内容如下。首先,设置栈指针,因为开发板上的DDR的地址范围为0X80000000~0X9FFFFFFF,然后我们设置栈首地址为0X80200000,这样其大小为2MB,因为IMX6UL的堆栈是向下增长的!我们需要注意的是,堆栈指针地址一定要是4字节地址对齐的!!!

/* 文件名:start.S
 * 描述:启动文件,完成C环境初始化, 然后跳转到C代码运行 */

.text
.global _start  		/* 全局标号 */

/* 程序从此处开始执行 */
_start:
	/* 设置栈,栈首地址为0X80200000,大小为2MB */
	ldr sp,=0X80200000

    /* 清除BSS段 */
    bl clean_bss

    /* 跳转到main函数 */
	bl main				

halt:
	b  halt 

clean_bss:
	ldr r1, =__bss_start
	ldr r2, =__bss_end
	mov r3, #0
	cmp r1, r2
	bne clean
	mov pc, lr
clean:
	str r3, [r1]
	add r1, r1, #4
	cmp r1, r2
	bne clean
	mov pc, lr

二、编写C语言代码

1、新建led.h文件,填入以下内容

#ifndef   __LED_H__
#define   __LED_H__

/* 初始化LED引脚 */
void led_init(void);

/* 设置LED状态 */
void led_ctl(int on);

/* ms延时函数 */
void delay_ms(volatile unsigned int n);

#endif

2、新建led.c文件
首先,宏定义我们需要使用的寄存器,然后实现和LED控制相关的几个函数,代码如下

#define CCM_CCGR1 			((volatile unsigned int *)0X020C406C)
#define SW_MUX_GPIO1_IO03 	((volatile unsigned int *)0X020E0068)
#define GPIO1_DR 			((volatile unsigned int *)0X0209C000)
#define GPIO1_GDIR 			((volatile unsigned int *)0X0209C004)


/* 描  述:初始化LED引脚,就是把它设置为输出引脚
 * 参  数:无
 * 返回值:无                               */
void led_init(void)
{
	unsigned int val;

	/* a. 使能GPIO1, bit[27:26] --> gpio1_clock */
	*CCM_CCGR1 |= (3<<26);
	
	/* b. 设置GPIO1_IO03用于GPIO */
	val = *SW_MUX_GPIO1_IO03;
	val &= ~(0xf);
	val |= (5);
	*SW_MUX_GPIO1_IO03 = val;
	
	/* c. 设置GPIO5_IO03作为output引脚 */
	*GPIO1_GDIR |= (1<<3);
}


/* 描  述: 设置LED状态
 * 参  数: on : 1-LED点亮, 0-LED熄灭
 * 返回值: 无                      */
void led_ctl(int on)
{
	if (on) /* on: output 0*/
	{
		/* 将GPIO1_DR的bit3清零,输出低电平 */
        *GPIO1_DR &= ~(1<<3); 
	}
	else  /* off: output 1*/
	{
		/* 将GPIO1_DR的bit3置1,输出高电平 */ 
		*GPIO1_DR |= (1<<3);
	}
}

/* 描  述: 短时间延时函数
 * 参  数: 要延时循环次数(空操作循环次数)
 * 返回值: 无                           */
void delay_short(volatile unsigned int n)
{
	while(n--){}
}

/* 描  述: ms延时函数,在396Mhz的主频下,延时时间大约为1ms
 * 参  数: 要延时的ms数
 * 返回值: 无                                       */
void delay_ms(volatile unsigned int n)
{
	while(n--)
	{
		delay_short(0x7ff);
	}
}

3、接下来就是编写主函数了,新建main.c文件

#include "led.h"

int main(void)
{
	led_init();			/* 初始化led 		*/
	
	while(1)			/* 死循环 			*/
	{		
		led_ctl(0);		/* 关闭LED   		*/
		delay_ms(500);		/* 延时大约500ms	*/
	
		led_ctl(1);		/* 打开LED			*/
		delay_ms(500);		/* 延时大约500ms	*/
	}

	return 0;
}

三、编写链接脚本和Makefile

1、编写连接脚本文件 touch imx6ul.lds

SECTIONS {
    . = 0x80100000;

    . = ALIGN(4);
    .text      :
    {
      *(.text)
    }

    . = ALIGN(4);
    .rodata : { *(.rodata) }

    . = ALIGN(4);
    .data : { *(.data) }

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    __bss_end = .;
}

2、编写Makefile文件 touch Makefile

objs := start.o led.o main.o

led.bin:$(objs)
	arm-linux-gnueabihf-ld -Timx6ull.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 -O2 -o $@ $<
	
%.o:%.S
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
	
%.o:%.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
	
clean:
	rm -rf *.o led.bin led.elf led.dis

四、生成工程

代码编写完成后,使用make命令对工程进行编译链接
嵌入式Linux学习之旅(4)— imx6ull裸机点亮第一个LED灯_第1张图片
可以看到,生成了led.bin

五、烧录工程

将正点原子提供的imxdownload文件复制到工程目录下,并修改其权限chmod 777 imxdownload
然后使用ls /dev/sd*命令找到sd卡
在这里插入图片描述
通过插入SD卡前后的对比,可以看到我们要使用的SD卡为sdb,然后用imxdownlord将led.bin烧写到sd卡中

./imxdownload led.bin /dev/sdb

烧写后可看到
嵌入式Linux学习之旅(4)— imx6ull裸机点亮第一个LED灯_第2张图片
要注意其烧写速度,如果烧写速度大于几十MB/s,那么就是烧写失败了,可以尝试重启Ubuntu系统。
另外可以看到,烧写完后再当前目录下会生成一个load.imx的文件,这个文件是软件imxdownlord根据NXP官方启动方式,再ledc.bin文件前面添加了一些数据头生成的,最终写到SD卡里的也是这个load.imx文件。

六、开发板启动

将SD卡插到开发板的SD卡槽中,并设置拨码开关为SD卡启动

然后上电可以看到LED闪烁

七、代码

完整代码在:https://gitee.com/william_william/imx6u_alpha.git

你可能感兴趣的:(嵌入式Linux)