树莓派基于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
evteston 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




