本帖最后由 RISCVLAR 于 2021-6-22 14:07 编辑
CH32V103应用教程——旋转编码器
最近,手上拿到一个360度旋转编码器模块—KY-040 FOR模块,因此就想用它写一个应用教程。本章教程主要用到GPIO和EXTI(外部中断/事件控制器)。
1、旋转编码器简介 KY-040旋转编码器属于增量编码器,本次实验所用KY-040模块工作电压为5V,转动一圈脉冲数为20,其分别引出5个引脚可与控制板连接,分别是VCC、GND、CLK、DT和SW,其中,VCC和GND引脚用来给模块供电,CLK和DT引脚用来计数和判断正反转,SW引脚用来复位并计数清零。 KY-040旋转编码器可以通过旋转计数正方向和反方向转动过程中输出脉冲的次数,旋转计数不像电位计,这种转动计数是没有限制的。配合旋转编码器上的按键,可以复位到初始状态,即从0开始计数。 通过逻辑分析仪采集旋转编码器波形,得到如下结果: 当旋转编码器正转时,逻辑分析仪采集波形如下: 由逻辑分析仪采集波形可知,在旋转编码器正转时,当CLK引脚设置为上升沿触发时,DT引脚为低电平;当CLK引脚设置为下降沿触发时,DT引脚所在电平为低电平。此外,在截图中还可看到毛刺。 当旋转编码器反转时,逻辑分析仪采集波形如下: 由逻辑分析仪采集波形可知,在旋转编码器反转时,其现象与正转时相反,同时也会出现毛刺现象。 关于旋转编码器工作原理,可自行百度,在此不再进行详细介绍。
2、硬件设计 本章教程使用红外测距模块进行测距。CH32V103开发板与旋转编码器模块的连接方式如下: - CH32V103开发板的VCC引脚与红外测距模块的VCC(5V)引脚连接
- CH32V103开发板的GND引脚与红外测距模块的GND引脚连接
- CH32V103开发板的PA0引脚与红外测距模块的CLK引脚连接
- CH32V103开发板的PA1引脚与红外测距模块的DT引脚连接
- CH32V103开发板的PA2引脚与红外测距模块的SW引脚连接
注意:该旋转编码器使用时存在抖动现象,可通过引脚接电容接GND进行消抖,但并不能完全消抖,只能进行改善。
3、软件设计 本章教程主要进行旋转编码器实验,主要用到GPIO和EXTI,关于旋转编码器计数以及正反转判断主要依据逻辑分析仪采集波形判断,具体程序如下: gpio.h文件 #ifndef __GPIO_H
#define __GPIO_H
#include "ch32v10x_conf.h"
#define CLK GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define DT GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)
#define SW GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
void GPIO_Toggle_INIT(void);
void EXTI0_IRQHandler(void);
#endif
gpio.h文件主要进行相关定义和函数声明; gpio.c文件 #include "gpio.h"
void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
u8 state = 0;
u_int32_t value = 0;
/*******************************************************************************
* Function Name : GPIO_Toggle_INIT
* Description : Initializes GPIOA.0
* Input : None
* Return : None
*******************************************************************************/
void GPIO_Toggle_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* GPIOA ----> EXTI_Line0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); //指定中断/事件线的输入源,实际上是设定外部中断配置寄存器AFIO_EXTICRx的值,此处为PA0
EXTI_InitStructure.EXTI_Line=EXTI_Line0; //EXTI中断/事件线选择,此处选择EXTI_Line0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI模式选择,此处选择为产生中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //EXTI边沿触发事件,此处选择为上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI线
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置子优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET) //EXTI_GetITStatus用来获取中断标志位状态,如果EXTI线产生中断则返回SET,否则返回RESET
{
//Delay_Ms(1);
//配置为上升沿触发中断,CLK=1时,DT=0则为顺时钟,否则为逆时钟;若配置为下降沿触发,则与之相反。
if(CLK == 1)
{
if(DT == 0)
{
printf("顺时针\r\n");
value++;
}
else
{
printf("逆时针\r\n");
value--;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
printf("%d\r\n",value);
}
}
}
gpio.c文件主要对旋转编码器的CLK、DT、SW引脚进行GPIO引脚初始化配置和中断触发配置。 main.c文件 /********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2020/04/30
* Description : Main program body.
*******************************************************************************/
/*
*@Note
串口打印调试例程:
USART1_Tx(PA9)。
本例程演示使用 USART1(PA9) 作打印调试口输出。
*/
#include "debug.h"
#include "gpio.h"
extern u8 state;
extern u_int32_t value;
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf("This is printf example\r\n");
GPIO_Toggle_INIT();
while(1)
{
if(SW == 0)
{
Delay_Ms(1);
if(SW == 0)
{
state = !state;
while(SW==0);
}
}
if(state == 1)
{
value = 0;
state = 0;
printf("RESET\r\n");
printf("%d\r\n",value);
}
}
}
main.c文件主要进行函数初始化并根据SW状态进行复位和计数清零。
4、下载验证 将编译好的程序下载到开发板并复位,与旋转编码器模块接好之后串口打印如下:
|