#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()
и очень медленно вращал кодер , чтобы вы могли видеть, что происходит.