学习笔记——GPIO按键与中断系统

嵌入式开发笔记:GPIO按键与中断系统

一、课前回顾要点

(1) volatile关键字的作用

作用:防止编译器优化,确保每次访问变量时都从内存中读取

  • 对编译器:禁止对该变量进行优化

  • 对程序:确保变量值的实时性

  • 应用场景:

    • 多线程共享变量

    • 硬件寄存器访问

    • 中断服务程序中的变量

(2) 链接脚本的作用

作用:控制程序在内存中的布局和组织

  • 指定各段(section)的存储位置

  • 定义程序的入口点

  • 管理内存分配(RAM/ROM)

  • 控制符号的地址和顺序

二、按键驱动实验(轮询方式)

1. 硬件介绍

复制代码

2. 按键代码编写(轮询方式)

(1) 查阅手册

参考文档

  • IMX6ULL_MINI_V2.2(Mini底板原理图)

  • IMX6ULL参考手册.pdf

(2) 初始化步骤
复制代码
/* 步骤1:复用功能配置 */
// 引脚:UART1_CTS_B (GPIO1_IO18)
// 寄存器:IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B
// 配置:ALT5模式 → GPIO1_IO18
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
/*
位域说明:
[0] SION: 0 (DISABLED) - 输入路径由功能决定
[3:0] MUX_MODE: 0101 (ALT5) - GPIO1_IO18
*/

/* 步骤2:电气特性配置 */
// 寄存器:IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
/*
位域详解(16位):
[16] HYS: 0 - 禁用迟滞
[15:14] PUS: 11 - 22K上拉电阻
[13] PUE: 1 - 选择上拉/下拉
[12] PKE: 1 - 使能上拉/下拉
[11] ODE: 0 - 禁用开漏
[7:6] SPEED: 10 - 中速(100MHz)
[5:3] DSE: 000 - 输出驱动禁用(输入模式)
[0] SRE: 0 - 慢转换速率
*/

/* 步骤3:GPIO方向设置 */
// 寄存器:GPIO1_GDIR
GPIO1->GDIR &= ~(1 << 18);  // 设置为输入模式
/*
GDIR位说明:
0 - INPUT (输入模式)
1 - OUTPUT (输出模式)
*/

/* 步骤4:使能GPIO时钟 */
// 寄存器:CCM_CCGR1
// GPIO1组所有引脚共用该时钟门控
CCM->CCGR1 |= (3 << 26);  // 使能GPIO1时钟
(3) 运行时开关检测
复制代码
/* 读取按键状态 */
// 寄存器:GPIO1_DR
int key_value = (GPIO1->DR >> 18) & 0x1;
/*
状态说明:
1 - 开关断开(高电平)
0 - 开关按下(低电平)
*/

3. 轮询方式的问题

复制代码
void main_task(void) {
    while(1) {
        // 复杂业务处理(耗时)
        process_complex_task();  // 可能执行很长时间
        
        // 按键检测(可能被漏掉)
        check_key_status();      // 轮询间隔可能很长
    }
}

// 模拟复杂业务
void delay(unsigned int count) {
    while(count--);  // 简单延时模拟
}

问题分析

  1. 响应延迟:主循环中的复杂任务会延迟按键检测

  2. 漏检风险:快速按键可能被完全错过

  3. 实时性差:不适用于紧急响应场景(如汽车刹车)

三、中断方式解决方案

1. 中断基本概念

中断:CPU能打断当前工作,处理紧急任务,处理完后返回原处继续工作

2. 中断处理流程

复制代码
┌─────────────────────────────────┐
│        中断处理流程              │
├─────────────────────────────────┤
│ 1. 中断源发出中断请求           │
│ 2. CPU检查是否响应中断          │
│ 3. 检查中断优先级               │
│ 4. 保护现场(保存寄存器)       │
│ 5. 执行中断服务函数(ISR)      │
│ 6. 恢复现场(恢复寄存器)       │
│ 7. 返回原程序继续执行           │
└─────────────────────────────────┘

3. 中断控制器GIC详解

(1) GIC架构概览

参考文档ARM Generic Interrupt Controller V2.0.pdf

复制代码
GICv2架构(单核IMX6ULL):
┌─────────────────────────────────┐
│      GIC Distributor            │
│  ┌─────────────────────────┐    │
│  │ 中断源管理 (0-1019)      │    │
│  │ • SGI (0-15): 软件中断   │    │
│  │ • PPI (16-31): 私有中断  │    │
│  │ • SPI (32-1019):共享中断 │    │
│  └─────────────────────────┘    │
│              │                  │
└──────────────┼──────────────────┘
               ▼
┌─────────────────────────────────┐
│     CPU Interface (Processor0)  │
│  ┌─────────────────────────┐    │
│  │ • 中断优先级屏蔽        │    │
│  │ • 中断确认              │    │
│  │ • 中断完成通知          │    │
│  └─────────────────────────┘    │
└─────────────────────────────────┘
(2) 中断类型说明
  • SGI (0-15):软件生成中断,用于核间通信

  • PPI (16-31):私有外设中断,每个核独有

  • SPI (32-1019):共享外设中断,所有核可见

(3) IMX6ULL中断映射

参考IMX6ULL参考手册.pdf - Table 3-1

  • 总中断号:0-128

  • GPIO中断属于SPI类型

4. 协处理器CP15

(1) 功能概述

位置 :Cortex-A7技术参考手册第4章
作用:系统控制与配置

  • 系统控制与配置

  • MMU配置与管理

  • Cache配置与管理

  • 虚拟化与安全

  • 系统性能监控

(2) 关键寄存器组
复制代码
/* c1寄存器:SCTLR (System Control Register) */
mrc p15, 0, r0, c1, c0, 0  // 读取SCTLR
bic r0, r0, #(1 << 13)     // 清除bit13 (V)
orr r0, r0, #(1 << 12)     // 设置bit12 (I Cache使能)
mcr p15, 0, r0, c1, c0, 0  // 写回SCTLR

/*
SCTLR关键位:
bit13 [V]: 异常向量表基地址
    0 - 正常异常向量,基地址0x00000000
    1 - 高异常向量,基地址0xFFFF0000
bit12 [I]: I Cache使能
    0 - 禁用指令Cache
    1 - 使能指令Cache
*/

/* c12寄存器:VBAR (Vector Base Address Register) */
__get_VBAR(0x87800000);  // 设置异常向量表基地址

/* c15寄存器:CBAR (Configuration Base Address Register) */
mrc p15, 4, r0, c15, c0, 0  // 读取GIC寄存器物理基地址

5. 中断代码实现架构

(1) 模块化设计
复制代码
/* 层次结构 */
┌─────────────────┐
│   应用层        │ ← 用户接口
├─────────────────┤
│   中断管理层    │ ← 中断注册/分发
├─────────────────┤
│   GPIO驱动层    │ ← 硬件操作
├─────────────────┤
│   硬件寄存器    │ ← 直接硬件访问
└─────────────────┘
(2) GPIO模块封装
复制代码
/* gpio.h */
typedef struct {
    int direction;      // 方向:输入/输出
    int pull;          // 上拉/下拉
    int speed;         // 速度
    int drive_strength; // 驱动能力
} gpio_config_t;

/* 函数声明 */
void gpio_init(GPIO_Type *gpio, int pin, gpio_config_t *config);
int gpio_read(GPIO_Type *gpio, int pin);
void gpio_write(GPIO_Type *gpio, int pin, int value);
void gpio_set_interrupt(GPIO_Type *gpio, int pin, int edge_type, void (*callback)(void));
(3) 中断模块设计(OCP原则)
复制代码
/* interrupt.h */
typedef void (*irq_handler_t)(int irq, void *data);

/* 中断控制器接口 */
void interrupt_init(void);
int register_irq(int irq_num, irq_handler_t handler, void *data);
void unregister_irq(int irq_num);
void enable_irq(int irq_num);
void disable_irq(int irq_num);

/* 应用示例 */
void key_interrupt_handler(int irq, void *data) {
    // 处理按键中断
    printf("Key pressed! IRQ: %d\n", irq);
}

int main() {
    // 初始化
    interrupt_init();
    
    // 注册中断处理函数(开放扩展)
    register_irq(GPIO1_IRQn, key_interrupt_handler, NULL);
    
    // 使能中断
    enable_irq(GPIO1_IRQn);
    
    while(1) {
        // 主任务(不会被中断打断的业务逻辑)
        // 按键处理已在中断中完成
    }
}

6. 代码优化原则

(1) 满足用户基本需求
  • 可靠的按键检测

  • 实时响应

  • 易于使用

(2) 程序稳定可靠
  • 中断嵌套处理

  • 临界区保护

  • 错误处理机制

(3) OCP原则(开放封闭原则)
  • 对修改封闭:核心中断框架稳定,不轻易修改

  • 对扩展开放:可以轻松添加新的中断处理函数

四、总结对比

特性 轮询方式 中断方式
响应速度 慢(依赖轮询间隔) 快(立即响应)
CPU占用 高(持续检查) 低(事件驱动)
实时性 优秀
代码复杂度 简单 较复杂
适用场景 简单应用 实时系统

五、实践建议

  1. 调试技巧

    • 使用LED指示灯辅助调试

    • 添加串口打印调试信息

    • 逐步验证每个配置步骤

  2. 常见问题

    • 中断未触发:检查GIC配置、GPIO中断使能

    • 中断频繁触发:检查消抖处理

    • 系统卡死:检查中断服务函数执行时间

  3. 性能优化

    • 中断服务函数尽量简短

    • 使用中断下半部处理耗时任务

    • 合理设置中断优先级

相关推荐
R-sz2 小时前
app登录接口实现,基于JWT的APP登录认证系统实现方案
java·开发语言·python
楼田莉子2 小时前
Linux学习之库的原理与制作
linux·运维·服务器·c++·学习
枷锁—sha2 小时前
【Vulhub】1Panel 访问控制绕过实战指南 (CVE-2024-39907)
运维·学习·安全·网络安全
Elieal2 小时前
@Api 系列注解
java·开发语言
__万波__2 小时前
STM32L475按键中断实验
stm32·单片机·嵌入式硬件
破晓单片机2 小时前
STM32单片机分享:智能恒温箱系统
stm32·单片机·嵌入式硬件·智能家居
保护我方头发丶2 小时前
hard_link.bat(个人用)
c语言
Remember_9932 小时前
【数据结构】深入理解Map和Set:从搜索树到哈希表的完整解析
java·开发语言·数据结构·算法·leetcode·哈希算法·散列表
小痞同学2 小时前
【铁头山羊STM32】HAL库 6.中断部分
stm32·单片机·嵌入式硬件