外观
EC11旋转编码器实验
电路分析
简易万用表中仅有一个EC11旋转编码器作为功能按键,通过旋转EC11编码器来实现挡位、模式等切换。
EC11的旋转与按下不同,不能当作一个简单的按键来进行处理,它是一种增量式旋转编码器,在原理图中,可以看到ABC三个输出通道,其中C相接入GND,AB两相输出正交信号,相位差为90°。
- 当编码器正转时,A相的输出信号超前B相90°;
- 当编码器反转时,A相滞后B相90°。
- 在代码程序中,可以通过判断AB两路输出信号的先后变化来判断旋转编码器是正转还是反转。
![]() | ![]() |
---|
理想波形图中,可以直接以AB的某一项为基准,假设以A相为基准,在下降沿到来时判断B相电平的变化,若高电平则是正转,若低电平则是反转。但实际波形往往伴随抖动,AB两相在跳变沿过程中会有抖动影响判断。
解决抖动的办法有很多,比如加入软件延时,硬件滤波等:
硬件滤波方式:此处在原理图中,在A相与B相输出通道上均加入了100nf电容进行滤波,这是硬件滤波的手段,但是实际电容大小需要使用示波器查看抖动脉冲进行调节,过大的电容可能会导致电平判断延时。
软件滤波方式:在A相下降沿触发中断,此时判断并记录B相电平,等待A相上升沿再次判断B相电平,若B相从高电平到低电平变化,此时是正转;反之是反转。若电平没有变化则是抖动,不做处理。
功能实现
创建工程
- 点击左上方文件、选择新建,选择新建MounRiver工程;
- 对工程进行修改,修改完成后点击“完成”按键,系统会自动生成好工程;
- 创建自己的工程库文件夹,分别创建头文件与源文件;
- 将之前的长按开关机代码包含进来修改;
- 对工程库路径进行包含;
代码实现
- 包含头文件,在头文件中包含ch32相关的头文件,方便对相关库函数进行调用;
C
#ifndef MYLIB*INC_EC11_H*
#define MYLIB*INC_EC11_H*
#include "ch32v20x.h"
#include "stdio.h"
#endif /_ MYLIB*INC_EC11_H* _/
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 在源文件中创建两个函数,用于实现EC11-AB相引脚初始化以及对应的中断服务函数,在中断服务函数中进行判断,CH32V208RBT6所有GPIO均可配置为外部中断输入通道,此时我们以A相位基准,在A相的下降沿与上升沿到来时触发中断进行判断,相关中断服务函数在启动文件中有进行声明,这里找到对应的函数名进行重定义使用即可。
- EC11引脚初始化函数内容:
- 需要对GPIO、外部中断线以及中断控制器配置参数;
- 注意使能复用功能时钟,否则默认是GPIO功能;
- 这里仅A相触发中断,A相引脚是PA7,挂载在外部中断线7上,B相直接上拉判断电平即可;
初始化EC11引脚
C
/*
* 函数内容:初始化EC11-A,B相引脚
* 函数参数:无
* 返回值:无
*/
void Init_EC11_GPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO参数结构体
EXTI_InitTypeDef EXTI_InitStructure; //外部中断线参数结构体
NVIC_InitTypeDef NVIC_InitStructure; //中断控制器配置参数结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); //使能GPIO时钟与复用时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //对A、B相引脚进行初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //对A、B相引脚进行初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* GPIOA ----> EXTI_Line0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7); //A相引脚挂载在外部中断线7上;
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升沿&下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //外部中断线5~9触发中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- 中断服务函数内容:在中断服务函数中,定义三个变量,用于保存记录A相触发情况与B相的电平值,当A相下降沿触发一次后,开始记录此时B相的电平值,当A相上升沿到来时再次读取B相电平值,从而判断正反转,最后通过LED灯的点亮或熄灭来检测效果。
EC11引脚中断服务函数
C
/*
* 函数内容:外部中断线5~9中断处理函数
* 函数参数;无
* 返回值:无
*/
void EXTI9_5_IRQHandler(void)
{
static uint8_t cnt = 0; //判断是否先经过下降沿触发中断
static uint8_t B_level_old = 0; //B相上一次电平
uint8_t B_level_now = 0; //B相当前电平
if(EXTI_GetITStatus(EXTI_Line7)!=RESET) //如果是中断线7触发中断
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == RESET){ //如果是A相PA7下降沿触发中断
cnt++; //已经触发了一次中断;
B_level_old = 1; //默认B相为高
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4) == RESET){ //如果为低
B_level_old = 0;
}
}
else if((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == SET) && (cnt == 1)){ //如果是A相PA7上升沿触发中断
cnt = 0;
B_level_now = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4);
if((B_level_old == 1) && (B_level_now == 0)){
Open_LED();
//正转
}
else if((B_level_old == 0) && (B_level_now == 1)){
Close_LED();
//反转
}
}
EXTI_ClearITPendingBit(EXTI_Line7); /* Clear Flag */
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 判断完成后,对函数进行声明;
C
#ifndef MYLIB_INC_EC11_H_
#define MYLIB_INC_EC11_H_
#include "ch32v20x.h"
#include "stdio.h"
void Init_EC11_GPIO(void);
void EXTI9_5_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
#endif /* MYLIB_INC_EC11_H_ */
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 在主函数中对头文件进行声明,注意调用长按开关机函数,否则松手就断电了,中断服务函数不需要在循环中调用,当中断触发时会自动进入中断服务函数中。
主函数
C
#include "debug.h"
#include "ec11.h"
#include "led.h"
#include "powerswitch.h"
/* Global typedef */
/* Global define */
/* Global Variable */
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
Init_Power_Con_GPIO();
Init_LED_GPIO();
Init_EC11_GPIO();
while(1)
{
key_task();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
TIP
旋转EC11编码器,LED灯点亮或熄灭
源码&USB下载工具等软件,均在[gitee资料中](https://gitee.com/chen11232/simple-digital-multimeter)