У меня не вызывает сомнения, что проблему, пока контакты
срабатывают хоть как-то, можно решить в два этапа: 1) Фильтруем
импульсы; 2) Декодируем позицию конечным автоматом по таблице. Вот образец рабочего кода из проекта с мотором и энкодером на редукторе, рассчитанным на работу в условиях высокого уровня ЭМП.
#define EncAbit PIND6 //Rotary encoder A input is D6 (PD6)
#define EncBbit PIND7 //Rotary encoder B input is D7 (PD7)
volatile int16_t PositionActual = 0; //Actual position in steps if !PositionUnknown, else not valid
volatile uint8_t DebounceCNT0 = 0; //Debounce "vertical" counters
volatile uint8_t DebounceCNT1 = 0; //
volatile uint8_t DebounceState = 0; //Debounce status (filtered inputs)
volatile uint8_t DebounceToggle = 0; //Toggle flags. Set if the state changed and stay steady for 4 accuisition periods
//Rotary encoder signal decoder, the table and algorithm (c) Blade (Klaus Varis?).
//From here: https://www.avrfreaks.net/sites/default/files/project_files/Rotary%20decoder.pdf
//https://community.atmel.com/projects/quadrature-rotary-decoder
const uint8_t LookupTable[256] = { //__attribute__((progmem))
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00,
0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00,
0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void EncoderSetZero(void) { //Reset position for current encoder state
uint8_t DecoderInitialState;
switch (DebounceState >> 6) { //Get two bits of current filtered encoder levels
case 0b00:
DecoderInitialState = 0x78;
break;
case 0b01:
DecoderInitialState = 0xE1;
break;
case 0b10:
DecoderInitialState = 0x1E;
break;
case 0b11:
DecoderInitialState = 0x87;
break;
}
cli();
DecorderState = DecoderInitialState; //Initialize decoder pre-history
PositionActual = 0; //Reset the position
EncoderErrors = 0; //Clear error counter
AlarmsActive.AsBit.PositionUnknown = 0; //Clear active alarm
Status.AsBit.HomingReq = 0; //Mark homing complete
sei();
}
ISR(TIMER1_OVF_vect) {
//Debounce inputs
uint8_t Delta = (PIND & 0b11111100) ^ DebounceState; //Choose PIND7:PIND2 bits for debounce and compare with previous steady state, see pin designation in header file
DebounceCNT1 = (DebounceCNT1 ^ DebounceCNT0) & Delta; //https://www.compuphase.com/electronics/debouncing.htm Copyright 2019, Thiadmer Riemersma
DebounceCNT0 = ~DebounceCNT0 & Delta;
DebounceToggle = Delta & ~(DebounceCNT0 | DebounceCNT1); //State change flags. "1" means a change just occured.
DebounceState ^= DebounceToggle; //Debounced discrete signals
//Read and decode rotary encoder pulses
if (DebounceToggle & ((1 << EncAbit) | (1 << EncBbit))) { //Check was there an encoder bit toggle or not?
//Encoder change detected. Validate the move
DecorderState = (DecorderState << 2) | (DebounceState >> 6); //Throw two old then add two fresh bits of encoder output (AB)
int8_t DecodeResult = (int8_t)LookupTable[DecorderState]; //Determine encoder state and position change.
PositionActual += (int16_t)DecodeResult; //Account the position
if (DecodeResult == 0) { //Check for possible errors
EncoderErrors++;
if (EncoderErrors >= EncoderErrThreshold) {
AlarmsActive.AsBit.PositionUnknown = 1; //Mark the position as not valid
Status.AsBit.HomingReq = 1; //Request new homing to clear the errors
Status.AsBit.Motion = 0; //Stop motion until a command
if (EncoderErrors >= 255) {
EncoderErrors = 255;
}
}
}
}
//Discrete signals sampled, filtered and corresponding flags are updated.
}
Код выдран из контекста, поэтому в лоб не скомпилируется. Но идея, надеюсь, ясна. К сожалению, ссылки на оригинал фильтра энкодера не действительны.
По примеру. В примере удаляется дребезг сразу шести контактов - двух контактов управления туда-сюда, двух концевиков и двух сигналов энкодера. Фильтрация работает на базе "вертикальных счётчиков". Для большей степени фильтрации, нужно добавить ещё уровней DebounceCNT (на счёт что с чем сравнивать для принятия решения о переключении сигнала - не уточню, нужно вникать).
После фильтрации, анализируем состояние бит энкодера. Таблица на 256 предварительных состояний (это получается ретроспектива на 8/2=4 состояния назад), с текущим получается пятое, можно сделать мажорирование попытку определения направления и факта поворота.