一、实现蜂鸣器功能
① 蜂鸣器功能实现步骤:
(1)查看原理图设计
(2)查阅IMX6ULL参考手册,按以下顺序配置:
- 开启外设时钟
- 设置引脚复用功能
- 配置引脚电气属性
- 设置GPIO方向寄存器
- 配置GPIO数据寄存器
② 在LED/bsp文件夹基础上添加beep.c、beep.h
//beep.h #ifndef __BEEP_H__ #define __BEEP_H__ extern void beep_init(void); extern void beep_on(void); extern void beep_off(void); extern void beep_nor(void); #endif //beep.c #include "beep.h" #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" void beep_init(void) { // 1. 复用功能 IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0); // 2. 电气属性 IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0x10B0); // 3. GPIO方向:输出模式 GPIO5->GDIR |= (1 << 1); } void beep_on(void) { GPIO5->DR &= ~(1 << 1); } void beep_off(void) { GPIO5->DR |= (1 << 1); } void beep_nor(void) { GPIO5->DR ^= (1 << 1); }③ main.c
//main.c #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" #include "led.h" #include "beep.h" void enable_clocks(void) { CCM->CCGR0 = 0xFFFFFFFF; CCM->CCGR1 = 0xFFFFFFFF; CCM->CCGR2 = 0xFFFFFFFF; CCM->CCGR3 = 0xFFFFFFFF; CCM->CCGR4 = 0xFFFFFFFF; CCM->CCGR5 = 0xFFFFFFFF; CCM->CCGR6 = 0xFFFFFFFF; } //延迟函数 void delay(unsigned int n) { while(n--); } int main(void) { //使能外设时钟 enable_clocks(); //led初始化 led_init(); //beep初始化 beep_init(); while(1) { led_nor(); beep_nor(); delay(0x5FFFF); } return 0; }
二、按键
2.1 IMX6ULL按键介绍
① 按键数量与功能:共 3 个按键(两红一黄),功能分工明确
- 左侧(on/off):低功耗控制按钮
- 中间(reset):系统复位按钮
- 右侧(KEY0):用户独立控制的试验按键<本次实验主要使用KEY0>
- 电平逻辑 :开关断开时,引脚保持高电平 ;开关按下时,引脚变为低电平(关键逻辑,代码设计核心依据)
② 核心寄存器参考手册
硬件模块 参考文档 核心章节 说明 引脚复用 《IMX6ULL 参考手册.pdf》 Chapter 32(IOMUX Controller) 配置 GPIO 功能复用 GPIO 控制 《IMX6ULL 参考手册.pdf》 Chapter 28(General Purpose Input/Output) GPIO 方向、数据读取 时钟控制 《IMX6ULL 参考手册.pdf》 Chapter 18(Clock Controller Module) 使能 GPIO 时钟 中断控制器 《ARM Generic Interrupt Controller V2.0.pdf》 第 23 页 GIC 中断分发与 CPU 接口 内核控制 《Cortex-A7 Technical Reference Manual.pdf》 Chapter 4(System Control) 协处理器 CP15 配置
2.2 轮询方式实现按键功能
① 轮询方式原理:循环检测GPIO引脚电平状态来判断按键动作,适用于逻辑简单且对实时性要求不高的场景。但当主程序存在大量耗时任务时,可能导致按键响应延迟(因此在汽车制动、工业急停等高实时性场景中禁止使用)。
② 代码实现步骤:
步骤 1:初始化配置(4 大核心模块)
(1)GPIO 时钟使能(CCM)
功能:使能 GPIO1 组时钟(默认时钟关闭,不使能则 GPIO 无响应)
关键寄存器:CCM_CCGR1(GPIO1 组共用该时钟门控)
(2)引脚复用配置(IOMUXC)
功能:将UART1_CTS_B引脚复用为GPIO1_IO18
关键寄存器:IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B(低 4 位控制复用模式)
(3)电气特性配置(PAD_CTL)
功能:配置引脚的上拉 / 下拉、速度、驱动能力等,确保电平稳定
关键寄存器:IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B(4)GPIO 方向配置(GDIR)
功能:设置 GPIO 为输入模式(读取按键电平)
关键寄存器:GPIO1->GDIR(bit18 对应 GPIO1_IO18,0 = 输入,1 = 输出)步骤 2:按键检测(轮询核心)
(1)功能:循环读取 GPIO 数据寄存器,判断按键按下 / 松开状态
关键寄存器:
GPIO1->PSR(bit18 对应引脚电平,1 = 高电平(断开),0 = 低电平(按下))③ 代码实现
//key.c #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" #include "key.h" void key_init(void) { //1.引脚复用 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0); //2.电气属性 IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0); //3.GPIO方向:输入模式 GPIO1->GDIR &= ~(1 << 18); } int key_press(void) { if((GPIO1->PSR & (1 << 18)) == 0) { return 1; } else { return 0; } } //main.c #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" #include "led.h" #include "beep.h" #include "key.h" void enable_clocks(void) { CCM->CCGR0 = 0xFFFFFFFF; CCM->CCGR1 = 0xFFFFFFFF; CCM->CCGR2 = 0xFFFFFFFF; CCM->CCGR3 = 0xFFFFFFFF; CCM->CCGR4 = 0xFFFFFFFF; CCM->CCGR5 = 0xFFFFFFFF; CCM->CCGR6 = 0xFFFFFFFF; } //延迟函数 void delay(unsigned int n) { while(n--); } int main(void) { //使能外设时钟 enable_clocks(); //led初始化 led_init(); //蜂鸣器beep初始化 beep_init(); //按键初始化 key_init(); led_off(); beep_off(); while(1) { if(key_press()) { led_on(); beep_on(); } else { led_off(); beep_off(); } } return 0; }
2.3 中断方式实现按键功能
① 中断方式原理:当按键按下时,GPIO 触发外部中断(EINT),CPU 暂停当前任务,优先执行中断服务程序(ISR),待处理完成后恢复原任务,从而避免轮询方式导致的漏检并提升实时性。
② 中断处理流程(6 步核心)
- 中断源发出中断请求:按键按下 --> GPIO 引脚电平变化 --> 触发外部中断请求
- CPU检查是否响应中断及该中断是否被屏蔽:CPU 判断中断是否被屏蔽(GIC 中是否允许该中断)
- 比较中断优先级:GIC 分发器(Distributor)选择最高优先级中断
- 保护现场:保存当前 CPU 寄存器值(如 R0~R15),避免数据丢失
- 执行中断服务函数:处理按键逻辑
- 恢复现场:恢复保存的寄存器值,回到原业务继续执行
③ IMX6ULL ------ GPIO外设中断触发模式: 上升沿,下降沿,高电平和低电平触发
④ 中断实现方法
⑤ GIC 中断控制器
GIC:Generic Interrupt Controller(通用中断控制器)是为ARM内核提供服务的中断控制器,负责中断的分发与管理;IMX6ULL 中 GIC版本为 GICV2 ,型号(GIC-400),GIC最多能管理 1020 个中断号(ID0~ID1019),IMX6ULL支持160(0 - 159)个中断,分类如下:
(1)SGI(Software-generated Interrupt),软件中断:
由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
(2)PPI(Private Peripheral Interrupt),私有中断:
我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断;
(3)SPI(Shared Peripheral Interrupt),共享中断: (注意!不是 SPI 总线那个中断),这类中断泛指所有的外设中断。
中断类型 范围 特点 典型用途 SGI(软件中断) 0~15 软件触发,多核间通信 多核CPU同步(如核0通知核1) PPI(私有中断) 16~31 每个CPU独有,绑定核心 核内定时器、看门狗等 SPI(共享中断) 32~1019 多CPU共享,外设中断 GPIO、UART、SPI等外设中断 GIC版本介绍:
GICv1:初代基础版本,是ARM最早标准化的通用中断控制器,仅实现最基础的中断优先级管理、中断路由功能,没有多核支持、安全扩展这类高级特性。(CortexA5已弃用)
GICv2:32位多核标准版本,最多支持8个CPU核心,可管理1020个中断ID。该架构明确定义了SGI、PPI、SPI三类中断类型:0~15为SGI(软中断,软件生成中断),16~31为PPI(私有外设中断,核间通信,内核与内核之间中断),32~1019为SPI(共享外设中断),其中SPI可作为外部设备的中断源。(Cortex-A7、Cortex-A9)
GICv3:64位架构普及版本,重新设计了硬件架构,新增Redistributor(中断再分配器)组件,用层级亲和性标识CPU核心,理论支持最多2^32个处理器单元,新增LPI消息中断和ITS中断翻译服务,最大支持超1600万个中断ID,完全适配PCIe等高速外设的大量中断需求。
GICv4:虚拟化优化版本,完全兼容GICv3的所有功能,新增虚拟LPI中断直接注入能力,不需要虚拟机管理器中转就能把虚拟中断直接送到对应vCPU,大幅降低虚拟化场景下的中断开销,提升云服务器的多核调度效率。
GICv5:下一代全新架构版本,对整个中断处理链路做了全栈重新设计,原生支持大规模异构核心集群、硬件级中断负载均衡、实时中断确定性调度,专门适配大算力场景下数万级中断源的管理需求,是未来超算、AI服务器的标准中断控制器方案。
⑥ 协处理器
(1) 协处理器 是一种协助主处理器完成特定功能 的专用处理单元。在 ARM 架构中,协处理器通过一组专用的指令(MCR/MRC)和寄存器与主 CPU 交互,用于完成主 CPU 不擅长或效率不高的任务。
| 对比项 | MRC(Move to ARM Register from Coprocessor) | MCR(Move from ARM Register to Coprocessor) |
| 核心功能 | 从协处理器寄存器读取数据到 ARM 核心通用寄存器 | 从 ARM 核心通用寄存器写入数据到协处理器寄存器 |
| 数据流向 | 协处理器寄存器 → ARM 核心寄存器(读操作) | ARM 核心寄存器 → 协处理器寄存器(写操作) |
| 指令格式 |MRC{<cond>} <coproc>, <opcode1>, <Rd>, <Crn>, <Crm>{, <opcode2>}|MCR{<cond>} <coproc>, <opcode1>, <Rd>, <Crn>, <Crm>{, <opcode2>}|
| 关键字段含义 |<coproc>:协处理器编号(如 CP15 对应 15)<Rd>:目标 ARM 寄存器(数据读入此寄存器)<Crn>/<Crm>:协处理器主/次寄存器<opcode1>/<opcode2>:协处理器操作码 |<coproc>:协处理器编号(如 CP15 对应 15)<Rd>:源 ARM 寄存器(数据从此寄存器写出)<Crn>/<Crm>:协处理器主/次寄存器<opcode1>/<opcode2>:协处理器操作码 |
应用场景(CP15 示例) 1. 读取处理器 ID(c0/c0 寄存器) 2. 读取系统控制状态(如 MMU 使能状态,c1/c0 寄存器) 3. 读取中断屏蔽状态(c12/c0 寄存器) 4. 读取 Cache 配置信息(c9/c0 寄存器) 1. 使能 MMU(修改 c1/c0 寄存器的 M 位) 2. 设置中断向量表基址(修改 c12/c0 寄存器) 3. 刷新 Cache(操作 c7/c6 寄存器) 4. 配置内存权限(修改 c2/c0 寄存器) (2)ARM 协处理器编号ARM 架构最多支持 16 个协处理器 ,编号为 cp0 ~ cp15。
(3)协处理器 CP15 配置
CP15 是 Cortex-A7 的系统控制协处理器,负责中断向量表、缓存、MMU 等配置,中断相关核心寄存器如下:
寄存器名称 功能描述 关键配置项 代码示例 MIDR (c0) 主 ID 寄存器 内核型号、版本信息,通过 mrc p15,0,r0,c0,c0,0读取内核型号及版本信息调试时读取确认内核型号 SCTLR (c1) 系统控制寄存器 中断异常向量表基地址(V 位,第13位)、控制指令缓存开关(I 位,第12位) mrc p15,0,r0,c1,c0,0; bic r0,r0,#(1<<13);mcr p15,0,r0,c1,c0,0;(向量表基址0x00000000)VBAR (c12) 向量基址寄存器 中断向量表物理地址(重定向后的异常向量表基地址.) __set_VBAR(0x87800000);(设置向量表基址为0x87800000)CBAR (c15) 配置基址寄存器 GIC 寄存器物理基址 mrc p15,4,r0,c15,c0,0;(读取 GIC 基址)⑦ 中断方式代码实现
//key.c #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" #include "key.h" #include "core_ca7.h" #include "interrupt.h" #include "led.h" void key_interrupt_handler(void) { if((GPIO1->ISR & (1 << 18)) != 0) { led_nor(); GPIO1->ISR |= (1 << 18); //清除中断标志 } } void key_init(void) { //1.引脚复用 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0); //2.电气属性 IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0); //3.GPIO方向:输入模式 GPIO1->GDIR &= ~(1 << 18); //4.配置中断触发方式:下降沿 GPIO1->ICR2 |= (3 << 4); //5.使能GPIO1_18引脚中断 GPIO1->IMR |= (1 << 18); //6.注册按键中断 system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_interrupt_handler); //7.设置中断优先级 GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0); //8.GIC使能中断 GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); } int key_press(void) { if((GPIO1->PSR & (1 << 18)) == 0) { return 1; } else { return 0; } } //start.S .global _start _start: ldr pc, =_reset_handler ldr pc, =_undefine_handler ldr pc, =_svc_handler ldr pc, =_prefetch_aboart_handler ldr pc, =_data_abort_handler ldr pc, =_reserved_handler ldr pc, =_irq_handler ldr pc, =_fiq_handler _fiq_handler: ldr pc, =_fiq_handler _irq_handler: subs lr, lr, #4 //GIC基地址 mrc p15, 4, r1, c15, c0, 0 add r1, r1, #0x2000 ldr r0, [r1, #0x0c] //中断号 stmfd sp!,{r0-r12, lr} stmfd sp!, {r0, r1} cps #0x1F //切换到system模式,在执行c函数是可以被其他中断打断 stmfd sp!, {lr} bl system_interrupt_handler ldmfd sp!, {lr} cps #0x12 //切换到IRQ模式 ldmfd sp!, {r0, r1} str r0, [r1, #0x10] //清中断标志 ldmfd sp!, {r0-r12, pc}^ _reserved_handler: ldr pc, =_reserved_handler _data_abort_handler: ldr pc, =_data_abort_handler _prefetch_aboart_handler: ldr pc, =_prefetch_aboart_handler _svc_handler: ldr pc, =_svc_handler _undefine_handler: ldr pc, =_undefine_handler _reset_handler: //切换工作模式:IRQ //mrs r0, cpsr //bic r0, r0 ,#0x1F //orr r0, r0, #0x12 cps #0x12 //直接切换工作模式 cpsie i //使能IRQ中断 // bic r0, r0, #(1 << 7) //使能中断 //msr cpsr, r0 //设置IRQ模式下的栈 ldr sp, =0x86000000 //切换工作模式:System //mrs r0, cpsr // bic r0, r0, #0x1F // orr r0, r0, #0x1F //msr cpsr, r0 cps #0x1F //设置system模式下的栈 ldr sp, =0x84000000 //清空bss段和common段 bl _bss_clear //打开icache bl _enable_icache //设置异常向量表重映射后的地址 bl _set_vbar //跳转到main函数执行 b main b finished _enable_icache: mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #(1 << 13) orr r0, r0, #(1 << 12) mcr p15, 0, r0, c1, c0, 0 bx lr _set_vbar: ldr r0, =0x87800000 mcr p15, 0, r0, c12, c0, 0 bx lr _bss_clear: ldr r0, =__bss_start ldr r1, =__bss_end loop: mov r2, #0 str r2, [r0] add r0, r0, #4 cmp r0, r1 blt loop bx lr finished: b finished //interrupt.c #include "interrupt.h" #include "MCIMX6Y2.h" #include "core_ca7.h" irq_handler_t interrupt_vector_table[160]; void system_interrupt_init(void) { GIC_Init(); } //注册中断函数 void system_interrupt_register(int num, irq_handler_t handler) { interrupt_vector_table[num] = handler; } //irq中断服务函数 void system_interrupt_handler(int num) { if (interrupt_vector_table[num] != NULL) { interrupt_vector_table[num](); } } //interrupt.h #ifndef __INTERRUPT_H__ #define __INTERRUPE_H__ typedef void (*irq_handler_t) (void); extern void system_interrupt_init(void); extern void system_interrupt_register(int num, irq_handler_t handler); #endif //main.c #include "MCIMX6Y2.h" #include "fsl_iomuxc.h" #include "led.h" #include "beep.h" #include "key.h" #include "core_ca7.h" #include "interrupt.h" //延迟函数 void delay(unsigned int n) { while(n--); } void enable_clocks(void) { CCM->CCGR0 = 0xFFFFFFFF; CCM->CCGR1 = 0xFFFFFFFF; CCM->CCGR2 = 0xFFFFFFFF; CCM->CCGR3 = 0xFFFFFFFF; CCM->CCGR4 = 0xFFFFFFFF; CCM->CCGR5 = 0xFFFFFFFF; CCM->CCGR6 = 0xFFFFFFFF; } int main(void) { //使能外设时钟 enable_clocks(); //使能中断 system_interrupt_init(); //led初始化 led_init(); //蜂鸣器beep初始化 beep_init(); //按键初始化 key_init(); led_off(); beep_off(); while(1) { delay(0xFFFFFFFF); } return 0; }

⑤ GIC 中断控制器


