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架构中一种特权模式,通常用于操作系统内核或系统初始化代码。
逐行解释:
mrs r0, cpsr
MRS是“Move from Status Register”的缩写,用于将状态寄存器的值读取到通用寄存器。- 这里将 CPSR(当前程序状态寄存器) 的值读取到
r0寄存器。- CPSR 是ARM处理器的核心状态寄存器,其中包含:
- 处理器模式(低5位,
M[4:0])- 中断禁止位(
I位和F位)- 条件标志位(
N、Z、C、V等)
bic r0, r0, #0x1f
BIC是“Bit Clear”的缩写,用于清除寄存器中的特定位(按位与非操作)。#0x1f是二进制00011111,对应 CPSR 的低5位(M[4:0],处理器模式位)。- 这行代码的作用是将
r0中低5位(原CPSR的模式位)清零,为后续设置新模式做准备。
orr r0, r0, #0x13
ORR是“Bitwise OR”的缩写,用于设置寄存器中的特定位(按位或操作)。#0x13是二进制00010011,对应 ARM 处理器的 SVC模式编码(M[4:0] = 0b10011)。- 这行代码将
r0的低5位设置为0x13,即指定新的处理器模式为 SVC 模式。
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
3897

被折叠的 条评论
为什么被折叠?



