【嵌入式Linux】i.MX6ULL GPIO 中断服务函数注册与编写

  • 本文章结合了正点原子的 i.mx6u嵌入式Linux开发指南和笔者的理解。
  • 前面我们进行了编写GPIO 中断管理与配置函数,下面将具体使用这些GPIO 中断管理与配置函数来进行一个具体的中断初始化,以及中断服务函数的编写

1 外部中断初始化与中断服务函数

这段代码主要用于初始化特定的GPIO端口(GPIO1_IO18)作为外部中断,并定义中断服务函数来处理中断事件。以下是每个部分的详细说明:

1.2 外部中断初始化函数 exti_init

1.2.1 GPIO引脚配置

key_config.direction = kGPIO_DigitalInput;
key_config.interruptMode = kGPIO_IntFallingEdge;
gpio_init(GPIO1, 18, &key_config);

设置GPIO1_IO18为数字输入并配置为下降沿触发中断。

1.2.2 中断使能与注册

GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
system_register_irqhandler(GPIO1_Combined_16_31_IRQn, gpio1_io18_irqhandler, NULL);
gpio_int_enable(GPIO1, 18);

在GIC(通用中断控制器)中使能对应的中断,并注册中断服务函数gpio1_io18_irqhandler

1.2.3 GIC_EnableIRQ()函数的分析

/*  
 * 使能指定的中断
 */
FORCEDINLINE __STATIC_INLINE void GIC_EnableIRQ(IRQn_Type IRQn)
{
  	GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
  	gic->D_ISENABLER[((uint32_t)(int32_t)IRQn) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}

这段代码定义了一个名为 GIC_EnableIRQ 的函数,用于启用指定的中断。

  • 函数首先获取 GIC 控制器基地址。

    • __get_CBAR() 函数返回 GIC 控制器基地址,并将其与 0xFFFF0000UL 进行按位与运算,得到 GIC 控制器基地址的最高 16 位。
    • Cortex-A7 Technical ReferenceManua.pdfP138
      在这里插入图片描述
      在这里插入图片描述
  • 然后,函数计算出要操作的寄存器地址。

    • ((uint32_t)(int32_t)IRQn) >> 5 将中断号右移 5 位,得到中断号所在的寄存器索引。
  • 最后,函数设置该寄存器中的相应位,从而启用该中断。

    • ((uint32_t)(int32_t)IRQn) & 0x1FUL 将中断号与 0x1FUL 进行按位与运算,得到中断号在寄存器中的位索引。
    • 然后,将 1UL 左移 位索引 位,得到一个掩码,并将其写入 D_ISENABLER 寄存器中,从而启用该中断。
  • D_ISENABLER 寄存器是 GIC 分发器中的一组寄存器,用于控制每个中断的启用状态。
    ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdfP93
    在这里插入图片描述

    • 每个寄存器包含 32 个 Set-enable 位,分别对应于 32 个中断。
    • 当 Set-enable 位被设置为 1 时,对应中断会被启用,这意味着 GIC 会将该中断转发到 CPU。
    • 当 Set-enable 位被设置为 0 时,对应中断会被禁用,这意味着 GIC 不会将该中断转发到 CPU。

    根据中断 ID (m),对应 GICD_ISENABLER 寄存器的编号 (n) 可以通过 n = m DIV 32 计算得到,也就是IRQn) >> 5

    • 对应 GICD_ISENABLER 寄存器的偏移地址为 0x100 + (4*n),这里不用手动计算,通过gic->D_ISENABLER·就可以得到。
    • 对应 GICD_ISENABLER 寄存器中需要设置的位的编号为 m MOD 32,也就是IRQn & 0x1FUL

1.3 中断服务函数 gpio1_io20_irqhandler

1.3.1 消抖处理

delay(10);

简单的延时用于消除由于机械或电气噪声产生的误触发。

1.3.2 中断事件处理

if(gpio_pin_read(GPIO1, 18) == 0){
    state = !state;
    beep_switch(state);
}

读取GPIO1_IO18的状态,如果为低电平(按键被按下),则切换状态并控制蜂鸣器的开关状态。

1.3.3 清除中断标志

gpio_int_flagClear(GPIO1, 18);

完成事件处理后,清除中断标志以准备接收下一个中断。

2 BUG处理

2.1 问题描述

程序烧写后,发现当尝试按下按键,进入按键中断处理函数时,灯会一直保持在按键按下那一刻的状态,程序卡死。

2.2 解决过程

  • 查看反汇编文件,发现bss段放在了程序起始地址,导致中断向量表的位置不对了(之前说过,应该放在程序的起始位置)

BSS 段(Block Started by Symbol)是程序内存中用于存储未初始化全局变量和静态变量的区域。

在这里插入图片描述

2.3 解决方式

_start:中,将bss段初始化放到中断向量表初始化后面

_start:
    @ 在处理器启动时执行一次,用于初始化中断向量表
    ldr pc, =Reset_Handler     @ 复位中断服务函数
    ldr pc, =Undefined_Handler @ 未定义指令中断服务函数
    ldr pc, =SVC_Handler       @ SVC中断服务函数
    ldr pc, =PreAbort_Handler  @ 预取终止
    ldr pc, =DataAbort_Handler @ 数据终止
    ldr pc, =NotUsed_Handler   @ 未使用
    ldr pc, =IRQ_Handler       @ IRQ中断服务函数
    ldr pc, =FIQ_Handler       @ FIQ中断服务函数

@ 复位中断服务函数
Reset_Handler:
    @ 0.禁止IRQ中断
    @ 方式:修改PSTATE处理器状态寄存器
    @ Change PE State (CPS) 用于修改处理器状态寄存器 (PSTATE) 中的某些位
    @ PSTATE 是一个特殊的寄存器,它包含了处理器当前的运行状态信息,包括:
    @ A (Application) 位: 控制应用程序模式下的中断是否允许。
    @ I (IRQ) 位: 控制 IRQ 中断是否允许。
    @ F (FIQ) 位: 控制 FIQ 中断是否允许。
    @ M (Mode) 位: 控制处理器当前运行的模式。
    cpsid i

    @ 1.关闭I/D Cache, MMU
    @ 方式:修改SCTLR寄存器
    @ (System Control Register,可以通过 CP15 协处理器访问)
    @ SCTLR寄存器:
        @ bit0:MMU 
        @ bit1:对齐控制 
        @ bit2:D Cache 
        @ bit11:分支预测控制 
        @ bit12:I Cache
    MRC p15, 0, r0, c1, c0, 0   @ Move to Register from Coprocessor(这个形式操作的是SCTLR寄存器)
    bic r0, r0, #(1<<12)        @ 关闭I Cache(bic:Bit Clear)
    bic r0, r0, #(1<<11)        @ 关闭分支预测
    bic r0, r0, #(1<<2)         @ 关闭D Cache
    bic r0, r0, #(1<<1)         @ 关闭对齐控制
    bic r0, r0, #(1<<0)         @ 关闭MMU
    MCR p15, 0, r0, c1, c0, 0   @ Move to Coprocessor from Register

#if 0
    @ 2.设置中断向量偏移
    @ 方式:修改VBAR寄存器
    @ (Vector Base Address Register,可以通过 CP15 协处理器访问)
    ldr r0, =0x87800000
    dsb
    isb
    MCR p15, 0, r0, c12, c0, 0  @ 设置VBAR寄存器为0x87800000
    dsb
    isb
#endif

.global _bss_start             @ 声明_bss_start符号为全局可见
_bss_start:                    @ 定义_bss_start标签
    .word _bss_start           @ 将_bss_start标签的地址存储为一个字(4字节)

.global _bss_end               @ 声明_bss_end符号为全局可见
_bss_end:                      @ 定义_bss_end标签
    .word _bss_end             @ 将_bss_end标签的地址存储为一个字(4字节)
    @ 3.清除bss段
    @ 方式:使用循环逐个清除
    ldr r0, _bss_start         @ 将_bss_start地址加载到r0寄存器
    ldr r1, _bss_end           @ 将_bss_end地址加载到r1寄存器
    mov r2, #0                 @ 将0存储到r2寄存器
bss_loop:
    stmia r0!, {r2}            @ 存储r2寄存器的值到r0指向的内存地址,并自增r0
    cmp r0, r1                 @ 比较r0和r1的值
    ble bss_loop               @ 如果r0小于等于r1,则跳转到bss_loop标签(继续循环)

    @ 4.设置处理器进入IRQ模式
    @ 方式:修改cpsr寄存器
    @ (Current Program Status Register,当前程序状态寄存器)
    @ 它包含了处理器状态和控制信息,例如 APSR、指令集状态、IT 块状态、字节序和当前处理器模式。 
    mrs r0, cpsr               @ 读取CPSR寄存器的值到r0寄存器
    bic r0, r0, #0x1f          @ 通过位清除操作,清除r0寄存器的低5位
    orr r0, r0, #0x12          @ 使用IRQ模式
    msr cpsr, r0               @ 将r0寄存器的值写回CPSR寄存器
    ldr sp, =0x80600000        @ 设置IRQ下的sp指针

    @ 5.设置处理器进入SYS模式
    @ 方式:修改cpsr寄存器
    @ (Current Program Status Register,当前程序状态寄存器)
    @ 它包含了处理器状态和控制信息,例如 APSR、指令集状态、IT 块状态、字节序和当前处理器模式。 
    mrs r0, cpsr               @ 读取CPSR寄存器的值到r0寄存器
    bic r0, r0, #0x1f          @ 通过位清除操作,清除r0寄存器的低5位
    orr r0, r0, #0x1f          @ 使用SYS模式
    msr cpsr, r0               @ 将r0寄存器的值写回CPSR寄存器
    ldr sp, =0x80400000        @ 设置SYS下的sp指针

    @ 6.设置处理器进入SVC模式
    @ 方式:修改cpsr寄存器
    @ (Current Program Status Register,当前程序状态寄存器)
    @ 它包含了处理器状态和控制信息,例如 APSR、指令集状态、IT 块状态、字节序和当前处理器模式。 
    mrs r0, cpsr               @ 读取CPSR寄存器的值到r0寄存器
    bic r0, r0, #0x1f          @ 通过位清除操作,清除r0寄存器的低5位
    orr r0, r0, #0x13          @ 使用SVC模式
    msr cpsr, r0               @ 将r0寄存器的值写回CPSR寄存器
    ldr sp, =0x80200000        @ 设置SVC下的sp指针

    @ 7.使能IRQ中断
    @ 方式:修改PSTATE处理器状态寄存器
    @ Change PE State (CPS) 用于修改处理器状态寄存器 (PSTATE) 中的某些位
    @ PSTATE 是一个特殊的寄存器,它包含了处理器当前的运行状态信息,包括:
    @ A (Application) 位: 控制应用程序模式下的中断是否允许。
    @ I (IRQ) 位: 控制 IRQ 中断是否允许。
    @ F (FIQ) 位: 控制 FIQ 中断是否允许。
    @ M (Mode) 位: 控制处理器当前运行的模式。
    cpsie i

    @ 8.跳转到main函数的入口
    b main                     @ 跳转到main函数的入口
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Witheart__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值