инкрементный энкодер на 1024 импульса

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017
//=============================================================================
void Encoder_A() iv IVT_ADDR_INT0 ics ICS_AUTO
{
    if(flag_enc_b)
    {
        flag_enc_b = 0;
    }
    else
    {
        enc_impulse_count++;
        flag_enc_a = 1;
    }
}
//==============================================================================
void Encoder_B() iv IVT_ADDR_INT1 ics ICS_AUTO
{
    if(flag_enc_a)
    {
        flag_enc_a = 0;
    }
    else
    {
        enc_impulse_count--;
        flag_enc_b = 1;
    }
}

подарили энкодер, питание 5 вольт и выходы A и B, 1024 импульса на оборот. Решил поиграться им. Инициализировал UART, два внешних прерывания, каждую секунду в сериал отправляю значение переменной enc_impulse_count (тип unsigned int). И происходит непонятное. Если я вращаю энкодер по часовой стрелке, значение инкрементируется, останавливаю на пару секунд, вращаю обратно - значение декрементируется, то есть все как и должно быть. Но если я вращая по часовой стрелке не останавливаясь начинаю вращать обратно, то значение все равно инкрементируется. Код выше это два внешних прерывания. Там же все верно? просто я понять немогу - либо энкодер неисправный, либо я в прерывании накосячил

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

но скорее изначально кривой код написали. Для нормальной работы нужен один вывод на прерывание а другой просто вход.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

qwone пишет:

но скорее изначально кривой код написали. Для нормальной работы нужен один вывод на прерывание а другой просто вход.

что то типа такого?

void Encoder_A() iv IVT_ADDR_INT0 ics ICS_AUTO
{
    if(PIN)
    {
        enc_impulse_count++;
    }
    else
    {
        enc_impulse_count--;
    }
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Похоже но скорее digitalRead(Bpin).

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

qwone пишет:

Похоже но скорее digitalRead(Bpin).

спасибо, завтра отпишусь о результате

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qwone пишет:

но скорее изначально кривой код написали. Для нормальной работы нужен один вывод на прерывание а другой просто вход.

С первым утверждением соглашусь, а со вторым - нет. Если необходимо максимальное разрешение энкодера, то оба контакта нужно сажать на прерывания.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

andriano пишет:

С первым утверждением соглашусь, а со вторым - нет. Если необходимо максимальное разрешение энкодера, то оба контакта нужно сажать на прерывания.

И что. Код окажется симетричным. По прерыванию первого смотришь после состояние второго и также на втором обработчике прерывания. Но все равно важнее уяснить как это должно функционировать и какой код писать. А дальше оно просто само выйдет.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

qwone пишет:

И что. Код окажется симетричным. По прерыванию первого смотришь после состояние второго и также на втором обработчике прерывания. Но все равно важнее уяснить как это должно функционировать и какой код писать. А дальше оно просто само выйдет.

[/quote]

Ну так то по логике первый код это и выполняет. Если первым пришел сигнал со входа А я проверяю, не было ли сигнала со входа B, если небыло, то инкрементирую переменную, а если был, то просто сбрасываю флаг. Во втором обработчике тоже самое

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

По мне так Вы половину информации пропускаете. Вы упростили логику до того, что и "ребёнка выплеснули". Там надо при каждом прерывании смотреть на два состояния обоих датчиков: прошлое и нынешнее. Там ведь код Грея обраузется. Сигналы меняются примерно так:

А теперь представьте, что Вы начинаете вращать в обратную стоорону. Сразу вопросы - из какого положения? Тут много вариантов.

Вам нужно расписать все 4 возможных положения (00, 01, 10 и 11), расписать все положения, которые могут получиться из каждого вращением в какую-либо сторону и соответвенно реализовать этот разбор. Погуглите, материалов довольно много.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

ЕвгенийП пишет:

Вам нужно расписать все 4 возможных положения (00, 01, 10 и 11), расписать все положения, которые могут получиться из каждого вращением в какую-либо сторону и соответвенно реализовать этот разбор. Погуглите, материалов довольно много.

както громоздко у меня получилось

unsigned char pre_position = 0;
unsigned char curr_position = 0;
//режим прерывания - вызов вектора при любом изменении уровня сигнала
void Encoder_A_Interrupt()    // вектор прерывания
{
    pre_position = curr_position;
	curr_position = (1 << PIN_A) | PIN_B;
	     if(pre_position == 00 && curr_position == 10) {enc_impulse_count++;}
	else if(pre_position == 00 && curr_position == 01) {enc_impulse_count--;}
	else if(pre_position == 10 && curr_position == 11) {enc_impulse_count++;}
	else if(pre_position == 10 && curr_position == 00) {enc_impulse_count--;}
	else if(pre_position == 11 && curr_position == 01) {enc_impulse_count++;}
	else if(pre_position == 11 && curr_position == 10) {enc_impulse_count--;}
	else if(pre_position == 01 && curr_position == 00) {enc_impulse_count++;}
	else if(pre_position == 01 && curr_position == 11) {enc_impulse_count--;}
}

 

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

можно как то более короче это дело организовать?

sadman41
Offline
Зарегистрирован: 19.10.2016

Это делается примерно так:

void handleIncEnc(void)
{ volatile static uint8_t stateTerminalA = 0, statePrevTerminalA = 0;
  delayMicroseconds(constEncoderStabilizationDelay);
  stateTerminalA = digitalRead(PIN_A);
  if ((!stateTerminalA) && (statePrevTerminalA)) {
    if (digitalRead(PIN_B))
    {
      value++;
    } else {
      value--;
    }
  }
  statePrevTerminalA = stateTerminalA;
}

Обработчик садится на Interrupt CHANGE state.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

semaawp пишет:

както громоздко у меня получилось

Слово "матрица" тебе чего-нибудь говорит? У тебя N состояний - создай двумерный массив, и заполни его значениями "что делать с переменной". По горизонтали - состояние 1, по вертикали - состояние 2, пересечение - даёт искомое действие.

И код станет менее громоздким ;)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

semaawp пишет:

можно как то более короче это дело организовать?

Конечно. Старое состояние - два бита, новое - ещё два, формируете 4-битное число и по нему расписываете switch

Вы бы посмотрели библиотеку encoder.h - там всё это уже есть.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

немного покумекав написал такой код и все заработало как надо. Правда он будет работать только оптических или магнитных энкодерах, для механического нужна фильтрация сигнала.

//Режим прерываний - вызов вектора при любой смене уровня сигнала
//==============================================================================
void Encoder_A() iv IVT_ADDR_INT0 ics ICS_AUTO        //Прерывание INT0
{
    if(PIND.B3)
    {
        if(PIND.B2) enc_impulse_count--;
        else        enc_impulse_count++;
    }
    else
    {
        if(PIND.B2) enc_impulse_count++;
        else        enc_impulse_count--;
    }
}
//==============================================================================
void Encoder_B() iv IVT_ADDR_INT1 ics ICS_AUTO        //Прерывание INT1
{
    if(PIND.B2)
    {
        if(PIND.B3) enc_impulse_count++;
        else        enc_impulse_count--;
    }
    else
    {
        if(PIND.B3) enc_impulse_count--;
        else        enc_impulse_count++;
    }
}

 

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Недавно ремонтировал подобный 1024 с Z каналом, заклинило подшипники.
Внутри фото диск.
Разобрать-собрать можно только с помощью шпинделя и задней бабки токарного станка.
Для проверки работы использовал Basic Example из https://www.pjrc.com/teensy/td_libs_Encoder.html
Отметил на валу метку.
Метка  совпадала прекрасно,  10-15 вращений в разные стороны
по 50-100 оборотов в каждую сторону и возврате в "0".

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

semaawp пишет:
немного покумекав написал такой код и все заработало как надо. Правда он будет работать только оптических или магнитных энкодерах, для механического нужна фильтрация сигнала.
А если еще покумекаете, то в механических внутри обработчика прерывания поставите задержку времени на длину дребезга. Первый сигнал пришел дернул обработчик и пошла задержка на дребезг, а дальше все так же само.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

qwone пишет:

semaawp пишет:
немного покумекав написал такой код и все заработало как надо. Правда он будет работать только оптических или магнитных энкодерах, для механического нужна фильтрация сигнала.
А если еще покумекаете, то в механических внутри обработчика прерывания поставите задержку времени на длину дребезга. Первый сигнал пришел дернул обработчик и пошла задержка на дребезг, а дальше все так же само.


Так то да, но с другой стороны задержки в прерывании - плохо

sadman41
Offline
Зарегистрирован: 19.10.2016

Не хотите ставить задержку в обработчике - ставьте микросхему между энкодером и МК.

semaawp
semaawp аватар
Offline
Зарегистрирован: 29.05.2017

sadman41 пишет:

Не хотите ставить задержку в обработчике - ставьте микросхему между энкодером и МК.


Не, у меня оптический дисковый энкодер, меня все устраивает. Я просто предупредил, о возможные проблемах, если кто нибудь захочет просто так этот кусок повторить

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

semaawp пишет:
Так то да, но с другой стороны задержки в прерывании - плохо
Использование механических энкодеров тоже плохо. Но они дешевые. И задержка в прерывании это и есть плата за использование механического энкодера. Можно организовать отработку через millis() не используя прерывания. Но там другие неприятности.