Форсирование энкодера

Да, это верно для энкодеров а ля «потенциометр», используемых на приборной панели, однако при применении энкодера в качестве датчика вращения так небрежно относиться к данным с него — непозволительная роскошь.
Как известно, инкрементальный энкодер выдает код Грея:

И если в энкодере-«потенциометре» мы руководствуемся всем циклом, то для промышленного энкодера выгоднее руководствоваться каждым тактом, что увеличивает число импульсов на оборот аж в четыре раза по сравнению с паспортным. Приличное форсирование, да?
Сложность заключается в определении направления вращения — тут указанный выше алгоритм уже не поможет. Рассмотрим таблицу переходов:
л>_<п
2>0<1
0>1<3
3>2<0
1>3<2
Левый и правые столбцы — это предыдущие значения с энкодера, посередине — текущее.
Составим по ней функцию, которая будет определять направление вращения:
uint8_t EncoderDirection(uint8_t* GrayData){
uint8_t EncoderRealDirection;
switch(GrayData[1]){
case 0:
if (GrayData[0]==2)
EncoderRealDirection=0;
else EncoderRealDirection=1;
break;
case 1:
if (GrayData[0])
EncoderRealDirection=1;
else EncoderRealDirection=0;
break;
case 2:
if (GrayData[0])
EncoderRealDirection=0;
else EncoderRealDirection=1;
break;
case 3:
if (GrayData[0]==2)
EncoderRealDirection=1;
else EncoderRealDirection=0;
break;
}
return EncoderRealDirection;
}
Соответственно, функция вернет единицу для правого вращения и нуль для левого.
Настраиваем контроллер:
#define F_CPU 16000000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "taskmanager.h"
uint8_t EncPortData[2];
uint8_t DriveRealDirection;
int16_t DataLog;
uint8_t number_x=0;
uint8_t hexnumber[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x77,0x7C,0x39,0x5E,0x79,0x71};
int main(void)
{
static uint8_t number_x;
DDRA=0xF0;//индикатор. разряды
DDRC=0xFF;//индикатор. сегменты
PCMSK2|=(1<<PCINT16)|(1<<PCINT17);//активируем ноги энкодера
PCICR|=(1<<PCIE2);//активация PCINT2
TSetup();
TStart();
TReload(0,*led,1);
sei();
while(1)
{
}
}
И описываем прерывание:
ISR (PCINT2_vect){//срабатывает энкодер
EncPortData[1]=(PINK)&0x03;//маска
DriveRealDirection=EncoderDirection(EncPortData);
if (DriveRealDirection){//вправо крутим
DataLog++;//плюсую
}
else
{
DataLog--;//минусую
}
EncPortData[0]=EncPortData[1];//обращаем значения
}
Я слышу бурю негодования по поводу отсутствия защиты от дребезга. Да зачем же она нужна? У меня датчик магнитный, он дребезгу не подвержен. А в механике сами боритесь. Мне больше метод Set/Reset нравится. Он надежнее, да и дарит несколько плюшек в виде длительности нажатия и отпускания.
Для вывода информации использую семисегментник:
void led(){
PORTC=hexnumber[(DataLog>>(number_x*4))&0x0F];
PORTA=(1<<(4+number_x));
number_x++;
if (number_x>3) number_x=0;
}
Функция, при помощи диспетчера вызывается каждые 10мс, обеспечивая полное обновление индикатора за 40мс, что дает частоту обновления 25кадров/с.
Итого мой энкодер на 1024 импульса выдает 4096 импульсов на оборот. Красота да и только(в видео косяк, как видно левый разряд толком не работает, в приведенном выше коде это исправлено):


27 комментариев
У конкретно того энкодера, который в кадре паспортный выход «открытый коллектор», но бывают и комплементарный и дифференциальный выходы.
Ну и сам энкодер отличается точностью выходного сигнала:
Я тут игрался с энкодером стоящим на плате — там механика, так только промежуточная система Set/Reset спасла от дребезга. Но там и не надо каждый фронт следить.
Посему либо Холл, либо оптика.
А защита от дребезга… Расскажу через пару часов про нее.
в другую сторону крутишь, выдает последовательность 1/2
Тут, правда, с определением направления вращения еще проще — XOR(a,b) и делов.
Взял с полки непаянный — та же хрень.
Ложась спать, меня вдруг покоробило «Но я же видел Правильные осциллограммы!»
Утром я буквально вскочил за пол часа до будильника с пониманием того, где закралась проблема. Разумеется, состояние энкодера выводится на семисегментник.
Считывается состояние так:
Выводится это состояние так:
А между ними код, подавляющий дребезг контактов. К слову сказать работающий без сбоев для какой-то там кнопки, где-то на реальной железке. Вчера я его не смог приладить к энкодеру, догадайтесь почему. Код фиксирует фронты по друм линиям. По фронту одной линии сетит, другой — ресетит.
Вопрос знатокам — почему код правильно отрабатывает свое предназначение при вращении энкодера в одну сторону и взрывает мозг в другую?
Какой код? Что за предназначение?
Задача кода — устанавливать выход в единицу по фронту сигнала А, а сбрасывать — по фронту сигнала Б.
Два «независимых» условия, каждое смотрит текущее и предыдущее состояния и в стучае фронта — делает свое дело.
Вот только если сначала появляется фронт А, а потом Б — все работает как надо (что идеально работает как раз с упомянутой кнопкой).
Но вот если крутить наоборот, т.е. сначала Б, потом А, то операция (EncPortData[1]>>1) портит данные. Именно этим меня утром и осенило :) Попытался найти подобный случай в Макконнелле(Совершенный код), но нашел только приведенную выше цитату на первой же странице результатов поиска :)
В первой строчке вообще не понятно зачем скобки вокруг элемента массива…
Спасибо тебе добрый человек.
но без форсирования.
tqfp.org/rln_electro/zaschita-ot-drebezga-mehanicheskogo-enkodera.html