Counting IRQ interrupts on GPIO for anemometer usage

Started by ilario, July 28, 2025, 01:58:51 PM

Previous topic - Next topic

ilario

Dear all,
I am using a A64-OLinuXino-2Ge16G-IND rev. G and I wanted to use this anemometer (as it includes also the detection of the wind direction, otherwise I would have used Adafruit's one):
https://www.sparkfun.com/weather-meter-kit.html
To measure the wind speed, what it does is briefly closing a contact at each rotation of the rotor. The more clicks per second the faster the wind.

Now, how would one measure that?
Does it make sense to use a GPIO exposed IRQ-capable pin and check how fast the interrupt count grows? (clearly, I should add some resistors connecting it to Vcc and to ground, so that I can actually see the click, a suggested circuit is indicated for the rain gauge in the datasheet: https://cdn.sparkfun.com/assets/d/1/e/0/6/DS-15901-Weather_Meter.pdf and it includes also a capacitor for debouncing the reed switch).

From these files https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c and https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c seems that all the PB* and the PL* pins present on the GPIO1 port could be used (they would be in use if I had 2G/3G modem or WiFi&BT chips, but I don't have them on the board).

There is some documentation here: https://linux-sunxi.org/External_interrupts and it seems like I have to write a DeviceTree overlay and a kernel module claiming that interrupt. Then I should be able to access the count from
cat /proc/interrupts or from
cat /sys/kernel/irq/the_number_of_the_interrupt/per_cpu_count
I just wanted to hear your opinion: does it make sense to use an anemometer via a pin configured to count external interrupts?

LubOlimex

No idea but the info is for the sunxi images, and I don't think we've released official sunxi image for the A64 boards. The info probably won't apply for mainline images or Olimage Linux. Even the older images that we released for A64 boards (and are still available the the FTP), the Armbian-based ones, weren't with sunxi kernel.
Technical support and documentation manager at Olimex

ilario

Finally I managed to have some code that seems working (it has been quite challenging).
As you said, I had to heavily adapt the code taken from the linux-sunxi wiki...

For future reference, here goes the code of the Device Tree overlay:

/dts-v1/;
/plugin/;

&{/soc/pinctrl@1c20800} {
    anemometerirq_pins: anemometerirq_pins {
        pins = "PB4";
        function = "gpio_in";
        bias-pull-up;
    };
};

&{/soc} {
    anemometerobserver {
        compatible = "anemometer-irq";
        pinctrl-names = "default";
        pinctrl-0 = <&anemometerirq_pins>;
        interrupt-parent = <&pio>;
        interrupts = <1 4 2>; // Bank 1, pin 4, IRQ_TYPE_EDGE_FALLING
        //status = "okay";
    };
};

The device tree overlay can be compiled with:

dtc -I dts -O dtb -o sun50i-a64-irq-PB4.dtbo sun50i-a64-irq-PB4.dts
And added it to the fdtoverlays list inside the /boot/uEnv.txt file.

Here the code of the kernel module that claims the IRQ:

#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_platform.h>

/* Inspired by https://linux-sunxi.org/External_interrupts */

static irqreturn_t anemometer_irq_handler(int irq, void *dev_id)
{
    //printk(KERN_INFO "anemometer_irq: Interrupt received!\n");
    return IRQ_HANDLED;
}

static int anemometer_probe(struct platform_device *pdev)
{
    int irq, ret;

    irq = platform_get_irq (pdev, 0);
    if (irq < 0) {
        printk(KERN_ERR "Failed to get IRQ: %d\n", irq);
        return irq;
    }

    ret = request_threaded_irq(irq, NULL, anemometer_irq_handler,
        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
        "anemometer-irq", NULL);
    if (ret) {
        dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
        return ret;
    }

    dev_info(&pdev->dev, "Anemometer IRQ %d registered successfully\n", irq);
    return 0;
}

static int anemometer_remove(struct platform_device *pdev)
{
    int irq = platform_get_irq(pdev, 0);
    if (irq > 0)
        free_irq(irq, NULL);
    return 0;
}

static const struct of_device_id anemometer_dt_ids[] = {
    { .compatible = "anemometer-irq" },
    { }
};
MODULE_DEVICE_TABLE(of, anemometer_dt_ids);

static struct platform_driver anemometer_driver = {
    .probe  = anemometer_probe,
    .remove = anemometer_remove,
    .driver = {
        .name          = "anemometer-irq",
        .of_match_table = anemometer_dt_ids,
    },
};

module_platform_driver(anemometer_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ilario Gelmetti");
MODULE_DESCRIPTION("Anemometer Switch Monitor");

And here the content of the Makefile file for compiling the kernel module:

obj-m += anemometer-irq.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

And it can be compiled with:

make
(I had many missing imports, so I had to install a kernel compiled locally).

After importing the module with:

insmod anemometer-irq.ko
You should see the PB4 pin claimed by that module:

# grep PB4 /sys/kernel/debug/pinctrl/1c20800.pinctrl/pinmux-pins
pin 36 (PB4): device soc:anemometerobserver function gpio_in group PB4