IMX6ULL裸机编程 - C语言LED

IMX6ULL裸机编程: C语言LED

1. 通过汇编准备C语言运行环境

start.s

.global _start  		/* 全局标号 */

/* _start函数,程序从此函数开始执行,此函数主要功能是设置C运行环境。*/
_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函数 		 */

这段汇编代码的作用是将ARM处理器切换到SVC模式(Supervisor Mode,管理模式)
这是ARM架构中一种特权模式,通常用于操作系统内核或系统初始化代码。

逐行解释:
  1. mrs r0, cpsr

    • MRS 是“Move from Status Register”的缩写,用于将状态寄存器的值读取到通用寄存器。
    • 这里将 CPSR(当前程序状态寄存器) 的值读取到 r0 寄存器。
    • CPSR 是ARM处理器的核心状态寄存器,其中包含:
      • 处理器模式(低5位,M[4:0]
      • 中断禁止位(I 位和 F 位)
      • 条件标志位(NZCV 等)
  2. bic r0, r0, #0x1f

    • BIC 是“Bit Clear”的缩写,用于清除寄存器中的特定位(按位与非操作)。
    • #0x1f 是二进制 00011111,对应 CPSR 的低5位(M[4:0],处理器模式位)。
    • 这行代码的作用是r0 中低5位(原CPSR的模式位)清零,为后续设置新模式做准备。
  3. orr r0, r0, #0x13

    • ORR 是“Bitwise OR”的缩写,用于设置寄存器中的特定位(按位或操作)。
    • #0x13 是二进制 00010011,对应 ARM 处理器的 SVC模式编码M[4:0] = 0b10011)。
    • 这行代码将 r0 的低5位设置为 0x13,即指定新的处理器模式为 SVC 模式。
  4. msr cpsr, r0

    • MSR 是“Move to Status Register”的缩写,用于将通用寄存器的值写入状态寄存器。
    • 这里将修改后的 r0(包含 SVC 模式设置)写回 CPSR,完成处理器模式的切换
为什么要进入SVC模式?
  • SVC模式是ARM的特权模式,拥有访问所有系统资源的权限(如修改特殊寄存器、配置硬件等)。
  • 程序启动时(如从复位向量进入),处理器通常处于特殊模式(如复位模式),需要切换到SVC模式以执行初始化操作(如设置栈指针、配置中断等)。
  • 后续的 ldr sp, =0X80200000(设置栈指针)和 b main(跳转到C语言 main 函数)都需要在特权模式下执行,确保初始化操作的合法性。

总结:这段代码通过修改CPSR寄存器的模式位,将处理器从初始模式切换到SVC模式,为后续的系统初始化和C程序运行提供合适的特权环境。

2. 定义外设寄存器地址

imx6ull.h

#ifndef IMX6ULL_H
#define IMX6ULL_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)

#endif

3. main.c

main.c

#include "imx6ull.h"

/* 使能I.MX6U所有外设时钟 */
void clk_enable(void)
{
	CCM_CCGR0 = 0xffffffff;
	CCM_CCGR1 = 0xffffffff;
	CCM_CCGR2 = 0xffffffff;
	CCM_CCGR3 = 0xffffffff;
	CCM_CCGR4 = 0xffffffff;
	CCM_CCGR5 = 0xffffffff;
	CCM_CCGR6 = 0xffffffff;
}

/* 初始化LED对应的GPIO */
void led_init(void)
{
	/* 1. 初始化IO复用 */
	SW_MUX_GPIO1_IO03 = 0x5;	/* 复用为GPIO1_IO03 */

	/*
	 * 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 = (1<<12) | (2<<6) | (6<<3);

	/* 3. 初始化GPIO */
	GPIO1_GDIR |= (1<<3);

	/* 4. 设置GPIO1_IO03输出低电平,打开LED0 */
	GPIO1_DR &= ~(1<<3);
}

/* LED开关灯 */
void led_on(int flag)
{
    if (flag) {
        GPIO1_DR &= ~(1<<3);
    } else {
        GPIO1_DR |= (1<<3);
    }
}


void delay_short(volatile unsigned int n)
{
	while (n--) {
    }
}

void delay_ms(volatile unsigned int ms)
{
	while (ms--) {
        /* 在396Mhz的主频下, 延时时间大约为1ms */
		delay_short(0x7ff);
	}
}

int main(void)
{
	clk_enable();
	led_init();

	while(1) {
		led_on(0);
		delay_ms(500);

		led_on(1);
		delay_ms(500);
	}
}

4. 链接脚本

imx6ull.ld

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 = .;
}
khd@pc:~/linux/IMX6ULL/Board-Drivers/2-ledc$ arm-linux-gnueabihf-objdump -h start.o

start.o:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000001c  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  00000000  00000000  00000050  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000050  2**0
                  ALLOC
  3 .ARM.attributes 0000001f  00000000  00000000  00000050  2**0
                  CONTENTS, READONLY
khd@pc:~/linux/IMX6ULL/Board-Drivers/2-ledc$ arm-linux-gnueabihf-objdump -h main.o

main.o:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000000e8  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  00000000  00000000  0000011c  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  0000011c  2**0
                  ALLOC
  3 .text.startup 000000d0  00000000  00000000  0000011c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  4 .comment      00000025  00000000  00000000  000001ec  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  00000000  00000000  00000211  2**0
                  CONTENTS, READONLY
  6 .ARM.attributes 00000035  00000000  00000000  00000211  2**0
                  CONTENTS, READONLY

可以看到目标.text段只会收集start.o和main.o的.text段, 然后收集其他文件的.text段
所以最终main.o的.text.startup段没有被收集

5. 编译脚本

Makefile

objs := start.o main.o

ledc.bin: $(objs)
	arm-linux-gnueabihf-ld -Timx6ull.ld -o ledc.elf $^
	arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
	arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis

%.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 ledc.bin ledc.elf ledc.dis load.imx
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值