encoder.c
CODE
#include <avr/io.h>
#include <avr/interrupt.h>
#include "encoder.h"
//Состояния энкодера, которые возвращает подпрограмма
#define ENCODER_LEFT_TURN -1
#define ENCODER_RIGHT_TURN 1
//Определяем состояния энкодера
#define STATE_LEFT 0x08 //Поворот налево
#define STATE_CENTER 0x00 //Состояние покоя
#define STATE_RIGHT 0x10 //Поворот направо
enum {
ENCODER_WAIT,
ENCODER_DEBOUNCE,
ENCODER_WAIT_TURN
};
uint8_t ENCODER_TASK_STATE = ENCODER_WAIT; //Статус энкодера для обработки дребезга
uint8_t ENCODER_TURNED = 0; //По-умолчанию энкодер не менял состояния
//Сюда помещаются данные энкодера для обработки дребезга
static uint8_t ENCODER_PREV_STATE;
static uint8_t ENCODER_MIDDLE_STATE;
static uint8_t ENCODER_LAST_STATE;
volatile uint8_t ENCODER_DEBOUNCE_COUNTER = 0;
volatile int8_t ENCODER_STATE; //Сюда получаем состояние энкодера после обработки в прерывании
volatile uint8_t isEncoderTurned = 0; //Если энкодер изменил состояние, флаг будет равен 1
//Определяем буфер для хранения состояний
static int8_t ENCODER_BUFFER = 0;
void ENCODER_TASK(void)
{
switch (ENCODER_TASK_STATE)
{
case ENCODER_WAIT: //Ждём изменения состояния энкодера
if (READ_ENCODER_STATE() == STATE_CENTER)
{
ENCODER_TURNED = 0;
break;
}
ENCODER_PREV_STATE = READ_ENCODER_STATE();
ENCODER_DEBOUNCE_COUNTER = 0;
ENCODER_TASK_STATE = ENCODER_DEBOUNCE;
break;
case ENCODER_DEBOUNCE: //Убираем дребезг
ENCODER_MIDDLE_STATE = READ_ENCODER_STATE();
if (ENCODER_MIDDLE_STATE != ENCODER_PREV_STATE)
{
ENCODER_DEBOUNCE_COUNTER = 0;
ENCODER_TURNED = 0;
ENCODER_TASK_STATE = ENCODER_WAIT;
break;
}
ENCODER_LAST_STATE = READ_ENCODER_STATE();
if (ENCODER_LAST_STATE != ENCODER_MIDDLE_STATE)
{
ENCODER_DEBOUNCE_COUNTER = 0;
ENCODER_TURNED = 0;
ENCODER_TASK_STATE = ENCODER_WAIT;
break;
}
if (ENCODER_DEBOUNCE_COUNTER > 20)
{
ENCODER_BUFFER = ENCODER_STATE;
isEncoderTurned = 1;
ENCODER_TASK_STATE = ENCODER_WAIT_TURN;
}
break;
case ENCODER_WAIT_TURN:
if (READ_ENCODER_STATE() != STATE_CENTER)
{
ENCODER_DEBOUNCE_COUNTER = 0;
break;
}
if (ENCODER_LAST_STATE == ENCODER_MIDDLE_STATE)
if (ENCODER_LAST_STATE != ENCODER_BUFFER)
ENCODER_BUFFER = ENCODER_LAST_STATE;
if (ENCODER_MIDDLE_STATE == ENCODER_PREV_STATE)
if (ENCODER_MIDDLE_STATE != ENCODER_BUFFER)
ENCODER_BUFFER = ENCODER_MIDDLE_STATE;
if (ENCODER_PREV_STATE == ENCODER_LAST_STATE)
if (ENCODER_PREV_STATE != ENCODER_BUFFER)
ENCODER_BUFFER = ENCODER_PREV_STATE;
if (ENCODER_DEBOUNCE_COUNTER > 80)
{
ENCODER_TASK_STATE = ENCODER_WAIT;
ENCODER_TURNED = 0;
}
}
}
int8_t encoder_state(void)
{
int8_t returnState = ENCODER_NONE;
if (isEncoderTurned)
{
switch (ENCODER_BUFFER)
{
case STATE_CENTER:
returnState = ENCODER_NONE;
break;
case STATE_LEFT:
returnState = ENCODER_LEFT_TURN;
break;
case STATE_RIGHT:
returnState = ENCODER_RIGHT_TURN;
break;
default:
returnState = ENCODER_NONE;
}
isEncoderTurned = 0;
}
return returnState;
}
void encoder_init(void)
{
//Нам необходимо модифицировать только эти биты порта
ENCODER_REG &= ~((ENCODER_LEFT)|(ENCODER_RIGHT));
//Порт энкодера - входы с подтяжкой (т.к. соответствующие регистры порта установлены в 0)
ENCODER_PORT |= ((ENCODER_LEFT)|(ENCODER_RIGHT));
}
encoder.h
CODE
#ifndef ENCODER_H
#define ENCODER_H
//Конфигурация порта энкодера
#define ENCODER_PORT PORTC
#define ENCODER_REG DDRC
#define ENCODER_PIN PINC
//Куда подключен левый вывод, куда - правый
#define ENCODER_LEFT (1<<4)
#define ENCODER_RIGHT (1<<3)
//Состояние покоя энкодера
#define ENCODER_NONE 0
//Считываем состояние энкодера
#define READ_ENCODER_STATE() ((~ENCODER_PIN) & ((ENCODER_LEFT)|(ENCODER_RIGHT)))
//Переменная для обработки состояний порта в прерывании
uint8_t ENCODER_TURN;
extern uint8_t ENCODER_TURNED;
extern volatile int8_t ENCODER_STATE;
extern volatile uint8_t isEncoderTurned;
extern volatile uint8_t ENCODER_DEBOUNCE_COUNTER;
void ENCODER_TASK(void);
int8_t encoder_state(void);
void encoder_init(void);
#endif
Ну и в прерывании кусок:
CODE
ENCODER_DEBOUNCE_COUNTER++;
if (!ENCODER_TURNED)
{
ENCODER_PORT &= ~(0x04<<ENCODER_TURN); //Выставляем 0 на вывод порта энкодера по очереди
if (++ENCODER_TURN > 1) //Переходим на следующий вывод порта. Если достигли конца,
ENCODER_TURN = 0; //начинаем считать с начала
ENCODER_PORT |= (0x04<<ENCODER_TURN); //Возвращаем 1 на соответствующий вывод порта энкодера
asm("nop");
ENCODER_STATE = READ_ENCODER_STATE(); //Считываем состояние порта энкодера
if (ENCODER_NONE != READ_ENCODER_STATE()) //Проверяем, действительно ли мы повернули энкодер
ENCODER_TURNED = 1; //Таки повернули
} else //И мы всё ещё поворачиваем его
ENCODER_TASK();