GD32F407VE天空星开发板的旋转编码器的实现

一、旋转编码器概述

1.1 什么是旋转编码器?

旋转编码器 (Rotary Encoder) 是一种机电元件,可以将旋转的机械位移转换成一系列的数字电信号。它本质上是一个"无限旋转的旋钮",为用户提供直观的交互体验。

1.2 主要应用场景

  • 音量调节:音响系统、汽车音响的音量控制
  • 菜单导航:设备设置界面的上下选择
  • 参数设定:仪器仪表的精确数值调整
  • 位置控制:电机转速、机械臂位置等精密控制

1.3 EC12旋转编码器特性

  • 型号:EC12D1524403
  • 引脚功能
    • A、B相:旋转检测
    • D引脚:按下功能
    • C、E引脚:未使用(根据原理图)

二、硬件设计与连接

2.1 引脚定义与接线

//////////////////// EC12 旋钮 ////////////////////
/*  接线说明
交互板           GD32F407
    A  ===>  PD11   (外部中断11)
    B  ===>  PD13   (普通输入)
    D  ===>  PD15   (外部中断15)
   3v3 ===>  3v3
   GND ===>  GND
*/

2.2 硬件抽象层定义

//// 外部中断实现
#define EC12_A_RCU          RCU_GPIOD
#define EC12_A_PIN          GPIOD, GPIO_PIN_11
#define EC12_A_STATE()      gpio_input_bit_get(EC12_A_PIN)
#define EC12_A_ON_TRIG()    EXTI11_on_trig() // 外部中断11回调函数

#define EC12_B_RCU          RCU_GPIOD
#define EC12_B_PIN          GPIOD, GPIO_PIN_13
#define EC12_B_STATE()      gpio_input_bit_get(EC12_B_PIN)

#define EC12_D_RCU          RCU_GPIOD
#define EC12_D_PIN          GPIOD, GPIO_PIN_15
#define EC12_D_STATE()      gpio_input_bit_get(EC12_D_PIN)
#define EC12_D_ON_TRIG()    EXTI15_on_trig() // 外部中断15回调函数

三、旋转检测原理

3.1 正交编码原理

旋转编码器采用正交编码技术,使用两个存在90度相位差的信号通道:

  • A相 (Phase A)B相 (Phase B)
  • 通过两个信号的相位关系判断旋转方向

在这里插入图片描述
EC11是相反的结构,其他都是一样的

3.2 方向判断算法

根据A、B两相的相位关系,可以准确判断旋转方向:

/*
旋转方向判断逻辑:
- 如果 A是下降沿时,此时B为高电平,那么说明,旋钮是顺时针旋转(右转)。
- 如果 A是下降沿时,此时B为低电平,那么说明,旋钮是逆时针旋转(左转)。
- 如果 A是上升沿时,此时B为低电平,那么说明,旋钮是顺时针旋转(右转)。
- 如果 A是上升沿时,此时B为高电平,那么说明,旋钮是逆时针旋转(左转)。
*/

3.3 时钟类比理解

  • 顺时针旋转 (Clockwise, CW) - 右转

    • 想象时钟指针从12点走向1点、2点
    • A相上升沿时,B相为低电平(0)
  • 逆时针旋转 (Counter-Clockwise, CCW) - 左转

    • 与时钟指针相反方向,从12点走向11点
    • A相上升沿时,B相为高电平(1)

四、软件架构设计

4.1 头文件定义

#ifndef __BSP_EC12_H__
#define __BSP_EC12_H__

#include "gpio_cfg.h"

//// 中断实现 
void EC12_init();

// ======= 轻触按钮回调配置
#define EC12_USE_DOWN   1
#define EC12_USE_UP     1
void EC12_on_down();
void EC12_on_up();

// ======= 旋转回调配置
#define EC12_USE_RIGHT  1
#define EC12_USE_LEFT   1
void EC12_on_turn_right(); // 顺时针旋转
void EC12_on_turn_left();  // 逆时针旋转

#endif

4.2 核心实现代码

#include "bsp_ec12.h"

void EC12_init() {
    printf("==EC12_init==\n");
    // B相配置为浮空输入
    GPIO_input(EC12_B_RCU, EC12_B_PIN, GPIO_PUPD_NONE);
}

#define DOWN    RESET
#define UP      SET

// 按键中断处理函数
void EC12_D_ON_TRIG() {
    static FlagStatus last_state = UP; // 上一次状态
    FlagStatus cur_state = EC12_D_STATE(); // 当前状态
    
    if (last_state != cur_state) {
        last_state = cur_state; // 保存状态
        
        if (cur_state == DOWN) {
            #if EC12_USE_DOWN
            EC12_on_down();
            #endif
        } else {
            #if EC12_USE_UP
            EC12_on_up();
            #endif
        }
    }
}

// 旋转检测中断处理函数
void EC12_A_ON_TRIG() {
    static FlagStatus last_state = SET;
    FlagStatus cur_state = EC12_A_STATE();
    
    if (last_state != cur_state) {
        last_state = cur_state;
        
        FlagStatus b_state = EC12_B_STATE();
        
        if (cur_state == DOWN) { // 下降沿
            if (b_state == SET) { // 顺时针旋转
                #if EC12_USE_RIGHT
                EC12_on_turn_right();
                #endif
            } else { // 逆时针旋转
                #if EC12_USE_LEFT
                EC12_on_turn_left();
                #endif 
            }   
        } else { // 上升沿
            if (b_state == RESET) { // 顺时针旋转
                #if EC12_USE_RIGHT
                EC12_on_turn_right();
                #endif
            } else { // 逆时针旋转
                #if EC12_USE_LEFT
                EC12_on_turn_left();
                #endif 
            }
        }
    }
}

五、外部中断配置

5.1 中断配置系统

// EXTI_config.h 配置
#define USE_EXTI_11    1  // A相旋转检测
#define USE_EXTI_15    1  // D相按键检测

#if USE_EXTI_11
    #define EXTI11_RCU          RCU_GPIOD
    #define EXTI11_PORT         GPIOD
    #define EXTI11_PUPD         GPIO_PUPD_PULLUP
    #define EXTI11_SOURCE_PORT  EXTI_SOURCE_GPIOD
    #define EXTI11_TRIG_TYPE    EXTI_TRIG_BOTH  // 双边沿触发
    #define EXTI11_PRIORITY     2, 2
#endif

#if USE_EXTI_15
    #define EXTI15_RCU          RCU_GPIOD
    #define EXTI15_PORT         GPIOD
    #define EXTI15_PUPD         GPIO_PUPD_PULLUP
    #define EXTI15_SOURCE_PORT  EXTI_SOURCE_GPIOD
    #define EXTI15_TRIG_TYPE    EXTI_TRIG_BOTH  // 双边沿触发
    #define EXTI15_PRIORITY     2, 2
#endif

5.2 通用中断配置函数

static void EXTI_config(rcu_periph_enum rcu, uint32_t port, uint32_t pin, 
                       uint32_t pull_up_down, uint8_t exti_port, uint8_t exti_pin,
                       exti_line_enum linex, exti_trig_type_enum trig_type,
                       uint8_t nvic_irq, uint8_t nvic_irq_pre_priority, 
                       uint8_t nvic_irq_sub_priority) {
    
    // 引脚初始化
    GPIO_input(rcu, port, pin, pull_up_down);
    
    // 系统配置时钟使能
    rcu_periph_clock_enable(RCU_SYSCFG);
    
    // 中断线连接配置
    syscfg_exti_line_config(exti_port, exti_pin);
    
    // EXTI初始化
    exti_init(linex, EXTI_INTERRUPT, trig_type);
    exti_interrupt_enable(linex);
    
    // NVIC中断配置
    nvic_irq_enable(nvic_irq, nvic_irq_pre_priority, nvic_irq_sub_priority);
}

六、应用层回调实现

6.1 基础回调函数

// ============= 用户回调函数,可移至业务模块
void EC12_on_down() { // 按下
    printf("ec12 down\n");
    // 实际应用中可执行菜单确认、功能切换等操作
}

void EC12_on_up() { // 抬起
    printf("ec12 up\n");
    // 可执行释放状态处理
}

void EC12_on_turn_right() { // 顺时针旋转
    printf("----顺时针旋转(右转)----\n");
    // 实际应用:音量增加、数值增大、菜单下翻等
}

void EC12_on_turn_left() {  // 逆时针旋转
    printf("----逆时针旋转(左转)----\n");
    // 实际应用:音量减小、数值减小、菜单上翻等
}

6.2 高级应用示例

// 音量控制应用
static int volume = 50;

void EC12_on_turn_right_volume() {
    if(volume < 100) {
        volume += 2;
        printf("音量增加: %d%%\n", volume);
        // 更新显示或发送控制命令
    }
}

void EC12_on_turn_left_volume() {
    if(volume > 0) {
        volume -= 2;
        printf("音量减少: %d%%\n", volume);
        // 更新显示或发送控制命令
    }
}

void EC12_on_down_volume() {
    printf("静音切换\n");
    // 实现静音功能
}

// 菜单导航应用
static int menu_index = 0;
static const char* menu_items[] = {"设置", "音乐", "视频", "系统"};

void EC12_on_turn_right_menu() {
    menu_index = (menu_index + 1) % 4;
    printf("选中: %s\n", menu_items[menu_index]);
}

void EC12_on_turn_left_menu() {
    menu_index = (menu_index - 1 + 4) % 4;
    printf("选中: %s\n", menu_items[menu_index]);
}

void EC12_on_down_menu() {
    printf("进入: %s\n", menu_items[menu_index]);
    // 执行进入菜单操作
}

七、防抖与稳定性优化

7.1 软件防抖处理

// 增强版旋转检测,添加时间防抖
#define DEBOUNCE_DELAY_MS 5

void EC12_A_ON_TRIG_enhanced() {
    static FlagStatus last_state = SET;
    static uint32_t last_time = 0;
    
    FlagStatus cur_state = EC12_A_STATE();
    uint32_t current_time = get_system_tick();
    
    // 时间防抖检查
    if((current_time - last_time) < DEBOUNCE_DELAY_MS) {
        return;
    }
    
    if (last_state != cur_state) {
        last_state = cur_state;
        last_time = current_time;
        
        // ... 原有的方向判断逻辑
    }
}

7.2 状态机实现

typedef enum {
    STATE_IDLE,
    STATE_ROTATING,
    STATE_BUTTON_PRESSED
} ec12_state_t;

static ec12_state_t current_state = STATE_IDLE;

void EC12_state_machine_handler() {
    switch(current_state) {
        case STATE_IDLE:
            // 等待事件
            break;
        case STATE_ROTATING:
            // 处理旋转事件
            break;
        case STATE_BUTTON_PRESSED:
            // 处理按钮事件
            break;
    }
}

八、系统集成与测试

8.1 完整初始化序列

void System_EC12_init() {
    // 初始化外部中断系统
    EXTI_init();
    
    // 初始化旋转编码器
    EC12_init();
    
    printf("旋转编码器系统初始化完成\n");
    printf("支持功能: 旋转检测、按键检测\n");
}

void Application_main() {
    System_EC12_init();
    
    while(1) {
        // 主循环处理其他任务
        // 旋转编码器通过中断自动处理
        
        // 系统休眠或处理其他业务
        delay_ms(10);
    }
}

8.2 调试与测试

// 调试信息输出
void EC12_debug_info() {
    printf("A相状态: %d\n", EC12_A_STATE());
    printf("B相状态: %d\n", EC12_B_STATE());
    printf("D按键状态: %d\n", EC12_D_STATE());
}

// 测试模式
void EC12_test_mode() {
    printf("进入旋转编码器测试模式\n");
    printf("旋转旋钮观察方向检测\n");
    printf("按下旋钮测试按键功能\n");
}

九、设计要点与最佳实践

9.1 硬件设计注意事项

  1. 上拉电阻:确保A、B、D引脚都有合适的上拉电阻
  2. 信号滤波:在噪声环境中添加硬件RC滤波器
  3. 布局优化:编码器尽量靠近MCU,减少信号线长度
  4. 电源稳定:确保3.3V电源干净稳定

9.2 软件优化建议

  1. 中断优先级:合理设置中断优先级,避免丢失脉冲
  2. 资源占用:中断处理函数尽量简洁,快速退出
  3. 状态保存:使用static变量保存历史状态
  4. 模块化设计:回调机制便于功能扩展

9.3 可靠性设计

// 错误检测与恢复
#define MAX_MISSED_PULSES 10

static int error_count = 0;

void EC12_error_handler() {
    error_count++;
    if(error_count > MAX_MISSED_PULSES) {
        printf("旋转编码器异常,尝试重新初始化\n");
        EC12_init();
        error_count = 0;
    }
}

十、总结

  1. 精确的方向检测:基于正交编码原理,准确识别顺时针/逆时针旋转
  2. 多功能集成:同时支持旋转检测和按键功能
  3. 高效的中断处理:双边沿触发,确保不丢失任何操作
  4. 灵活的架构:回调机制便于功能扩展和模块化设计
  5. 稳定性保障:软件防抖、错误恢复等可靠性设计
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐