I.MX6U-ALPHA开发板(C语言版本LED驱动实验)

一、C语言运行环境测试

.global _start  		/* 全局标号 */

_start:
	/* 进入SVC模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4*/
	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中*/

	ldr sp, =0X80200000	/* 设置栈指针*/
	b main				/* 跳转到main函数 */

一、设置处理器模式

​ 设置6ULL处于SVC模式 下。设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0X13。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。

二、设置sp指针

​ Sp可以指向内部RAM,也可以指向DDR,我们将其指向DDR。Sp设置到哪里?512MB的范围0x80000000~0x9FFFFFFF。栈大小,0x200000=2MB。处理器栈增长方式,对于A7而言是向下增长的。设置sp指向0x80200000。

三、跳转到C语言

​ 使用b指令,跳转到C语言函数,比如main函数。

二、软件编写

#ifndef __MAIN_H
#define __MAIN_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
#include "main.h"
/*使能外设时钟*/
void clk_enable(void)
{
    CCM_CCGR1 = 0xFFFFFFFF;
    CCM_CCGR2 = 0xFFFFFFFF;
    CCM_CCGR3 = 0xFFFFFFFF;
    CCM_CCGR4 = 0xFFFFFFFF;
    CCM_CCGR5 = 0xFFFFFFFF;
    CCM_CCGR6 = 0xFFFFFFFF;
}

/*初始化LED*/
void led_init(void)
{
    SW_PAD_GPIO1_IO03 = 0x5;/*复用为GPIO1-IO03*/
    SW_PAD_GPIO1_IO03 = 0x10B0;/*设置GPIO1——IO03电气属性*/

    /*GPIO初始化*/
    GPIO1_GDIR = 0x8;/*设置为输出*/
    GPIO1_DR = 0x0;/*打开LED灯*/
}

/*短延时*/
void delay_short(volatile unsigned int n)
{
    while(n--){}
}

void delay(volatile unsigned int n)
{
    while(n--)
    {
        delay_short(0x77f); //1毫秒(396MHZ)
    }
}

void led_on(void)
{
    GPIO1_DR &= ~(1<<3);
}

void led_off(void)
{
    GPIO1_DR |= (1<<3);
}


int main(void)
{
    clk_enable();
    led_init();
    while(1)
    {
        led_on();
        delay(500);

        led_off();
        delay(500);
    }
    return 0;
}

三、链接脚本

​ 链接脚本描述了要连接的文件,以及链接顺序,和链接首地址
​ 链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接键入,也可以使用通配符“*”。最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。我们一般编译出来的代码都包含在 text、data、bss 和 rodata 这四个段内,

我们本试验的链接脚本要求如下:

①、链接起始地址为 0X87800000

②、start.o 要被链接到最开始的地方,因为 start.o 里面包含这第一个要执行的命令

根据要求,在 Makefile 同目录下新建一个名为“imx6ul.lds”的文件,然后在此文件里面输入如下所示代码:

SECTIONS{
	. = 0X87800000;
	.text :
	{
		start.o 
		main.o 
		*(.text)
	}
	.rodata ALIGN(4) : {*(.rodata*)}     
	.data ALIGN(4)   : { *(.data) }    
	__bss_start = .;    
	.bss ALIGN(4)  : { *(.bss)  *(COMMON) }    
	__bss_end = .;
}

​ 第 1 行我们先写了一个关键字“SECTIONS”,后面跟了一个大括号,这个大括号和第14行的大括号是一对,这是必须的。看起来就跟 C 语言里面的函数一样。

​ 第 2 行对一个特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为 0。我们要求代码链接到以 0X87800000为起始地址的地方,因此这一行给“.”赋值0X87800000,表示以0X87800000开始,后面的文件或者段都会以0X87800000为起始地址开始链接。

​ 第 3 行的“.text”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text段都放到“.text”中。

​ 第5行设置链接到开始位置的文件为start.o,因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。第 6 行是 main.o这个文件,其实可以不用写出来,因为 main.o 的位置就无所谓了,可以由编译器自行决定链接位置。

​ 第10行定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。ALIGN(4)是用来对“.data”这个段的起
始地址做字节对齐的,ALIGN(4)表示 4 字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。

​ 在第 11、13 行的__bss_start__bss_end是符号,第 11、13 这两行其实就是对这两个符号进行赋值,其值为定位符“.”,这两个符号用来保存.bss 段的起始地址和结束地址。.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋 0 即可完成清零。通过第 11、13 行代码,.bss 段的起始地址和结束地址就保存在了__bss_start__bss_end中,我们就可以直接在汇编或者C 文件里面使用这两个符号。

执行后LED闪动
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值