GD32F407VE天空星开发板的旋转编码器EC12的实现
本文介绍了基于GD32F407VE天空星开发板的旋转编码器实现方案。主要内容包括:1) 旋转编码器的基本概念和应用场景;2) EC12型号编码器的硬件连接与引脚定义;3) 正交编码原理及方向判断算法设计;4) 软件架构设计,包含中断处理函数和回调机制;5) 外部中断配置方法。文章详细阐述了旋转编码器在嵌入式系统中的硬件接口设计和软件实现,为开发人员提供了完整的解决方案和技术参考。
·
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 硬件设计注意事项
- 上拉电阻:确保A、B、D引脚都有合适的上拉电阻
- 信号滤波:在噪声环境中添加硬件RC滤波器
- 布局优化:编码器尽量靠近MCU,减少信号线长度
- 电源稳定:确保3.3V电源干净稳定
9.2 软件优化建议
- 中断优先级:合理设置中断优先级,避免丢失脉冲
- 资源占用:中断处理函数尽量简洁,快速退出
- 状态保存:使用static变量保存历史状态
- 模块化设计:回调机制便于功能扩展
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;
}
}
十、总结
- 精确的方向检测:基于正交编码原理,准确识别顺时针/逆时针旋转
- 多功能集成:同时支持旋转检测和按键功能
- 高效的中断处理:双边沿触发,确保不丢失任何操作
- 灵活的架构:回调机制便于功能扩展和模块化设计
- 稳定性保障:软件防抖、错误恢复等可靠性设计
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)