Android旋转编码器驱动开发实战:从硬件抽象到事件处理

1次阅读
没有评论

共计 2524 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

旋转编码器的工业应用场景

旋转编码器作为精密的角位移传感器,在工业控制和车载设备中扮演着关键角色。比如在工业机械臂关节位置反馈、车载中控旋钮操作、医疗设备调节旋钮等场景中,都需要高精度的旋转检测。与普通按键相比,旋转编码器能提供连续的角度变化信息,并且具有更长的使用寿命。

Android 旋转编码器驱动开发实战:从硬件抽象到事件处理

GPIO 轮询 vs 中断驱动方案对比

在驱动实现上,主要有两种方式来处理旋转编码器的信号:

  1. GPIO 轮询方式
  2. 通过定时器周期性检查 GPIO 电平状态
  3. 优点:实现简单,不依赖中断资源
  4. 缺点:CPU 占用率高(实测在 100Hz 采样率下约占用 5% CPU),响应延迟大(通常 10-20ms)
  5. 典型功耗:约 15mA(基于 Cortex-A53 测试)

  6. 中断驱动方式

  7. 在 GPIO 边沿触发中断处理
  8. 优点:响应速度快(<1ms),CPU 占用率低(<1%)
  9. 缺点:需要处理防抖和中断风暴问题
  10. 典型功耗:约 8mA(相同测试条件)

对于大多数应用场景,中断驱动方式更为推荐,特别是在电池供电的设备上。

Linux input 子系统事件上报流程

Linux 输入子系统为旋转编码器提供了标准化的上报接口,核心流程如下:

  1. 驱动通过 input_allocate_device() 分配输入设备
  2. 设置设备能力set_bit(EV_REL, input_dev->evbit)
  3. 注册设备input_register_device(input_dev)
  4. 在中断处理中上报事件:
    input_report_rel(input_dev, REL_WHEEL, delta);
    input_sync(input_dev);

关键数据结构 input_event 定义如下:

struct input_event {
    struct timeval time;
    __u16 type;     // 事件类型,如 EV_REL
    __u16 code;     // 代码,如 REL_WHEEL
    __s32 value;    // 变化量
};

完整驱动代码实现

设备树配置示例

encoder: rotary-encoder {
    compatible = "rotary-encoder";
    gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>,  // A 相
            <&gpio0 13 GPIO_ACTIVE_HIGH>;  // B 相
    linux,axis = <0>;  // REL_X 或 REL_WHEEL
    rotary-encoder,steps = <24>;  // 每转脉冲数
    rotary-encoder,relative-axis;
};

核心驱动代码(带防抖算法)

#include <linux/input.h>
#include <linux/interrupt.h>

#define DEBOUNCE_TIME_MS 5

struct rotary_encoder {
    struct input_dev *input;
    int gpio_a, gpio_b;
    int last_state;
    unsigned int steps;
    struct timer_list debounce_timer;
};

static void encoder_report(struct rotary_encoder *encoder, int delta)
{input_report_rel(encoder->input, REL_WHEEL, delta);
    input_sync(encoder->input);
}

static void encoder_debounce(struct timer_list *t)
{struct rotary_encoder *encoder = from_timer(encoder, t, debounce_timer);
    int state = (gpio_get_value(encoder->gpio_a) << 1) | 
                 gpio_get_value(encoder->gpio_b);

    // 四步解码状态机
    static const signed char state_table[] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};
    int delta = state_table[(encoder->last_state << 2) | state];

    if (delta) {encoder_report(encoder, delta);
    }
    encoder->last_state = state;
}

static irqreturn_t encoder_isr(int irq, void *dev_id)
{
    struct rotary_encoder *encoder = dev_id;
    mod_timer(&encoder->debounce_timer, 
              jiffies + msecs_to_jiffies(DEBOUNCE_TIME_MS));
    return IRQ_HANDLED;
}

// 初始化代码省略...

性能优化实践

采样率与 CPU 占用率关系

采样率(Hz) 轮询模式 CPU 占用 中断模式 CPU 占用
100 5% <1%
500 22% 1.5%
1000 45% 3%

中断风暴防护方案

  1. 在中断处理中延迟实际工作(如上面的 debounce timer)
  2. 使用 disable_irq_nosync()/enable_irq() 临时关闭中断
  3. 设置合理的防抖时间(通常 5 -10ms)

常见问题与解决方案

厂商 IC 波形差异处理

不同厂商的编码器可能产生不同的波形质量:

  • 欧姆龙系列:波形干净,可直接使用
  • 国产低成本编码器:需要增加硬件 RC 滤波或软件防抖
  • 光学编码器:注意处理高速旋转时的脉冲丢失

安卓输入子系统映射问题

在 Android HAL 层需要正确配置输入设备类型:

<!-- 在输入设备配置文件中 -->
<axis id="0" label="Rotary" 
      min="-10" max="10" 
      flat="0" fuzz="0" resolution="1" />

延伸思考

在完成基础驱动开发后,还有三个值得深入探讨的方向:

  1. 如何实现驱动与 HAL 层的动态功耗协商?比如在屏幕关闭时降低采样率
  2. 多编码器同步采样的可行性方案,特别是在工业控制场景
  3. 在 RTOS 环境下的驱动适配挑战,比如中断延迟的确定性保证

通过本文介绍的技术方案,开发者可以快速实现一个稳定可靠的旋转编码器驱动。实际开发中还需要根据具体硬件特性调整防抖参数和采样策略,以获得最佳的用户体验。

正文完
 0
评论(没有评论)