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

如何为树莓派实现边缘计数器/时钟检测器及低CPU占用的频率检测内核驱动

嘿,刚好做过类似的树莓派边缘计数/频率检测驱动,完全匹配你的低CPU占用和文件接口需求,给你详细捋一遍实现思路和代码:

核心实现思路

要满足低CPU占用,绝对不能用用户空间轮询的方式——必须靠内核GPIO中断+定时器统计的组合:

  • 用GPIO边缘触发中断,每次信号跳变时仅做原子计数(中断上下文尽量轻量化)
  • 用内核定时器定期读取计数值,计算频率后重置计数器
  • 通过sysfs提供文件接口,让用户空间直接读取计算好的频率

1. 驱动核心代码实现

以下是可直接参考的关键代码片段,注释已经写得很清楚:

头文件与全局变量定义

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

// 原子计数器:避免中断与定时器的并发冲突
static atomic_t edge_count = ATOMIC_INIT(0);
// 存储当前计算出的频率值
static unsigned int current_freq = 0;
// 内核定时器
static struct timer_list freq_timer;
// 要使用的GPIO引脚(根据你的硬件连接修改,比如GPIO18)
static int gpio_pin = 18;
// 定时器间隔(单位ms,可根据精度需求调整)
static unsigned int timer_interval_ms = 100;
// sysfs节点对象
static struct kobject *freq_kobj;

中断处理函数(仅做计数)

static irqreturn_t edge_interrupt_handler(int irq, void *dev_id) {
    // 原子递增计数器,避免并发问题
    atomic_inc(&edge_count);
    return IRQ_HANDLED;
}

定时器回调(计算频率)

static void freq_timer_callback(struct timer_list *t) {
    // 读取当前计数值
    unsigned int count = atomic_read(&edge_count);
    // 计算频率:频率 = 计数 / 时间间隔(秒)
    current_freq = (count * 1000) / timer_interval_ms;
    // 重置计数器
    atomic_set(&edge_count, 0);
    // 重新启动定时器,实现周期性统计
    mod_timer(&freq_timer, jiffies + msecs_to_jiffies(timer_interval_ms));
}

Sysfs文件接口实现

// 用户读取文件时的回调,返回当前频率
static ssize_t freq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
    return sprintf(buf, "%u\n", current_freq);
}

// 定义sysfs属性:只读权限,对应/sys/kernel/edge_counter/frequency
static struct kobj_attribute freq_attr = __ATTR(frequency, 0444, freq_show, NULL);

驱动初始化与退出函数

static int __init edge_counter_init(void) {
    int irq;
    int ret;

    // 校验并申请GPIO引脚
    if (!gpio_is_valid(gpio_pin)) {
        pr_err("Invalid GPIO pin %d\n", gpio_pin);
        return -EINVAL;
    }
    ret = gpio_request(gpio_pin, "edge_counter");
    if (ret) {
        pr_err("Failed to request GPIO %d\n", gpio_pin);
        return ret;
    }
    gpio_direction_input(gpio_pin);

    // 申请GPIO对应的中断(这里用上升沿触发,可根据VCO信号修改)
    irq = gpio_to_irq(gpio_pin);
    ret = request_irq(irq, edge_interrupt_handler, IRQF_TRIGGER_RISING, 
                      "edge_counter_irq", NULL);
    if (ret) {
        pr_err("Failed to request IRQ %d\n", irq);
        gpio_free(gpio_pin);
        return ret;
    }

    // 初始化定时器
    timer_setup(&freq_timer, freq_timer_callback, 0);
    mod_timer(&freq_timer, jiffies + msecs_to_jiffies(timer_interval_ms));

    // 创建sysfs节点
    freq_kobj = kobject_create_and_add("edge_counter", kernel_kobj);
    if (!freq_kobj) {
        pr_err("Failed to create kobject\n");
        free_irq(irq, NULL);
        gpio_free(gpio_pin);
        del_timer(&freq_timer);
        return -ENOMEM;
    }
    ret = sysfs_create_file(freq_kobj, &freq_attr.attr);
    if (ret) {
        pr_err("Failed to create sysfs file\n");
        kobject_put(freq_kobj);
        free_irq(irq, NULL);
        gpio_free(gpio_pin);
        del_timer(&freq_timer);
        return ret;
    }

    pr_info("Edge counter driver initialized on GPIO %d\n", gpio_pin);
    return 0;
}

static void __exit edge_counter_exit(void) {
    int irq = gpio_to_irq(gpio_pin);
    // 清理资源
    sysfs_remove_file(freq_kobj, &freq_attr.attr);
    kobject_put(freq_kobj);
    del_timer(&freq_timer);
    free_irq(irq, NULL);
    gpio_free(gpio_pin);
    pr_info("Edge counter driver exited\n");
}

module_init(edge_counter_init);
module_exit(edge_counter_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Raspberry Pi Edge Counter/Frequency Detector Driver");
MODULE_AUTHOR("Your Name");

2. 编译与使用步骤

编写Makefile

obj-m += edge_counter.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

编译与加载驱动

  1. 先安装树莓派内核头文件:sudo apt install raspberrypi-kernel-headers
  2. 编译驱动:make
  3. 加载驱动:sudo insmod edge_counter.ko
  4. 查看当前频率:cat /sys/kernel/edge_counter/frequency
  5. 卸载驱动:sudo rmmod edge_counter

3. 优化与注意事项

  • CPU占用控制:整个驱动几乎不占用CPU——只有中断触发时执行1行原子计数,定时器每隔固定时间执行一次轻量计算,比用户空间轮询高效几个数量级
  • 精度调整:如果需要更高精度,可将timer_interval_ms调小(比如10ms);对精度要求不高时,调大到1秒能进一步降低CPU占用
  • 边缘触发选择:根据VCO输出信号,可将中断触发方式改为IRQF_TRIGGER_FALLING(下降沿)或IRQF_TRIGGER_BOTH(双边沿)
  • GPIO引脚选择:尽量避开树莓派的特殊功能引脚(如I2C、SPI引脚),避免硬件冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:16:21