第二期 GPIO中断实验

本文详细介绍了STM32和Cortex-A7的中断系统,包括中断向量表、中断向量偏移、NVIC与GIC中断控制器的使用,并通过实例展示了中断服务函数的编写,特别探讨了在Cortex-A7中如何关闭I/D Cache和MMU,以及设置中断向量偏移。最后,文章提供了按键中断例程和IRQ中断服务函数的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、回顾STM32中断系统

1、STM32中断向量表

ARM芯片从0X00000000开始运行,执行指令。在程序开始的地方存放着中断向量表。中断向量表主要功能是描述中断对应的中断服务函数。

对于STM32来说代码最开始的地址存放堆栈栈顶指针。

2、中断向量偏移

一般ARM从0X000000000地址开始运行,对于STM32我们设置连接首地址为0X8000000。

如果代码一定要从0X8000000开始运行,那么需要告诉一下soc内核。也就是设置中断向量偏移。设置SCB的VTOR寄存器为新的中断向量表起始地址即可。

3、NVIC中断控制器

NVIC就是中断管理机构。使能和关闭指定的中断、设置中断优先级。

4、中断服务函数的编写

中断服务函数就是中断要做的事情。

二、Cortex-A7中断系统

1、Cortex-A中断向量表

Cortex-A中断向量表有8个中断,其中重点关注IRQ。Cortex-A的中断向量表需要用户自己去定义。

2、中断向量偏移

我们的裸机历程都是从0X87800000开始的,因此要设置中断向量偏移。

3、GIC中断控制器

同NVIC一样,GIC用于管理Cortex-A的中断。GIC提供了开关中断,设置中断优先级。

4、IMX6U中断号

为了区分不同的中断,引入了中断号。ID0ID15是给SGI,ID16ID31是给PPI。剩下的ID32~1019给SPI,也就是按键中断、串口中断。。。。

6ULL支持128个中断。

4、中断服务函数的编写

### GPIO中断程序实现方法 在嵌入式Linux系统中,GPIO中断是一种常见的硬件事件处理机制。以下是两种主流的实现方式及其对应的示例代码。 #### 方法一:Pin写死法 此方法通过硬编码的方式,在驱动代码中直接定义GPIO的功能和属性[^2]。这种方式简单明了,适合快速验证功能,但在实际项目中不推荐使用,因为它缺乏灵活性。 ```c #include <linux/gpio.h> #include <linux/interrupt.h> static irqreturn_t gpio_irq_handler(int irq, void *dev_id) { printk(KERN_INFO "GPIO Interrupt Triggered\n"); return IRQ_HANDLED; } static int __init gpio_interrupt_init(void) { int ret; unsigned int gpio_num = 17; // 假设使用的GPIO编号为17 int irq; if (gpio_request(gpio_num, "sysfs")) { // 请求GPIO资源 printk(KERN_ERR "Failed to request GPIO %u\n", gpio_num); return -ENODEV; } gpio_direction_input(gpio_num); // 设置GPIO方向为输入模式 set_gpio_to_active_low(gpio_num, false); // 配置触发极性 irq = gpio_to_irq(gpio_num); // 将GPIO映射到IRQ号 if (irq < 0) { printk(KERN_ERR "Failed to map GPIO to IRQ\n"); goto fail; } ret = request_irq(irq, gpio_irq_handler, IRQF_TRIGGER_RISING, "gpio-int-test", NULL); // 注册中断处理函数 if (ret) { printk(KERN_ERR "Failed to register interrupt handler\n"); goto free_gpio; } return 0; free_gpio: gpio_free(gpio_num); fail: return ret; } static void __exit gpio_interrupt_exit(void) { free_irq(gpio_to_irq(17), NULL); // 解除中断注册 gpio_free(17); // 释放GPIO资源 } module_init(gpio_interrupt_init); module_exit(gpio_interrupt_exit); MODULE_LICENSE("GPL"); ``` 上述代码展示了如何通过`request_irq()`接口绑定一个上升沿触发的GPIO中断,并实现了简单的中断服务程序(ISR)[^2]。 --- #### 方法二:DTS文件配置法 现代嵌入式开发更倾向于利用设备树(DTS/DTSI)来描述硬件信息,这种方法可以分离硬件配置与驱动逻辑,增强系统的可维护性和扩展性。 ##### DTS文件中的GPIO节点配置 ```dts / { gpio_keys: keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; button@0 { label = "User Button"; gpios = <&gpio 17 1>; /* 使用第17号GPIO设置为高电平有效 */ linux,input-type = <1>; /* 表示按键按下时为高电平 */ debounce-interval = <50>; /* 按键去抖时间(ms) */ interrupt-parent = <&intc>; interrupts = <17 1>; /* 中断源ID和触发类型 */ }; }; }; ``` ##### 对应的驱动代码片段 ```c #include <linux/platform_device.h> #include <linux/input.h> #include <linux/gpio/consumer.h> struct gpio_button_data { struct input_dev *input; struct gpio_desc *gpiod; }; static irqreturn_t gpio_dts_irq_handler(int irq, void *data) { struct gpio_button_data *button_data = data; bool value = gpiod_get_value(button_data->gpiod); input_report_key(button_data->input, KEY_ENTER, value); // 报告按键状态变化 input_sync(button_data->input); return IRQ_HANDLED; } static int gpio_button_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_button_data *data; int error; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->gpiod = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN); if (IS_ERR(data->gpiod)) { error = PTR_ERR(data->gpiod); dev_err(dev, "failed to get GPIO: %d\n", error); return error; } data->input = devm_input_allocate_device(dev); if (!data->input) return -ENOMEM; data->input->name = "gpio-button-input"; data->input->keycode = BTN_0; data->input->evbit[0] = BIT_MASK(EV_KEY); data->input->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); error = input_register_device(data->input); if (error) { dev_err(dev, "unable to register input device: %d\n", error); return error; } error = devm_request_threaded_irq(dev, gpiod_to_irq(data->gpiod), NULL, gpio_dts_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, "gpio-dts-btn", data); if (error) { dev_err(dev, "failed to request threaded IRQ: %d\n", error); return error; } return 0; } static const struct of_device_id gpio_button_of_match[] = { { .compatible = "gpio-keys" }, {}, }; MODULE_DEVICE_TABLE(of, gpio_button_of_match); static struct platform_driver gpio_button_driver = { .probe = gpio_button_probe, .driver = { .name = "gpio-buttons-driver", .of_match_table = gpio_button_of_match, }, }; module_platform_driver(gpio_button_driver); MODULE_AUTHOR("Example Author"); MODULE_DESCRIPTION("GPIO Buttons Driver Example"); MODULE_LICENSE("GPL"); ``` 该部分代码展示了一个完整的基于DTS的GPIO中断驱动框架,其中包含了平台设备探测、GPIO初始化以及中断回调函数的设计。 --- ### 总结 以上分别介绍了两种不同的GPIO中断实现方案——Pin写死法和DTS文件配置法。前者适用于实验环境下的快速原型设计;而后者则更适合于生产环境中复杂的多模块协作场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值