Linux开发——开发板介绍及裸机程序设计
本次Linux开发学习使用的开发板是来自正点原子的i.mx6ull开发板。
我们的linux开发主要聚焦内核开发,这块开发板采用 ARM Cortex-A7 内核,芯片内部集成了丰富的外设控制器,支持 DDR3,支持 LCD 显示屏;多种连接接口: 如双网口(支持百兆以太网)、USB OTG、多种串口(UART)、CAN 总线、I2C、SPI 等。满足复杂linux系统开发学习。
本次学习开发语言采用C语言。
.led点灯程序设计:
1.main.c
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E0068)
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E02F4)
#define GPIO1_GDIR (*(volatile unsigned int*)0x0209C004)
#define GPIO1_DR (*(volatile unsigned int*)0x0209C000)#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)struct GPIO_t
{unsigned int DR;unsigned int GDIR;unsigned int PSR;unsigned int ICR1;unsigned int ICR2;unsigned int IMR;unsigned int ISR;unsigned int EDGE_SEL;
};#define GPIO1 ((struct GPIO_t *)0x0209C000)
#define GPIO2 ((struct GPIO_t *)0x020A0000)void clock_init(void)
{CCM_CCGR0 = 0xFFFFFFFF;CCM_CCGR1 = 0xFFFFFFFF;CCM_CCGR2 = 0xFFFFFFFF;CCM_CCGR3 = 0xFFFFFFFF;CCM_CCGR4 = 0xFFFFFFFF;CCM_CCGR5 = 0xFFFFFFFF;CCM_CCGR6 = 0xFFFFFFFF;}void led_init(void)
{IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x5;IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0;GPIO1 -> GDIR |=(1<<3);
}
void led_on(void)
{GPIO1-> DR &=~(1<<3);
}
void led_off(void)
{GPIO1->DR |=(1<<3);
}
void led_delay(int t)
{while(t--);
}int main(void)
{clock_init();led_init();while (1){led_on();led_delay(0xFFFFFF);led_off();led_delay(0xFFFFFF);}return 0;
}
1. 寄存器定义部分
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E0068) #define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 (*(volatile unsigned int *)0x020E02F4)
引脚复用控制寄存器:配置 GPIO1_IO03 引脚的功能(设置为 GPIO 模式)
引脚属性控制寄存器:配置引脚的电气特性(如上拉/下拉、驱动强度等)
#define GPIO1_GDIR (*(volatile unsigned int*)0x0209C004) #define GPIO1_DR (*(volatile unsigned int*)0x0209C000)
GPIO 方向寄存器:控制引脚输入/输出方向
GPIO 数据寄存器:读取或写入引脚电平
#define CCM_CCGR0 ... #define CCM_CCGR6
时钟控制模块寄存器:控制各个外设模块的时钟使能
2. GPIO 结构体定义
struct GPIO_t {unsigned int DR; // 数据寄存器unsigned int GDIR; // 方向寄存器unsigned int PSR; // 引脚状态寄存器unsigned int ICR1; // 中断配置寄存器1unsigned int ICR2; // 中断配置寄存器2unsigned int IMR; // 中断掩码寄存器unsigned int ISR; // 中断状态寄存器unsigned int EDGE_SEL; // 边沿选择寄存器 };
将 GPIO 的所有寄存器映射到结构体,方便访问。
3. 功能函数
时钟初始化
void clock_init(void) {CCM_CCGR0 = 0xFFFFFFFF;// ... 其他 CCGR 寄存器CCM_CCGR6 = 0xFFFFFFFF; }
使能所有外设时钟,确保 GPIO 模块可以正常工作。
LED 初始化
void led_init(void) {IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x5; // 设置为 GPIO 功能IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0; // 配置引脚电气属性GPIO1->GDIR |= (1<<3); // 设置为输出模式 }
LED 控制函数
void led_on(void) { GPIO1->DR &= ~(1<<3); } // 第3位清零,LED亮 void led_off(void) { GPIO1->DR |= (1<<3); } // 第3位置1,LED灭
延时函数
void led_delay(int t) { while(t--); } // 简单软件延时
4. 主函数
int main(void) {clock_init(); // 初始化时钟led_init(); // 初始化LED引脚while (1) // 主循环{led_on(); // 点亮LEDled_delay(0xFFFFFF); // 延时led_off(); // 熄灭LED led_delay(0xFFFFFF); // 延时}return 0; }
2.start.S
.global _start_start:ldr pc, =_reset_handler_ldr pc, =_undef_handler_ldr pc, =_svc_handler_ldr pc, =_prefetch_handler_ldr pc, =_data_handler_nopldr pc, =_irq_handler_ldr pc, =_fiq_handler__undef_handler_:b _undef_handler__svc_handler_:b _svc_handler__prefetch_handler_:b _prefetch_handler__data_handler_:b _data_handler__irq_handler_:b _irq_handler__fiq_handler_:b _fiq_handler__reset_handler_:cpsid icps #0x12/*mrs r0, cpsrbic r0, r0, #(0x1F << 0)bic r0, r0, #(1 << 7) //使能irq中断orr r0, r0, #(0x12 << 0) //切换到irq模式msr cpsr, r0*/ldr sp, =0x82000000cps #0x1F/*mrs r0, cpsrorr r0, r0, #(0x1F << 0) //切换到system模式msr cpsr, r0*/ldr sp, =0x84000000cpsie ib main
_finish:b _finish
1. 异常向量表
assembly
复制下载
_start:ldr pc, =_reset_handler_ ; 复位异常ldr pc, =_undef_handler_ ; 未定义指令异常ldr pc, =_svc_handler_ ; 软件中断异常(SVC)ldr pc, =_prefetch_handler_ ; 预取指异常ldr pc, =_data_handler_ ; 数据访问异常nop ; 保留ldr pc, =_irq_handler_ ; 普通中断异常ldr pc, =_fiq_handler_ ; 快速中断异常
异常向量表位置:ARM 处理器在复位或发生异常时,会跳转到特定的地址执行相应的处理程序。
2. 异常处理程序
assembly
复制下载
_undef_handler_:b _undef_handler_ ; 未定义指令异常 - 死循环 _svc_handler_:b _svc_handler_ ; SVC 异常 - 死循环 _prefetch_handler_:b _prefetch_handler_ ; 预取指异常 - 死循环 _data_handler_:b _data_handler_ ; 数据异常 - 死循环 _irq_handler_:b _irq_handler_ ; 中断异常 - 死循环 _fiq_handler_:b _fiq_handler_ ; 快速中断 - 死循环
这些处理程序目前都是空实现,只是简单地在原地循环。在实际应用中需要根据具体需求实现相应的异常处理逻辑。
3. 复位处理程序(主程序入口)
assembly
复制下载
_reset_handler_:cpsid i ; 禁用中断cps #0x12 ; 切换到 IRQ 模式
处理器模式设置
cpsid i
:禁用 IRQ 中断cps #0x12
:切换到 IRQ 模式(中断模式)
栈指针设置
assembly
复制下载
ldr sp, =0x82000000 ; 设置 IRQ 模式的栈指针cps #0x1F ; 切换到 System 模式ldr sp, =0x84000000 ; 设置 System 模式的栈指针
栈内存布局:
IRQ 模式栈:0x82000000
System 模式栈:0x84000000
注释掉的代码说明
assembly
复制下载
/* mrs r0, cpsr bic r0, r0, #(0x1F << 0) bic r0, r0, #(1 << 7) // 使能 IRQ 中断 orr r0, r0, #(0x12 << 0) // 切换到 IRQ 模式 msr cpsr, r0 */
这是用传统方式修改 CPSR 寄存器来切换模式的代码,被更简洁的 cps
指令替代。
4. 主程序执行
assembly
复制下载
cpsie i ; 启用中断bl _led_init ; 调用 LED 初始化函数b _finish ; 跳转到结束循环b main ; 这行代码不会执行(被上一行跳转跳过)
5. 结束处理
assembly
复制下载
_finish:b _finish ; 无限循环,程序停留在这里
代码执行流程总结
上电复位 → 跳转到
_reset_handler_
初始化设置:
禁用中断
设置 IRQ 模式栈指针
设置 System 模式栈指针
启用中断
执行应用代码:调用
_led_init
进入死循环:程序执行完毕,停留在
_finish