Проблема считывания поворотного энкодера с atmega8

#c #embedded #atmega

Вопрос:

Я пытаюсь прочитать поворотный кодер как часть более крупного проекта. Я использую Atmega8L и MPLab X с компилятором XC8.
Проблема, с которой я сталкиваюсь, заключается в том, что кодер работает ненадежно и, похоже, способен только считать вверх, а не вниз. из-
за некоторых других аспектов схемы контакты кодера не могут быть подключены к контактам atmega8 с возможностью прерывания.

Вот мой код, который считывает кодировщик.

 #include <xc.h>

#include "pinmapping.h"
#include "main.h"


static inputEvent lastEvent;
static int aLastState;
static int aState;

//get the last Events data for use elsewhere
inputEvent getLastInputEvent(){
    
    return lastEvent;
    
}

void initEncoder(){
    
    //get our current button state and set rotation change to 0
    if((PINB amp; ENCODER_SW) == 0){
     
        //also save that to our Event
        lastEvent.buttonPressed = 1;
        
    }
    else{
            
        lastEvent.buttonPressed = 0;
        
    }
    lastEvent.rotationChange = 0;
    //also save the current state of the A pin for the first read
    aLastState = (PINB amp; ENCODER_A);
    if(aLastState >= 1){
        aLastState = 1;
    }
    
}

//this should run regularly eg. on a timer interrupt
void readEncoder(){
    
    //first get the state of both pins
    aState = (PINB amp; ENCODER_A);
    int bState = (PINB amp; ENCODER_B);
    if(aState >= 1){
        aState = 1;
    }
    if(bState >= 1){
        bState = 1;
    }
    
    //check if the button is pressed
    if((PINB amp; ENCODER_SW) == 0){

        //save that to our Event
        lastEvent.buttonPressed = 1;

    }
    else{

        lastEvent.buttonPressed = 0;

    }
    
    //check if the state has changed since last time
    if(aState != aLastState){
        
        //check if A and B have the same state -> positive rotation
        if(bState != aState){
            
            //save the rotation change to our Event
            lastEvent.rotationChange = 1;
            
        }
        else{
            
            lastEvent.rotationChange = -1;
            
        }
        
        //save the current state of the A pin for next time
        aLastState = aState;
        
    }
    else{
        
        //if no rotation change happend, save that to our Event
        lastEvent.rotationChange = 0;
        
    }
    
}

 

Нажатие кнопки работает правильно, но изменение поворота никогда не равно -1 и всегда 1 или 0.

ENCODER_SW, ENCODER_A и ENCODER_B являются битовыми масками, которые все равны 0 и одному 1 в бите, соответствующем правильному выводу ввода-вывода.

В основном сначала вызывается initEncoder (после настройки ввода-вывода), а затем в бесконечном цикле вызывается функция, которая, в свою очередь, вызывает readEncoder (), а затем getLastInputEvent (), а затем выполняет еще кое-что, прежде чем вернуться к основному.

Я также пробовал запускать функцию readEncoder() при прерывании таймера примерно каждые мс. Однако это приводит к тому, что он вообще не работает.

В чем проблема? Почему это не работает?

Изменить: Обновил код после того, как я внес некоторые изменения.

Я использую ALPS EC12E с кнопкой.

Функция rotationChange используется для увеличения/уменьшения числа, которое выводится на ЖК-дисплей.

Правка 2: Теперь я думаю, что это проблема со временем, так как я попытался запустить в основном тот же код, только без чего-либо еще на Arduino, и напечатал значения на последовательной консоли, и это сработало в основном идеально.

Я проверил, что еще сделано, и понял, что в моем текущем коде функция readEncoder() выполняется примерно раз в 98 или около того мс.

Когда я запускаю функцию с прерыванием таймера каждые 1 или 2 мс, она просто не работает вообще. Я даже больше не получаю реакции на нажатие кнопки, чего я совсем не понимаю. На самом деле таймер, похоже, вообще не запускается, или, по крайней мере, процедура обслуживания прерываний никогда не выполняется. (Я попытался включить светодиод в настройках и выключить его в isr, но он остается включенным на неопределенный срок.)

Так что, я думаю, теперь возникла новая проблема…
Вот инициализация таймера:

 ASSR = 0x00;        //make sure we are not in asynchronous mode
OCR2 = 3;           //Output compare register
TCNT2 = 0;          //Reset counter to 0
TCCR2 = 0b00001111; //Timer 2 CTC mode, clkio/1024
 

Это делается внутри initDecoder(). Бит OCIE2 уже установлен ранее в основной настройке, что позволяет прерывать таймер 2.

isr, чем просто выглядит так:

 void __interrupt (TIMER2_COMP_vect_num) timer2_isr(){
    
    //Just read the encoder
    readEncoder();
    
}
 

Флаг прерывания сбрасывается аппаратным обеспечением при выполнении isr.

Значение TIMER2_COMP_vect_num также определенно правильное, так как MPLab распознает его, и я даже могу посмотреть на его определение.

Комментарии:

1. «если(aState != aLastState){» что, если bState изменился, а aState нет? Я предполагаю, что это квадратурный кодер?

2. Я НАСТОЯТЕЛЬНО рекомендую вам запустить это в отладчике, чтобы убедиться, что управление происходит так, как вы ожидаете, и в то время, когда вы этого ожидаете.

3. Вы уверены, что ваш код интерпретирует lastEvent переменную всегда после readEncoder() вызова? Я бы поместил код считывания переключателя/кнопки из if else кода, readEncoder() потому что он один и тот же и повторяется. Вы уверены, что ваши контакты настроены в качестве входных данных? Почему бы вам не запустить кодер очень медленно и не мигнуть светодиодами или не отправить сообщение на последовательный порт для его отладки? Я бы переименовал PINB в PORTB .

4. ^^^^^^^^^ ‘мигание некоторых светодиодов-очень хорошая идея, и ее стоит оставить в серийной версии. Вы можете «с первого взгляда» увидеть, что проводка и работа энкодера в порядке:)

5. Я бы также напечатал на ЖК-дисплее значения aState bState и aLastState в самом начале readEncoder() и очень медленно вращал кодер , чтобы вы могли видеть, что происходит.