В данном уроке мы с Вами подключим энкодер к плате Arduino Nano в среде программирования Arduino IDE. При этом мы с Вами рассмотрим два варианта написания программы: без прерывания и с прерыванием.
Для начало рассмотрим алгоритм работы энкодера. Сам энкодер имеет три выхода:
При вращении энкодера у нас у нас по очереди на ножках появляются логические нули и единицы, при этом по их последовательности мы можем понять в какую сторону вращается энкодер. Если изменений состояний ножек не происходит, — энкодер стоит на месте.
Для начала напишем программный код, который будет в цикле проверять состояние наших ножек и тем самым определять в каком направлении вращается энкодер:
#define C1 2 // первый контакт #define C2 8 // второй контакт int stepEnc = 0; // состояние энкодера byte buffEnc[2]; // буффер состояния ножек byte flagEnc = 0; // флаг изменения void setup() { pinMode(C1, INPUT_PULLUP); pinMode(C2, INPUT_PULLUP); Serial.begin(9600); } void loop() { encoder(); // отслеживаем if (flagEnc) { // если изменение было Serial.println(stepEnc); // выводим состояние в порт flagEnc = 0; // сбрасываем флаг } } void encoder() { static unsigned long timer; if (timer + 5 > millis()) return; // замеряем каждые 5 мс buffEnc[0] = buffEnc[1]; buffEnc[1] = digitalRead(C1); if (buffEnc[0] != buffEnc[1]) { // заметили изменение сигнала на первой ножке stepEnc += digitalRead(C2) == buffEnc[1] ? 1 : -1; // если вторая еще в плюсе, прибавляем, если уже в минусе, убавляем flagEnc = 1; // поднимаем флаг } timer = millis(); }
Рассмотрим программный код с использованием прерывания:
Для начала нам нужно настроить таймер, для этого мы вначале запрещаем все прерывания командой cli().
Далее нам надо настроить регистр TCCR1A, где:
бит 7 | бит 6 | бит 5 | бит 4 | бит 3 | бит 2 | бит 1 | бит 0 |
COM1A1 | COM1A0 | COM1B1 | COM1B0 | — | — | WGM11 | WGM10 |
Биты COM1A1 и COM1A0 влияют на то, какой сигнал появится на выводе OC1A (15 ножка) при совпадении счетного регистра TCNT1 со значением регистра сравнения OCR1A:
При этом необходимо учитывать режим работы таймера:
1. Обычный режим
Биты COM1B1 и COM1B0 влияют на то, какой сигнал появится на выводе OC1B (16 ножка) при совпадении счетного регистра TCNT1 со значением регистра сравнения OCR1B:
При этом необходимо учитывать режим работы таймера:
Так как мы не будем использовать не какие режимы в регистр TCCR1A записываем 0b00000000.
Следующий регистр TCCR1B:
Регистр TCCR1B:
бит 7 | бит 6 | бит 5 | бит 4 | бит 3 | бит 2 | бит 1 | бит 0 |
ICNC1 | ICES1 | — | WGM13 | WGM12 | CS12 | CS11 | CS10 |
Биты WGM13 и WGM12 регистра TCCR1B и биты WGM11 и WGM10регистра TCCR1A устанавливают режим работы таймера/счетчика T1 (WGM13 (4) , WGM12 (3), WGM11 (1) , WGM10 (0)):
Бит ICNC1 регистра TCCR1B управляет схемой подавления помех блока захвата (0 — выключена / 1 — включена).
Бит ICES1 регистра TCCR1B выбирает активный фронт регистра захвата (0 — по спадающему фронту сигнала и 1 — по нарастающему фронту сигнала).
Биты CS12 , CS11 , CS10 регистра TCCR1B устанавливают режим тактирования и предделителя тактовой частоты таймера/счетчика T1:
Таким образом мы записываем в регистр TCCR1B = 0b00001100, что означает: режим счета импульсов и коэффициент делителя 256.
Далее выставляем число с которого наш таймер начнет считать, в данном случаи для проверки состояния изменения состояния мы выберем 5ms:
OCR1A =частота процессора*время/(2*коэффициент делителя) — 1
OCR1A=16000000*0,005/(2*256)-1=155,25 округляем до 155.
И последний регистр который нам нужен TIMSK1:
бит 7 | бит 6 | бит 5 | бит 4 | бит 3 | бит 2 | бит 1 | бит 0 |
— | — | ICIE1 | — | — | OCIE1B | OCIE1A | TOIE1 |
Бит ICIE1 разрешает прерывание по захвату, биты OCIE1B и OCIE1A разрешают прерывания при совпадении, бит TOIE1 разрешает прерывание по переполнению при установке 1. Так как мы используем режим счета А:
TIMSK1 = 0b0000010;
#define A 2 // первый контакт
#define B 8 // второй контакт
int stepEnc = 0; // состояние энкодера
byte buffEnc[2]; // буффер состояния ножек
byte flagEnc = 0; // флаг изменения
unsigned long timerPrint;
ISR(TIMER1_COMPA_vect) {
buffEnc[0] = buffEnc[1];
buffEnc[1] = digitalRead(A);
if (buffEnc[0] != buffEnc[1]) { // заметили изменение сигнала на первой ножке
stepEnc += digitalRead(B) == buffEnc[1] ? 1 : -1; // если вторая еще в плюсе, прибавляем, если уже в минусе, убавляем
flagEnc = 1; // поднимаем флаг
}
}
void setup() {
pinMode(A, INPUT_PULLUP);
pinMode(B, INPUT_PULLUP);
cli();
TCCR1A = 0b00000000;
TCCR1B = 0b00001100; // x256, сброс при совпадении 1A
OCR1A = 155; // 5ms
TIMSK1 = 0b0000010; // прерывание А
sei();
Serial.begin(9600);
}
void loop() {
if (flagEnc) { // если изменение было
Serial.println(stepEnc); // выводим состояние в порт
flagEnc = 0; // сбрасываем флаг
}
}