You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派3B+ Linux 6.6下用FIQ实现按键触发GPIO控LED的方法咨询

树莓派3B+ 基于FIQ实现按键触发LED切换方案

一、FIQ处理程序注册方法修正

你当前的写法存在问题:把汇编代码嵌套在C函数内部会引入C函数栈帧初始化、返回指令等冗余代码,而FIQ处理程序需要纯汇编实现,且必须符合ARM架构的异常处理上下文规范。

正确实现方式:

  1. 单独编写纯汇编FIQ处理程序,包含寄存器保存、GPIO操作、异常返回的完整逻辑:
.global fiq_handler
fiq_handler:
    ; 保存FIQ模式下的通用寄存器(r0-r7、lr需要手动保存,r8-r12为FIQ私有寄存器无需保存)
    stmfd sp!, {r0-r7, lr}

    ; 切换LED电平(示例:LED接GPIO17)
    ldr r0, =0x3F200000    ; GPIO外设基地址
    ldr r1, [r0, #0x34]    ; 读取GPLEV0寄存器,获取GPIO17当前电平
    tst r1, #(1 << 17)     ; 判断电平状态
    ldrne r2, =(1 << 17)
    strne r2, [r0, #0x28]  ; 若为高电平,写入GPCLR0寄存器拉低
    ldreq r2, =(1 << 17)
    streq r2, [r0, #0x1C]  ; 若为低电平,写入GPSET0寄存器拉高

    ; 恢复寄存器
    ldmfd sp!, {r0-r7, lr}
    ; FIQ异常返回:修正LR值后跳转回原执行点
    subs pc, lr, #4
  1. 在C代码中通过内核API完成注册:
#include <linux/module.h>
#include <linux/fiq.h>
#include <linux/io.h>
#include <linux/gpio/consumer.h>

extern void fiq_handler(void);

static struct fiq_handler fh = {
    .name = "led_fiq_handler",
};

static int __init fiq_led_init(void) {
    int ret;

    // 申请FIQ控制权
    ret = claim_fiq(&fh);
    if (ret) {
        pr_err("Failed to claim FIQ\n");
        return ret;
    }

    // 设置FIQ处理程序入口
    set_fiq_handler(fiq_handler, sizeof(fiq_handler));

    pr_info("FIQ LED handler registered\n");
    return 0;
}

static void __exit fiq_led_exit(void) {
    // 释放FIQ控制权
    release_fiq(&fh);
    pr_info("FIQ LED handler unregistered\n");
}

module_init(fiq_led_init);
module_exit(fiq_led_exit);
MODULE_LICENSE("GPL");

二、将GPIO中断转为FIQ触发

树莓派3B+搭载的BCM2835芯片,其中断控制器支持将单条中断线配置为FIQ源,操作步骤如下:

  • 确定按键GPIO对应的中断号:
    BCM2835中GPIO n的中断号为17 + n(例如GPIO23对应中断号40),可通过cat /proc/interrupts命令确认。
  • 配置GPIO为中断触发模式:
    将按键GPIO设置为输入模式,配置为上升沿/下降沿触发(根据按键接线决定),这部分逻辑与普通IRQ配置一致,但不要注册IRQ处理程序。
  • 配置中断控制器的FIQ选择寄存器:
    BCM2835中断控制器基地址为0x3F00B000,其中FIQ_CONTROL寄存器偏移为0x20C。该寄存器bit0用于启用FIQ,bit1-6用于选择对应的中断线(仅支持单条中断线作为FIQ源)。
    示例代码(在模块初始化函数中添加):
#define IRQ_CONTROL_BASE 0x3F00B000
#define FIQ_CONTROL_REG 0x20C

static void __iomem *irq_control;

// 在init函数中添加:
irq_control = ioremap(IRQ_CONTROL_BASE, SZ_4K);
if (!irq_control) {
    pr_err("Failed to remap IRQ control\n");
    release_fiq(&fh);
    return -ENOMEM;
}

// 假设按键对应中断号为40,配置FIQ_CONTROL寄存器启用该中断为FIQ源
writel( (1 << 0) | (40 << 1), irq_control + FIQ_CONTROL_REG );

// 禁用该中断线的IRQ触发,避免IRQ与FIQ同时触发
disable_irq(40);

注意:需确保内核启动参数未占用FIQ资源(例如禁用fiq_debugger类参数)。

三、关键注意事项

  • FIQ是ARM架构优先级最高的异常,处理程序需尽可能简短,避免耗时操作。
  • 调试FIQ难度高于普通IRQ,建议先通过IRQ验证GPIO控制逻辑正确后,再切换到FIQ实现。
  • BCM2835官方外设手册包含中断控制器与GPIO的完整寄存器定义,是最权威的参考依据。

内容的提问来源于stack exchange,提问作者InsaneCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:17:06