diff --git a/Inc/general_encoder.h b/Inc/general_encoder.h index ee97247..b4cdb25 100644 --- a/Inc/general_encoder.h +++ b/Inc/general_encoder.h @@ -23,16 +23,17 @@ typedef struct uint16_t pin_a; uint16_t pin_b; uint8_t state; + uint8_t detent_state; uint8_t steps_per_detent; int8_t partial_steps; uint32_t invalid_transitions; } Encoder_HandleTypeDef; -/** Привязать настроенные GPIO A/B; steps_per_detent — от 1 до 4. */ +/** Привязать GPIO A/B; detent_state задаёт фазу фиксации, не фазу включения. */ HAL_StatusTypeDef Encoder_Init(Encoder_HandleTypeDef *encoder, GPIO_TypeDef *port_a, uint16_t pin_a, GPIO_TypeDef *port_b, uint16_t pin_b, - unsigned steps_per_detent); + unsigned steps_per_detent, unsigned detent_state); /** Самостоятельно опросить GPIO, обработать фазы и вернуть -1, 0 или +1 детент. */ int Encoder_Update(Encoder_HandleTypeDef *encoder); diff --git a/Src/general_encoder.c b/Src/general_encoder.c index efd1759..fef2562 100644 --- a/Src/general_encoder.c +++ b/Src/general_encoder.c @@ -22,16 +22,17 @@ static uint8_t read_phases(const Encoder_HandleTypeDef *encoder) * @brief Подготовить независимый экземпляр энкодера к опросу. * @details Входы предварительно настраивает CubeMX или вызывающий код. * Начальный снимок предотвращает событие при включении питания. - * Число переходов задаётся механикой конкретного энкодера. + * Число переходов и фаза фиксации заданы конфигурацией энкодера. + * Промежуточное положение при включении не становится фазой фиксации. * @return HAL_OK при успехе, HAL_ERROR при неверной конфигурации. */ HAL_StatusTypeDef Encoder_Init(Encoder_HandleTypeDef *encoder, GPIO_TypeDef *port_a, uint16_t pin_a, GPIO_TypeDef *port_b, uint16_t pin_b, - unsigned steps_per_detent) + unsigned steps_per_detent, unsigned detent_state) { if (!encoder || !port_a || !port_b || !pin_a || !pin_b || - !steps_per_detent || steps_per_detent > 4U) + !steps_per_detent || steps_per_detent > 4U || detent_state > 3U) { return HAL_ERROR; } @@ -40,6 +41,7 @@ HAL_StatusTypeDef Encoder_Init(Encoder_HandleTypeDef *encoder, encoder->pin_a = pin_a; encoder->pin_b = pin_b; encoder->state = read_phases(encoder); + encoder->detent_state = (uint8_t)detent_state; encoder->steps_per_detent = (uint8_t)steps_per_detent; encoder->partial_steps = 0; encoder->invalid_transitions = 0U; @@ -50,6 +52,9 @@ HAL_StatusTypeDef Encoder_Init(Encoder_HandleTypeDef *encoder, * @brief Превратить последовательность фаз в законченный шаг вращения. * @details Обратные переходы компенсируют дребезг. Одновременная смена обеих * фаз обрывает незавершённый детент и учитывается как пропуск опроса. + * Для 4/2 переходов шаг завершается только в опорной/противоположной + * фазе покоя. Возврат к ней сбрасывает остаток повреждённого детента: + * пропуск не сдвигает последующие шаги и не создаёт люфт при реверсе. * Функция сама читает оба GPIO; вызывающий код задаёт частоту опроса. */ int Encoder_Update(Encoder_HandleTypeDef *encoder) @@ -71,6 +76,19 @@ int Encoder_Update(Encoder_HandleTypeDef *encoder) return 0; } encoder->partial_steps += direction[previous * 4U + state]; + if (encoder->steps_per_detent == 4U || encoder->steps_per_detent == 2U) + { + int step; + if (state != encoder->detent_state && + !(encoder->steps_per_detent == 2U && state == (encoder->detent_state ^ 3U))) + { + return 0; + } + step = encoder->partial_steps >= (int)encoder->steps_per_detent ? 1 : + encoder->partial_steps <= -(int)encoder->steps_per_detent ? -1 : 0; + encoder->partial_steps = 0; + return step; + } if (encoder->partial_steps >= (int)encoder->steps_per_detent) { encoder->partial_steps -= (int8_t)encoder->steps_per_detent;