2.2 EC11旋转编码器
旋转编码器是一种将旋转位移转换为一连串数字脉冲信号的旋转式传感器。这些脉冲用来控制角位移。读数系统通常采用差分方式,即将两个波形一样但相位差为180°的不同信号进行比较,以便提高输出信号的质量和稳定性。读数是在两个信号的差别基础上形成的,从而消除了干扰。
2.2.1 模块来源
采购链接:
旋转编码器模块 工作电压5V
资料下载链接:
https://pan.baidu.com/s/18pp1KaT2V_llizWvdIXtKA?pwd=8889
资料提取码:8889
2.2.2 规格参数
工作电压:5V
工作电流:1MA
模块尺寸:18 x 25 mm
旋转角度: 360度
通信协议:相位差
管脚数量:5 Pin(2.54mm间距排针)
工作电流:1MA
模块尺寸:18 x 25 mm
旋转角度: 360度
通信协议:相位差
管脚数量:5 Pin(2.54mm间距排针)
2.2.3 移植过程
我们的目标是在天空星GD32F407上能够判断旋转方向、旋转次数和是否按下的功能。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
2.2.3.1 查看资料
2.2.3.2 引脚选择
2.2.3.3 移植至工程
在文件ec11.c中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2023-11-02 LCKFB-yzh first version
*/
#include "ec11.h"
#include "board.h"
#include "stdio.h"
/******************************************************************
* 函 数 名 称:Encoder_GPIO_Init
* 函 数 说 明:旋转编码器引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:使用定时器每10Ms扫描一次是否有旋转,即通过定时器进行消抖
******************************************************************/
void Encoder_GPIO_Init(void)
{
timer_parameter_struct timere_initpara; // 定义定时器结构体
rcu_periph_clock_enable(RCU_ENCODER_LCK); // 开启CLK引脚时钟
rcu_periph_clock_enable(RCU_ENCODER_DT); // 开启DT引脚时钟
rcu_periph_clock_enable(RCU_ENCODER_SW); // 开启SW引脚时钟
rcu_periph_clock_enable(BSP_TIMER_RCU); // 开启定时器时钟
/* CK_TIMERx = 4 x CK_APB1 = 4x42M = 168MHZ */
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // 配置定时器时钟
/* 配置 SW 为上拉输入模式(必须上拉) */
gpio_mode_set(PORT_ENCODER_SW,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_ENCODER_SW);
/* 配置 CLK 为输入模式 上拉模式 */
gpio_mode_set(PORT_ENCODER_LCK,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_ENCODER_LCK);
/* 配置 DT 为输入模式 上拉模式 */
gpio_mode_set(PORT_ENCODER_DT,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_ENCODER_DT);
timer_deinit(BSP_TIMER); // 复位定时器
/* 配置定时器参数 10Ms 扫描一次 */
/* f = 168,000,000 / 1680 * 1000) = 100Hz */
/* T = 1/f = 1/100 = 0.01 S = 10 MS */
timere_initpara.prescaler = 1680-1; // 时钟预分频值 0-65535
timere_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐
timere_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timere_initpara.period = 1000-1; // 周期
/* 在输入捕获的时候使用 数字滤波器使用的采样频率之间的分频比例 */
timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // 分频因子
/* 只有高级定时器才有 配置为x,就重复x+1次进入中断 */
timere_initpara.repetitioncounter = 0; // 重复计数器 0-255
timer_init(BSP_TIMER,&timere_initpara); // 初始化定时器
/* 配置中断优先级 */
nvic_irq_enable(BSP_TIMER_IRQ,2,2); // 设置中断优先级为 2,2
/* 使能中断 */
timer_interrupt_enable(BSP_TIMER,TIMER_INT_UP); // 使能更新事件中断
/* 使能定时器 */
timer_enable(BSP_TIMER);
}
/******************************************************************
* 函 数 名 称:Encoder_Scanf
* 函 数 说 明:判断旋转编码器是否有往哪一个方向旋转
* 函 数 形 参:无
* 函 数 返 回:1=正转 2=反转
* 作 者:LC
* 备 注:哪一边正转哪一边反转不需要太在意,你说的算
******************************************************************/
char Encoder_Scanf(void)
{
static FlagStatus EC11_CLK_Last= RESET; //EC11的LCK引脚上一次的状态 (A相)
static FlagStatus EC11_DT_Last = RESET; //EC11的DT引脚上一次的状态(B相)
char ScanResult = 0;
//当A发生跳变时采集B当前的状态,并将B与上一次的状态进行对比。
if(GET_CLK_STATE !=EC11_CLK_Last)
{ //若A 0->1 时,B 1->0 正转;若A 1->0 时,B 0->1 正转;
//若A 0->1 时,B 0->1 反转;若A 1->0 时,B 1->0 反转
if(GET_CLK_STATE == 1) //EC11_A和上一次状态相比,为上升沿
{
//EC11_B和上一次状态相比,为下降沿
if((EC11_DT_Last == 1)&&(GET_DT_STATE == 0))
ScanResult = 1; //正转
//EC11_B和上一次状态相比,为上升沿
if((EC11_DT_Last == 0)&&(GET_DT_STATE == 1))
ScanResult = 2; //反转
//>>>>>>>>>>>>>>>>下面为正转一次再反转或反转一次再正转处理<<<<<<<<<<<<<<<<//
//A上升沿时,采集的B不变且为0
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 0))
ScanResult = 1; //正转
//A上升沿时,采集的B不变且为1
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 1))
ScanResult = 2; //反转
}
else //EC11_A和上一次状态相比,为下降沿
{
//EC11_B和上一次状态相比,为下降沿
if((EC11_DT_Last == 1)&&(GET_DT_STATE == 0))
ScanResult = 2; //反转
//EC11_B和上一次状态相比,为上升沿
if((EC11_DT_Last == 0)&&(GET_DT_STATE == 1))
ScanResult = 1; //正转
//>>>>>>>>>>>>>>>>下面为正转一次再反转或反转一次再正转处理<<<<<<<<<<<<<<<<//
//A上升沿时,采集的B不变且为0
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 0))
ScanResult = 2; //反转
//A上升沿时,采集的B不变且为1
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 1))
ScanResult = 1; //正转
}
EC11_CLK_Last = GET_CLK_STATE; //更新编码器上一个状态暂存变量
EC11_DT_Last = GET_DT_STATE; //更新编码器上一个状态暂存变量
return ScanResult; //返回值的取值: 0:无动作; 1:正转; 2:反转;
}
return 0;
}
/******************************************************************
* 函 数 名 称:Encoder_Sw_Down
* 函 数 说 明:判断编码器是否被按下
* 函 数 形 参:无
* 函 数 返 回:0=没有被按下 1=被按下
* 作 者:LC
* 备 注:请注意消抖
******************************************************************/
unsigned char Encoder_Sw_Down(void)
{
//没有按下
if( gpio_input_bit_get(PORT_ENCODER_SW, GPIO_ENCODER_SW) == SET )
{
delay_ms(100);//消抖
return 0;
}
else//按下
{
delay_ms(100);//消抖
// printf("down\r\n");
return 1;
}
}
/******************************************************************
* 函 数 名 称:Encoder_Rotation_left
* 函 数 说 明:左旋转服务函数。当编码器左转时,需要执行的操作
* 函 数 形 参:无
* 函 数 返 回:向左旋转次数
* 作 者:LC
* 备 注:无
******************************************************************/
int Encoder_Rotation_left(void)
{
static int left_num = 0;//左转次数
left_num++;
/* 你的代码写在此处 */
printf("left num = %d\r\n",left_num);
/* 你的代码写在此处 */
return left_num;
}
/******************************************************************
* 函 数 名 称:Encoder_Rotation_left
* 函 数 说 明:右旋转服务函数。当编码器右转时,需要执行的操作
* 函 数 形 参:无
* 函 数 返 回:向右旋转次数
* 作 者:LC
* 备 注:无
******************************************************************/
int Encoder_Rotation_right(void)
{
static int right_num = 0;//右转次数
right_num++;
/* 你的代码写在此处 */
printf("right num = %d\r\n",right_num);
/* 你的代码写在此处 */
return right_num;
}
/************************************************
函数名称 : BSP_TIMER_IRQHandler
功 能 : 基本定时器中断服务函数
参 数 : 无
返 回 值 : 无
作 者 : LC
*************************************************/
void BSP_TIMER_IRQHANDLER(void)
{
static char dat = 0;
/* 这里是定时器中断 */
if(timer_interrupt_flag_get(BSP_TIMER,TIMER_INT_FLAG_UP) == SET)
{
timer_interrupt_flag_clear(BSP_TIMER,TIMER_INT_FLAG_UP); // 清除中断标志位
/* 执行功能 */
dat = Encoder_Scanf();//扫描编码器是否扭动
if( dat != 0 )//如果有转动
{
if( dat == 2 )
{
Encoder_Rotation_left();
}
else
{
Encoder_Rotation_right();
}
}
}
}
在文件ec11.h中,编写如下代码。
#ifndef _BSP_ENCODER_H_
#define _BSP_ENCODER_H_
#include "gd32f4xx.h"
//SW引脚
#define RCU_ENCODER_SW RCU_GPIOA
#define PORT_ENCODER_SW GPIOA
#define GPIO_ENCODER_SW GPIO_PIN_7
//CLK引脚
#define RCU_ENCODER_LCK RCU_GPIOA
#define PORT_ENCODER_LCK GPIOA
#define GPIO_ENCODER_LCK GPIO_PIN_6
//DT引脚
#define RCU_ENCODER_DT RCU_GPIOA
#define PORT_ENCODER_DT GPIOA
#define GPIO_ENCODER_DT GPIO_PIN_4
//获取CLK引脚的状态
#define GET_CLK_STATE gpio_input_bit_get(PORT_ENCODER_LCK,GPIO_ENCODER_LCK)
//获取DT引脚的状态
#define GET_DT_STATE gpio_input_bit_get(PORT_ENCODER_DT,GPIO_ENCODER_DT)
//CLK引脚的外部中断
#define CLK_EXTI_IRQN EXTI5_9_IRQn // 外部中断6
#define CLK_EXTI_PORT_SOURCE EXTI_SOURCE_GPIOA // 外部中断端口资源
#define CLK_EXTI_PIN_SOURCE EXTI_SOURCE_PIN6 // 外部中断引脚资源
#define CLK_EXTI_LINE EXTI_6 // 外部中断
#define CLK_EXTI_IRQHANDLER EXTI5_9_IRQHandler // 外部中断函数名
//DT
#define DT_EXTI_IRQN EXTI4_IRQn // 外部中断4
#define DT_EXTI_PORT_SOURCE EXTI_SOURCE_GPIOA // 外部中断端口资源
#define DT_EXTI_PIN_SOURCE EXTI_SOURCE_PIN4 // 外部中断引脚资源
#define DT_EXTI_LINE EXTI_4 // 外部中断
#define DT_EXTI_IRQHANDLER EXTI4_IRQHandler // 外部中断函数名
//定时器扫描
#define BSP_TIMER_RCU RCU_TIMER5 // 定时器时钟
#define BSP_TIMER TIMER5 // 定时器
#define BSP_TIMER_IRQ TIMER5_DAC_IRQn // 定时器中断
#define BSP_TIMER_IRQHANDLER TIMER5_DAC_IRQHandler // 定时器中断服务函数
void Encoder_GPIO_Init(void);//旋转编码器初始化
unsigned char Encoder_Sw_Down(void);//编码器是否按下
int Encoder_Rotation_left(void);//左转服务函数
int Encoder_Rotation_right(void);//右转服务函数
#endif
2.2.4 移植验证
在自己工程中的main主函数中,编写如下。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2023-11-02 LCKFB-yzh first version
*/
#include "board.h"
#include "stdio.h"
#include "ec11.h"
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
board_init();
bsp_uart_init();
Encoder_GPIO_Init();
printf("encoder demo start\r\n");
while(1)
{
if( Encoder_Sw_Down() == 1 )//旋转编码器被按下
{
printf("Encoder down\r\n");
}
}
}
移植现象:向右旋转10次,向左旋转10次,按下一次。
移植成功案例:链接:https://pan.baidu.com/s/1wetEoZqXat2zG32HUaZrGQ?pwd=mdg3
提取码:mdg3
–来自百度网盘超级会员V4的分享