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

在Linux Kernel v3.4.113中处理10MHz GPIO中断的性能问题求助

全志H3 GPIO中断处理性能瓶颈解决方案

问题背景

硬件:搭载1.2GHz时钟的全志H3开发板
系统:Linux Kernel v3.4.113
需求:接收时钟频率为10MHz的并行数据
问题:内核GPIO中断处理速度极慢,每秒仅能处理500次左右中断(测试代码预期counter值为10,000,000,实际仅1200、700、1000、900左右)
测试代码如下:

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/delay.h>

#define GPIO_IN 6

unsigned long counter=0;

static irqreturn_t my_handler(int irq, void *dev_id)
{
    counter++;
    return IRQ_HANDLED;
}

static int __init exemple_init (void)
{
    int err, i = 0;
    printk(KERN_INFO "Loading GPIO module\n");

    if ((err = gpio_request(GPIO_IN, THIS_MODULE->name)) !=0) {
        printk(KERN_INFO "GPIO Request Error\n");
        return err;
    }

    if ((err = gpio_direction_input(GPIO_IN)) !=0) {
        printk(KERN_INFO "GPIO Direction Error\n");
        return err;
    }

    if ((err = request_irq(gpio_to_irq(GPIO_IN), my_handler, IRQF_TRIGGER_RISING, THIS_MODULE->name, THIS_MODULE->name)) != 0) {
        printk(KERN_INFO "GPIO IRQ Error\n");
        gpio_free(GPIO_IN);
        return err;
    }
    printk(KERN_INFO "Waiting for interrupts ... \n");
    for (i = 0; i < 10; i++) {
        counter = 0;
        mdelay(1000);
        printk(KERN_INFO "Frequency: %lu\n", counter);
    }
    printk(KERN_INFO "Done \n");    
    return 0; 
} 

static void __exit exemple_exit (void) 
{
    free_irq(gpio_to_irq(GPIO_IN), THIS_MODULE->name);  
    gpio_free(GPIO_IN);
}

module_init(exemple_init);
module_exit(exemple_exit);

MODULE_AUTHOR("Pierre LE COZ");
MODULE_DESCRIPTION("GPIO interrupt");
MODULE_LICENSE("GPL");

解决方案

一、中断上下文与代码优化

  • 启用快速中断模式:在request_irq时添加IRQF_DISABLED标记,关闭本地CPU其他中断以减少调度开销,修改后的flags参数为:
    IRQF_TRIGGER_RISING | IRQF_DISABLED
    
  • 绑定中断到单核心:避免跨核心调度开销,在request_irq成功后添加核心绑定代码:
    cpumask_t mask;
    cpumask_clear(&mask);
    cpumask_set_cpu(0, &mask);
    irq_set_affinity(gpio_to_irq(GPIO_IN), &mask);
    
  • 替换忙等待为定时器统计:原代码用mdelay(1000)忙等待会占用CPU,改用定时器在进程上下文统计:
    static struct timer_list stat_timer;
    static int stat_count = 0;
    
    static void stat_timer_func(unsigned long data) {
        printk(KERN_INFO "Frequency: %lu\n", counter);
        counter = 0;
        stat_count++;
        if (stat_count < 10) {
            mod_timer(&stat_timer, jiffies + msecs_to_jiffies(1000));
        } else {
            printk(KERN_INFO "Done \n");
        }
    }
    
    // 在exemple_init的request_irq成功后添加:
    setup_timer(&stat_timer, stat_timer_func, 0);
    mod_timer(&stat_timer, jiffies + msecs_to_jiffies(1000));
    
  • 使用原子操作计数:解决多核环境下的计数竞争问题,将counter替换为原子变量:
    atomic_t counter = ATOMIC_INIT(0);
    
    static irqreturn_t my_handler(int irq, void *dev_id)
    {
        atomic_inc(&counter);
        return IRQ_HANDLED;
    }
    
    // 统计时:
    unsigned long cnt = atomic_read(&counter);
    printk(KERN_INFO "Frequency: %lu\n", cnt);
    atomic_set(&counter, 0);
    

二、全志H3硬件层面优化

  • 提升GPIO控制器时钟:全志H3的GPIO挂载在APB总线,默认时钟可能被降频,直接操作CCU寄存器将GPIO时钟调至最高(如100MHz),具体寄存器地址参考H3 datasheet,修改对应分频系数为1。
  • 关闭GPIO硬件防抖:部分GPIO默认开启防抖功能会过滤高频信号,通过GPIO控制器寄存器关闭防抖阈值配置。
  • 改用硬件DMA或专用外设:10MHz频率已远超Linux中断处理极限(中断上下文开销至少1μs,而10MHz对应100ns周期),最优方案是放弃GPIO中断,使用H3的并行总线(如FMC)、SPI等带DMA功能的外设接收数据,通过硬件批量传输规避中断开销。

三、内核配置优化

  • 关闭抢占与调试选项:重新编译内核,关闭CONFIG_PREEMPT(抢占式内核增加中断调度开销)、CONFIG_DEBUG_KERNEL相关调试选项,开启CONFIG_HIGH_RES_TIMERS提升定时器精度。
  • 设置中断最高优先级:通过irq_set_priority将GPIO中断优先级设为最高:
    irq_set_priority(gpio_to_irq(GPIO_IN), IRQ_PRIO_HIGHEST);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:57:49