当前位置: 首页 > news >正文

I.MX6ULL按键实现(轮询及中断)及工程优化

一、硬件介绍

1.1 按键硬件结构

  • 按键数量与功能:共 3 个按键(两红一黄),功能分工明确
    • 左侧(on/off):低功耗控制按钮
    • 中间(reset):系统复位按钮
    • 右侧(KEY0):用户独立控制的试验按键<本次实验主要使用KEY0>
  • 电平逻辑:开关断开时,引脚为高电平;开关按下时,引脚为低电平(关键逻辑,代码设计核心依据)

1.2 核心引脚与手册参考

硬件模块参考文档核心章节说明
引脚复用《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.1 轮询方式原理

通过循环读取 GPIO 引脚电平判断按键状态,适用于业务逻辑简单、无实时性要求的场景。但当主程序存在大量耗时业务时,会出现按键漏检问题(如汽车刹车、工业急停等实时场景禁用)。

2.2 代码实现

步骤 1:初始化配置(4 大核心模块)
(1)引脚复用配置(IOMUXC)
  • 功能:将UART1_CTS_B引脚复用为GPIO1_IO18
  • 关键寄存器:IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B(低 4 位控制复用模式)
(2)电气特性配置(PAD_CTL)
  • 功能:配置引脚的上拉 / 下拉、速度、驱动能力等,确保电平稳定
  • 关键寄存器:IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B

参数拆解(二进制对应位):

  • HYS=0:禁用输入滞回(无需防抖时关闭)
  • PUS=11:22KΩ 上拉(匹配 “断开高电平” 硬件逻辑)<与原理图中KEY0的上拉电阻并联后阻止小于10Ω>
  • PUE=1:选择上拉模式(而非保持模式)
  • PKE=1:使能上拉 / 保持功能
  • ODE=0:禁用漏极开漏(GPIO 输入模式无需开漏)
  • SPEED=10:中等速度(100MHz)
  • DSE=000:禁用输出驱动(输入模式无需驱动)
  • SRE=0:慢压摆率(减少信号干扰)
(3)GPIO 方向配置(GDIR)
  • 功能:设置 GPIO 为输入模式(读取按键电平)
  • 关键寄存器:GPIO1->GDIR(bit18 对应 GPIO1_IO18,0 = 输入,1 = 输出)
(4)GPIO 时钟使能(CCM)
  • 功能:使能 GPIO1 组时钟(默认时钟关闭,不使能则 GPIO 无响应)
  • 关键寄存器:CCM_CCGR1(GPIO1 组共用该时钟门控)
mrc与mcr命令
对比MRC(Move to ARM Register from Coprocessor)<“读”>MCR(Move from ARM Register to Coprocessor)<“写”>
核心功能协处理器寄存器的数据读取到ARM 核心通用寄存器ARM 核心通用寄存器的数据写入到协处理器寄存器
数据流向协处理器寄存器 → ARM 核心寄存器(读操作)ARM 核心寄存器 → 协处理器寄存器(写操作)
指令格式(ARM 汇编)MRC{<cond>} <coproc>, <opcode1>, <Rd>, <Crn>, <Crm>{, <opcode2>}MCR{<cond>} <coproc>, <opcode1>, <Rd>, <Crn>, <Crm>{, <opcode2>}
关键字段含义各字段功能与 MCR 完全一致,仅数据流向相反:
<coproc>:协处理器编号(如 CP15 对应 15)
<Rd>:目标 ARM 寄存器(数据读入此寄存器)
<Crn>/<Crm>:协处理器主 / 次寄存器
<opcode1>/<opcode2>:协处理器操作码
各字段功能与 MRC 完全一致,仅数据流向相反:
<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:按键检测(轮询核心)
  • 功能:循环读取 GPIO 数据寄存器,判断按键按下 / 松开状态
  • 关键寄存器:GPIO1->DR(bit18 对应引脚电平,1 = 高电平(断开),0 = 低电平(按下))
#include "key.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"void key_init(void)
{//复用功能 配置为GPIO1_18IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);GPIO1->GDIR &= ~(1 << 18);
}int key_check(void)
{return (GPIO1->DR & (1 << 18)) == 0;
}

2.3 轮询方式缺点

  • 漏检问题:当delay耗时操作执行时,主循环暂停,按键按下无法被检测
  • 资源占用:循环读取 GPIO 占用 CPU 资源,无法并行处理其他业务
  • 实时性差:不适用于汽车电子、工业控制等需毫秒级响应的场景

三、中断方式按键实现

3.1 中断方式原理

当按键按下时,GPIO 触发外部中断(EINT),CPU 暂停当前业务,优先执行中断服务函数(ISR),处理完成后返回原业务,解决轮询的漏检与实时性问题。

3.2 中断处理流程(6 步核心)

  1. 中断请求:按键按下  -->  GPIO 引脚电平变化  -->  触发外部中断请求
  2. 中断响应检查:CPU 判断中断是否被屏蔽(GIC 中是否允许该中断)
  3. 优先级判断:GIC 分发器(Distributor)选择最高优先级中断
  4. 保护现场:保存当前 CPU 寄存器值(如 R0~R15),避免数据丢失
  5. 执行中断服务函数:处理按键逻辑
  6. 恢复现场:恢复保存的寄存器值,回到原业务继续执行
kernal:被打断的
外设:  发出中断
GPIO发出的中断:外部中断(EINT)

3.3 GIC 中断控制器

GIC(Generic Interrupt Controller)是 ARM 架构的通用中断控制器,负责中断的分发与管理,IMX6ULL 中 GIC V2.0 支持 1020 个中断源,分类如下:

中断类型范围特点用途
SGI(软件中断)0~15软件触发,多核间通信多核 CPU 同步(如核 0 通知核 1)
PPI(私有中断)16~31每个 CPU 独有,绑定核心核内定时器、看门狗等
SPI(共享中断)32~1019多 CPU 共享,外设中断GPIO、UART、SPI 等外设中断
Distributor(分发器):
(1)SGI(Software-generated Interrupt),软件中断:由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
(2)PPI(Private Peripheral Interrupt),私有中断:我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断;
(3)SPI(Shared Peripheral Interrupt),共享中断:(注意!不是 SPI 总线那个中断),这类中断泛指所有的

3.4 协处理器 CP15 配置

CP15 是 Cortex-A7 的系统控制协处理器,负责中断向量表、缓存、MMU 等配置,中断相关核心寄存器如下:

寄存器功能关键配置代码示例
SCTLR(c1)系统控制寄存器中断向量表位置(V 位)、指令缓存(I 位)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 基址)
MIDR(c0)主 ID 寄存器内核型号、版本信息调试时确认内核型号

通过查阅手册,确定寄存器(以下图为例)

3.5 中断方式代码实现(分层设计)

  • 逻辑图
中断相关寄存器组
c0 registers:MIDR(Main ID Register):存储内核的一些基本信息
c1 registers:SCTLR(System Control Register)bit13:V	bit12:I			
c12 registers:VBAR(Vector Base Address Register)					
c15 registers:CBAR(Configuration Base Address Register)
步骤 1:中断初始化(GIC+GPIO + 向量表)
(1)初始化 GIC 中断控制器
(2)GPIO 中断配置(触发方式 + 使能)
(3)中断向量表配置
步骤 2:中断服务函数(ISR)
  • 功能:处理按键中断逻辑,需快速执行(避免阻塞其他中断)
步骤 3:主函数(业务逻辑 + 中断使能)
整体函数实现
  •  key.c
//key.c#include "key.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "core_ca7.h"void key_init(void)
{//复用功能 配置为GPIO1_18IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);//电气特性配置IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);//引脚方向GPIO1->GDIR &= ~(1 << 18);//中断触发方式配置GPIO1->ICR2 |= (3 << 4); //中断源屏蔽寄存器解除屏蔽GPIO1->IMR |= (1 <<18);//2.GIC中断使能GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0);
}int key_check(void)
{return (GPIO1->DR & (1 << 18)) == 0;
}
  • start.S
.global _start_start:ldr pc, = _start_handerldr pc, = _undef_handerldr pc, = _supervisor_handerldr pc, = _prefetch_handerldr pc, = _data_handerldr pc, = _notuse_handerldr pc, = _irq_handerldr pc, = _fiq_hander_undef_hander:b _undef_hander_supervisor_hander:b _supervisor_hander_prefetch_hander:b _prefetch_hander_data_hander:b _data_hander_notuse_hander:b _notuse_hander_irq_hander:sub lr, #4stmfd sp!, {r0-r12, lr}mrc p15, 4, r1, c15, c0, 0add r1, r1, #0x2000ldr r0, [r1, #0x0C]bl system_interrupt_handlerstr r0, [r1, #0x10]ldmfd sp!, {r0-r12, pc}^_fiq_hander:b _fiq_hander_start_hander:/* mrs r0, cpsrbic r0, r0, #(0x1f << 0)bic r0, r0, #(1 << 7)orr r0, r0, #(0x12 << 0)    //irqmrs cpsr, r0*/mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #(1 << 13)      //修改异常向量表映射方式orr r0, r0, #(1 << 12)      //打开ICachemcr p15, 0, r0, c1, c0, 0 cpsid icps #0x12ldr sp, =0x82000000/* mrs r0, cpsrorr r0, r0, #(0x1f << 0)    //sysmrs cpsr, r0*/cps #0x1F ldr sp, =0x84000000cpsie i bl _bss_initb mainb finishfinish:b finish_bss_init:ldr r0, =__bss_startldr r1, =__bss_end mov r2, #0
loop: str r2, [r0]add r0, #4cmp r0, r1blt loop bx lr

特别提醒:

启动代码中添加对IRQ中断的handler函数跳转;

在_start_hander中加入对异常向量表的修改函数

  • main.c
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "core_ca7.h"
#include "led.h"
#include "beep.h"
#include "key.h"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_delay(unsigned int t)
{while(t--);
}void system_interrupt_handler(IRQn_Type irq)
{if (irq == GPIO1_Combined_16_31_IRQn){if((GPIO1->ISR & (1 << 18)) !=0){led_nor();//中断处理GPIO1->ISR |= (1 << 18);   }}
}void system_interrupt_init(void)
{//基地址映射__set_VBAR(0x87800000);//1.GIC初始化GIC_Init();
}int main(void)
{system_interrupt_init();clock_init();led_init();beep_init();key_init();while (1){led_delay(0x7FFFFF);}
}

四、扩展性优化

4.1 遵循OCP原则进行优化

原始代码中,GPIO 配置、中断逻辑、业务处理高度耦合,修改按键引脚或业务时需大量改动代码,违反开放 - 封闭原则(OCP):对修改封闭,对扩展开放。

降低程序耦合性
(1)满足用户基本需求
(2)程序稳定可靠
(3)满足OCP(open close principle)原则对代码的修改是关闭的,对代码的拓展是开放的

4.2 分层封装 

模块分层封装(3 层架构)
层级功能接口设计职责边界
硬件抽象层(HAL)封装 GPIO、GIC 寄存器操作GPIO_SetMux()GIC_EnableInterrupt()与硬件强相关,不涉及业务
驱动层(Driver)封装按键驱动逻辑Key_Init()Key_RegisterCallback()处理中断触发、防抖,提供业务接口
应用层(App)实现业务逻辑Key_Press_Callback()Key_Release_Callback()只调用驱动接口,不关心硬件细节
KEY、中断模块优化
//interrupt.c#include "interrupt.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "core_ca7.h"static irq_interrupt_t interrupt_Vector_table[160] = {NULL};void system_interrupt_init(void)
{//基地址映射__set_VBAR(0x87800000);//1.GIC初始化GIC_Init();
}void system_interrupt_register(IRQn_Type irq, irq_interrupt_t handler)
{interrupt_Vector_table[irq] = handler;
}void system_interrupt_handler(IRQn_Type irq)
{if (interrupt_Vector_table[irq] != NULL){interrupt_Vector_table[irq]();}
}//key.c#include "key.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "interrupt.h"
#include "led.h"void key_16_31_handler(void)
{if((GPIO1->ISR & (1 << 18)) !=0){led_nor();//中断处理GPIO1->ISR |= (1 << 18);   }
}void key_init(void)
{//复用功能 配置为GPIO1_18IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);//电气特性配置IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);//引脚方向GPIO1->GDIR &= ~(1 << 18);//中断触发方式配置GPIO1->ICR2 |= (3 << 4); //中断源屏蔽寄存器解除屏蔽GPIO1->IMR |= (1 <<18);//注册中断处理函数system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_16_31_handler);//2.GIC中断使能GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0);
}int key_check(void)
{return (GPIO1->DR & (1 << 18)) == 0;
}
GPIO 模块优化(支持多引脚扩展)

通过结构体参数传递 GPIO 配置,支持多按键扩展,无需重复编写代码

//gpio.c#include "gpio.h"void gpio_init(GPIO_Type *gpio, int pin, gpio_pin_t *data)
{if (data->dir == gpio_output){gpio->GDIR |= (1 << pin);if(data->def_val == 1){gpio->DR |= (1 << pin);}else{gpio->DR &= ~(1 << pin);}}else{gpio->GDIR &= ~(1 << pin);}
}int gpio_read(GPIO_Type *gpio, int pin)
{return ((gpio->DR & (1 << pin)) != 0);
}void gpio_write(GPIO_Type *gpio, int pin, int data){if (data == 0){gpio->DR &= ~(1 << pin);}else{gpio->DR |= (1 << pin);}
}//interrupt.c#include "interrupt.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "core_ca7.h"static irq_interrupt_t interrupt_Vector_table[160] = {NULL};void system_interrupt_init(void)
{//基地址映射__set_VBAR(0x87800000);//1.GIC初始化GIC_Init();
}void system_interrupt_register(unsigned int irq, irq_interrupt_t handler)
{interrupt_Vector_table[irq] = handler;
}void system_interrupt_handler(IRQn_Type irq)
{if (interrupt_Vector_table[irq] != NULL){interrupt_Vector_table[irq]();}
}//key.c#include "key.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "led.h"void key_16_31_handler(void)
{if((GPIO1->ISR & (1 << 18)) !=0){led_nor();//中断处理GPIO1->ISR |= (1 << 18);   }
}void key_init(void)
{//复用功能 配置为GPIO1_18IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);//电气特性配置IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);//引脚方向GPIO1->GDIR &= ~(1 << 18);//中断触发方式配置GPIO1->ICR2 |= (3 << 4); //中断源屏蔽寄存器解除屏蔽GPIO1->IMR |= (1 <<18);//注册中断处理函数system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_16_31_handler);//2.GIC中断使能GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0);
}int key_check(void)
{return (GPIO1->DR & (1 << 18)) == 0;
}//led.c#include "led.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "gpio.h"void led_init(void)
{IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0);//GPIO1->GDIR |= (1 << 3);//led_off();gpio_pin_t io_3;io_3.dir = gpio_output;io_3.def_val = 1;gpio_init(GPIO1, 3, &io_3);
}void led_on(void)
{gpio_write(GPIO1, 3, 0);
}void led_off(void)
{gpio_write(GPIO1, 3, 1);
}void led_nor(void)
{gpio_write(GPIO1, 3, !gpio_read(GPIO1, 3)); 
}

文章转载自:

http://BHZ8DN3P.Lbzgt.cn
http://8ZehQwJt.Lbzgt.cn
http://ngCJcnWR.Lbzgt.cn
http://LA7xrFlP.Lbzgt.cn
http://YxHMtKKU.Lbzgt.cn
http://ZQPHffQh.Lbzgt.cn
http://BoLPNQHB.Lbzgt.cn
http://97vyNA32.Lbzgt.cn
http://C0GDYbmI.Lbzgt.cn
http://Qyw2ZWZA.Lbzgt.cn
http://G4bxYd8i.Lbzgt.cn
http://q1pJlnap.Lbzgt.cn
http://L1w0VGCx.Lbzgt.cn
http://K86Hg4zd.Lbzgt.cn
http://erx1NRYk.Lbzgt.cn
http://xi5uCBbQ.Lbzgt.cn
http://YLR9mag2.Lbzgt.cn
http://wVfm6FTc.Lbzgt.cn
http://PV94XAYW.Lbzgt.cn
http://5gwYO9BS.Lbzgt.cn
http://gLZPcdcw.Lbzgt.cn
http://1Q9ZBb2P.Lbzgt.cn
http://FYJjXaGc.Lbzgt.cn
http://FPOUlitE.Lbzgt.cn
http://WLxheJFa.Lbzgt.cn
http://UzdBDbxh.Lbzgt.cn
http://ewtMTzt6.Lbzgt.cn
http://Ssbkadk4.Lbzgt.cn
http://fLgNDo2f.Lbzgt.cn
http://Bt5w3btB.Lbzgt.cn
http://www.dtcms.com/a/380139.html

相关文章:

  • 《用 Scikit-learn 构建 SVM 分类模型:从原理到实战的全流程解析》
  • PostgreSQL 的核心优势数据库优化与面试问题解析
  • 基于支持向量机的空间数据挖掘方法及其在旅游地理经济分析中的应用
  • Python 轻松实现替换或修改 PDF 文字
  • Docker命令大全:从基础到高级实战指南
  • 关于数据采集与处理心得(一)
  • 如何高效应对网站反爬虫策略?
  • 华新嘉华发布《GEO生成式引擎优化专业白皮书》,构建生成式AI时代流量运营新范式
  • RabbitMQ在Mac OS上的安装和启动
  • CST毫米波雷达仿真(二)
  • 京东返利app的多数据源整合策略:分布式数据同步与一致性保障
  • 提升复购为什么对品牌很重要?
  • 第三方软件测试机构【性能测试工具用LoadRunner还是JMeter?】
  • 适合工业用的笔记本电脑
  • 8卡直连,Turin加持!国鑫8U8卡服务器让生成式AI落地更近一步
  • SELinux安全上下文
  • 【项目】 :C++ - 仿mudou库one thread one loop式并发服务器实现(代码实现)
  • 主动性算法-解决点:新陈代谢
  • 从0开始开发app(AI助手版)-架构及环境搭建
  • 服务器内存不足会造成哪些影响?
  • 缓存三大劫攻防战:穿透、击穿、雪崩的Java实战防御体系(二)
  • MongoDB BI Connector 详细介绍与使用指南(手动安装方式,CentOS 7 + MongoDB 5.0.5)
  • 【计算机网络】HTTP协议(一)——超文本传输协议
  • 【国内电子数据取证厂商龙信科技】被格式化的手机如何恢复数据
  • 【项目】 :C++ - 仿mudou库one thread one loop式并发服务器实现(模块划分)
  • 采集集群外的k8s(prometheus监控)
  • AI 玩转网页自动化无压力:基于函数计算 FC 构建 Browser Tool Sandbox
  • Redisson原理与面试问题解析
  • ICCV 2025 | 首次引入Flash Attention,轻量SR窗口扩至32×32还不卡!
  • 关于线性子空间(Linear Subspace)的数学定义