树莓派3B+ Linux 6.6下用FIQ实现按键触发GPIO控LED的方法咨询
树莓派3B+ 基于FIQ实现按键触发LED切换方案
一、FIQ处理程序注册方法修正
你当前的写法存在问题:把汇编代码嵌套在C函数内部会引入C函数栈帧初始化、返回指令等冗余代码,而FIQ处理程序需要纯汇编实现,且必须符合ARM架构的异常处理上下文规范。
正确实现方式:
- 单独编写纯汇编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
- 在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
相关产品推荐
相关产品推荐

