解决旋转编码抖动的方案
本帖最后由 吃樱桃不吐胡 于 2017-1-15 16:06 编辑本人菜鸟一枚,虽然注册论坛账号很长时间了,但一般都是看的多,还是第一次在arduino版块发帖。
这段时间对diy时钟产生了很大兴趣,设想的方案是旋转编码器EC11来一键调整时间。编程过程中发现抖动问题很难消除,按论坛上方法折腾了好久,但效果都不是很理想,昨天一个朋友发给我一个方案,试了一下,果然十分有效,所以发上来,供大家参考。
其实这个方案原作者在2012年就已经在别的论坛上发出了,不过原作者用的是单片机,所以大家在网上找的时候可能容易忽略,原程序如下:
void interrupt main_int(void)
{
if(RBIF)
{
if(int_nu==0 && KEY_A==0) //第一次中断,并且A相是下降沿
{
flag=0;
if(KEY_B) flag=1;//根据B相,设定标志
int_nu=1; //中断计数
}
if(int_nu && KEY_A) //第二次中断,并且A相是上升沿
{
if(KEY_B==0 && flag==1) --power_m;
if(KEY_B && flag==0) ++power_m;
int_nu=0; //中断计数复位,准备下一次
}
RBIF=0;
}
仔细学习了一下,发现作者的方法真的很巧妙,大家用旋转编码器判断方向的时候,经常只用了一个触发沿,作者同时使用了两个,一个下降沿,然后在上升沿再检查一遍,如果两次结果一样,则输出数据,否则放弃。
按照作者思路,我编制了arduino下的程序,另外本人比较懒,上次无疑中发现了MIXLY这个编程神器,所以现在基本上编程都用的mixly,下面的程序也是mixly自动生成的:
#include <PinChangeInt.h>
long num;
long count;
long encoderPinA;
long encoderPinB;
long int_num;
boolean CW_1; // 转向判据1
boolean CW_2; // 转向判据2
int mixly_digitalRead(uint8_t pin) {
pinMode(pin, INPUT);
return digitalRead(pin);
}
void attachPinInterrupt_fun_encoderPinA() {
if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
CW_1 = mixly_digitalRead(encoderPinB);
int_num = 1;
}
if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
CW_2 = !mixly_digitalRead(encoderPinB);
if (CW_1 == true && CW_2 == true) {
count = count + 1;
}
if (CW_1 == false && CW_2 == false) {
count = count - 1;
}
int_num = 0;
}
}
void setup()
{
num = 0;
count = 0;
encoderPinA = 3;
encoderPinB = 4;
int_num = 0;
CW_1 = 0;
CW_2 = 0;
pinMode(encoderPinA, INPUT);
PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
Serial.begin(9600);
}
void loop()
{
while (num != count) {
num = count;
Serial.println(num);
}
}
自动生成的代码有点傻,可能读起来比较麻烦,我把mixly文件放在附件里,大家用mixly看,就很容易了。
经测试,输出非常稳定,没有一点抖动。
另外受这个方案启发,我发现按键防抖用这种双触发方式效果也很好,EC11+DS3231一键调时间的程序也已经编好,一起发上来。
#include <DS3231.h>
#include <Wire.h>
#include <PinChangeInt.h>
long settime;
long hour;
long minute;
long second;
long TEMP_hour;
long TEMP_minute;
long TEMP_second;
boolean h12;
boolean pm;
long num;
long count;
long switchPin;
boolean switchPushed;
long encoderPinA;
long encoderPinB;
long time;
long int_num0;
long int_num;
boolean CW_1;
boolean CW_2;
DS3231 clock;
int mixly_digitalRead(uint8_t pin) {
pinMode(pin, INPUT);
return digitalRead(pin);
}
void attachInterrupt_fun_switchPin() {
if (int_num0 == 0 && mixly_digitalRead(switchPin) == LOW) {
int_num0 = 1;
}
if (int_num0 == 1 && mixly_digitalRead(switchPin) == HIGH) {
switchPushed = true;
time = millis();
settime = settime + 1;
count = 0;
int_num0 = 0;
}
}
void attachPinInterrupt_fun_encoderPinA() {
if (switchPushed) {
if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
CW_1 = mixly_digitalRead(encoderPinB);
int_num = 1;
}
if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
CW_2 = !mixly_digitalRead(encoderPinB);
if (CW_1 == true && CW_2 == true) {
count = count + 1;
}
if (CW_1 == false && CW_2 == false) {
count = count - 1;
}
int_num = 0;
}
time = millis();
}
}
void setup()
{
settime = 0;
hour = clock.getHour(h12, pm);
minute = clock.getMinute();
second = clock.getSecond();
TEMP_hour = 0;
TEMP_minute = 0;
TEMP_second = 0;
h12 = false;
pm = true;
num = 0;
count = 0;
switchPin = 2;
switchPushed = false;
encoderPinA = 3;
encoderPinB = 4;
time = millis();
int_num0 = 0;
int_num = 0;
CW_1 = 0;
CW_2 = 0;
pinMode(switchPin, INPUT);
pinMode(encoderPinA, INPUT);
Wire.begin();
attachInterrupt(digitalPinToInterrupt(switchPin),attachInterrupt_fun_switchPin,CHANGE);
PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
Serial.begin(9600);
}
void loop()
{
if (TEMP_second != clock.getSecond() && !switchPushed) {
TEMP_hour = clock.getHour(h12, pm);
TEMP_minute = clock.getMinute();
TEMP_second = clock.getSecond();
Serial.println(String(TEMP_hour) + String(String(":") + String(String(TEMP_minute) + String(String(":") + String(TEMP_second)))));
}
if (switchPushed) {
if (millis() - time > 10000) {
switchPushed = false;
settime = 0;
Serial.println("Time OUT!");
}
}
if (switchPushed) {
switch (settime) {
case 1:
hour = TEMP_hour + count;
if (hour >= 24) {
hour = (long) (hour) % (long) (24);
}
if (hour < 0) {
hour = (long) (hour) % (long) (24) + 24;
}
if (num != hour) {
num = hour;
Serial.println(String("SEThour:") + String(num));
}
break;
case 2:
clock.setHour(hour);
minute = TEMP_minute + count;
if (minute >= 60) {
minute = (long) (minute) % (long) (60);
}
if (minute < 0) {
minute = (long) (minute) % (long) (60) + 60;
}
if (num != minute) {
num = minute;
Serial.println(String("SetMinute:") + String(num));
}
break;
case 3:
clock.setMinute(minute);
second = TEMP_second + count;
if (second >= 60) {
second = (long) (second) % (long) (60);
}
if (second < 0) {
second = (long) (second) % (long) (60) + 60;
}
if (num != second) {
num = second;
Serial.println(String("SetSecond:") + String(num));
}
break;
case 4:
clock.setSecond(second);
switchPushed = false;
settime = 0;
Serial.println("Set time successed!");
break;
}
}
}
忘了说连线方式了:其实都是最基本的联系,看代码也能知道:ec11的A联arduino的3,B联4,按键接的是是arduino的2,AB和按键都接10k的上拉。DS3231也是最基本连线:SCL接A5,SDA接A4. 正需要,可惜刚注册积分不够,再等等吧:Q 用一个中断的原因是要把另一个中断留给其他东西用,毕竟uno nano这类就只有两个外部中断,实测编码器AB相加电容接地再用一个中断很稳定 积分 不够啊郁闷 下载不了 答复。研究中 我也想参考一下,不过积分不够!:hug: Highnose 发表于 2021-2-4 13:59
void ChkBMQ(){
BAKT=T;
:victory::victory::victory:
试了您的代码,很好用。
请教一个问题:BAKT和T分别是什么? Highnose 发表于 2021-2-10 11:40
这是我程序里的一个变量,当时没去掉
谢谢,明白了
正想学习学习,谢谢了!
int mixly_digitalRead(uint8_t pin) {
pinMode(pin, INPUT);
return digitalRead(pin);
没有搞懂,这段是什么?
正需要,可惜刚注册积分不够,再等等吧 正需要,可惜刚注册积分不够,再等等吧 大卫1999 发表于 2021-2-20 13:24
正想学习学习,谢谢了!
int mixly_digitalRead(uint8_t pin) {
pinMode(pin, INPUT);
就是个digitalRead,这是mixly自动生成的一段程序,很多冗余的,当时偷懒没修改。这个帖子只是提供给大家一个思路哈,理解第一段程序就可以编自己的程序了,思路很简单 感谢分享! 积分 不够啊 郁闷 下载不了 正需要,可惜刚注册积分不够,再等等吧
页:
[1]
2