You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

树莓派基于rotary_encoder模块实现音量控制的Linux驱动问询

Hey there! Let's walk through getting your rotary encoder-based volume control working on Raspberry Pi, since you've already got the EV_KEY driver registration started. First, let's clear up the confusion around interrupts: the rotary_encoder kernel module already handles GPIO edge interrupts for you, decoding the encoder's rotation into relative axis events (+1/-1 for REL_DIAL). You don't need to write custom interrupt handlers—your job is just to translate those REL_DIAL events into volume key presses.

Step 1: Ensure the Rotary Encoder Module is Configured Correctly

First, make sure the rotary_encoder module is set up to output relative axis events. You can do this via device tree (preferred for Raspberry Pi) or module parameters:

Option A: Device Tree Configuration

Add this to your custom device tree overlay (e.g., rotary-encoder-overlay.dts):

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            rotary_encoder: rotary-encoder {
                compatible = "rotary-encoder";
                gpios = <&gpio 17 GPIO_ACTIVE_HIGH>, <&gpio 27 GPIO_ACTIVE_HIGH>; // Adjust pins to yours
                rotary-encoder,relative-axis;
                rotary-encoder,encoding = "gray";
                pinctrl-names = "default";
                pinctrl-0 = <&rotary_encoder_pins>;
            };

            rotary_encoder_pins: rotary-encoder-pins {
                brcm,pins = <17 27>;
                brcm,function = <0>; // GPIO input
                brcm,pull = <2>; // Enable pull-up resistors
            };
        };
    };
};

Compile and load the overlay, then verify the module creates an input device with evdev (look for /dev/input/eventX where the device name includes "rotary-encoder").

Option B: Module Parameters

If you're not using device tree, load the module with relative axis enabled:

sudo modprobe rotary-encoder relative_axis=1 gpio_a=17 gpio_b=27

Step 2: Translate REL_DIAL Events to Volume Keys

You have two paths here—user-space (quick and easy) or kernel-space (integrated, if you want to stick with your existing EV_KEY driver work).

Path 1: User-Space Solution (No Kernel Code Needed)

Use Python's evdev library to listen for the encoder's REL_DIAL events and simulate volume key presses via uinput. This avoids kernel driver complexity entirely.

First, install dependencies:

sudo apt install python3-evdev

Then create this script (volume_control.py):

import evdev
from evdev import UInput, ecodes as e

# Locate the rotary encoder input device
encoder_dev = None
for path in evdev.list_devices():
    dev = evdev.InputDevice(path)
    if "rotary-encoder" in dev.name:
        encoder_dev = dev
        break

if not encoder_dev:
    print("Rotary encoder device not found!")
    exit(1)

# Create a virtual input device to send volume key events
ui = UInput(
    {e.EV_KEY: [e.KEY_VOLUMEUP, e.KEY_VOLUMEDOWN]},
    name="volume-control-encoder"
)

print("Listening for encoder events...")
try:
    for event in encoder_dev.read_loop():
        if event.type == e.EV_REL and event.code == e.REL_DIAL:
            if event.value == 1:
                # Send volume-up press + release
                ui.write(e.EV_KEY, e.KEY_VOLUMEUP, 1)
                ui.syn()
                ui.write(e.EV_KEY, e.KEY_VOLUMEUP, 0)
                ui.syn()
            elif event.value == -1:
                # Send volume-down press + release
                ui.write(e.EV_KEY, e.KEY_VOLUMEDOWN, 1)
                ui.syn()
                ui.write(e.EV_KEY, e.KEY_VOLUMEDOWN, 0)
                ui.syn()
finally:
    ui.close()

Run it with sudo python3 volume_control.py—your system should now respond to encoder rotations with volume changes.

Path 2: Kernel-Space Driver Integration

If you want to build on your existing EV_KEY driver, write a small kernel module that listens to the rotary_encoder's events and forwards them as volume key presses. Here's a simplified example:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/string.h>

static struct input_handle *encoder_handle;
static struct input_dev *volume_dev;

// Handle incoming rotary encoder events
static int volume_encoder_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
    if (type == EV_REL && code == REL_DIAL) {
        if (value == 1) {
            input_report_key(volume_dev, KEY_VOLUMEUP, 1);
            input_sync(volume_dev);
            input_report_key(volume_dev, KEY_VOLUMEUP, 0);
            input_sync(volume_dev);
        } else if (value == -1) {
            input_report_key(volume_dev, KEY_VOLUMEDOWN, 1);
            input_sync(volume_dev);
            input_report_key(volume_dev, KEY_VOLUMEDOWN, 0);
            input_sync(volume_dev);
        }
    }
    return 0;
}

// Match the rotary encoder input device
static int volume_encoder_match(struct input_handler *handler, struct input_dev *dev)
{
    return strstr(dev->name, "rotary-encoder") != NULL;
}

static const struct input_handler volume_encoder_handler = {
    .event = volume_encoder_event,
    .match = volume_encoder_match,
    .name = "volume-encoder-handler",
};

static int __init volume_encoder_init(void)
{
    int ret;

    // Initialize and register the EV_KEY input device
    volume_dev = input_allocate_device();
    if (!volume_dev) return -ENOMEM;

    volume_dev->name = "volume-control";
    volume_dev->id.bustype = BUS_VIRTUAL;
    set_bit(EV_KEY, volume_dev->evbit);
    set_bit(KEY_VOLUMEUP, volume_dev->keybit);
    set_bit(KEY_VOLUMEDOWN, volume_dev->keybit);

    ret = input_register_device(volume_dev);
    if (ret) goto cleanup_dev;

    // Register the handler to listen for rotary encoder events
    ret = input_register_handler(&volume_encoder_handler);
    if (ret) goto cleanup_reg;

    pr_info("Volume encoder driver loaded\n");
    return 0;

cleanup_reg:
    input_unregister_device(volume_dev);
cleanup_dev:
    input_free_device(volume_dev);
    return ret;
}

static void __exit volume_encoder_exit(void)
{
    input_unregister_handler(&volume_encoder_handler);
    input_unregister_device(volume_dev);
    input_free_device(volume_dev);
    pr_info("Volume encoder driver unloaded\n");
}

module_init(volume_encoder_init);
module_exit(volume_encoder_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Rotary Encoder to Volume Key Translator");
MODULE_AUTHOR("Your Name");

This module registers an input handler that matches the rotary encoder device, translates its REL_DIAL events to volume key presses, and sends them via your existing EV_KEY framework.

Step 3: Test Everything

  • Use evtest /dev/input/eventX (replace X with your encoder device number) to verify REL_DIAL events are generated when you rotate the encoder.
  • Use evtest on the virtual volume control device to confirm key presses are sent.
  • Test your system's volume to ensure it responds to the key events.

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

火山引擎 最新活动