Подключаем энкодер к плате Arduino Nano в среде программирования Arduino Ide.

В данном уроке мы с Вами подключим энкодер к плате 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. Обычный режим

  • 00 — вывод OC1A не функционирует.
  • 01 — изменение состояния вывода OC1A на противоположное при совпадении.
  • 10 — сброс вывода OC1A в 0 при совпадении.
  • 11 — установка вывода OC1A в 1 при совпадении.
  1. Режим ШИМ
  • 00 — вывод OC1A не функционирует.
  • 01 — если биты WGM13 — WGM10 установлены в (0000 — 1101), вывод OC1A не функционирует.
  • 01 — если битыWGM13 — WGM10 установлены в 1110 или 1111, изменение состояния вывода OC0A на противоположное при совпадении.
  • 10 — сброс вывода OC1A в 0 при совпадении, установка  вывода OC1A в 1 если регистр TCNT1 принимает значение 0x00.
  • 11 — установка вывода OC1A в 1 при совпадении, установка  вывода OC1A в 0 если регистр TCNT1 принимает значение 0x00.
  1. Режим коррекции фазы ШИМ
  • 00 — вывод OC1A не функционирует.
  • 01 — если биты WGM13 — WGM10 установлены в (0000 — 1100, 1010, 1100 — 1111), вывод OC1A не функционирует.
  • 01 — если битыWGM13 — WGM10 установлены в 1101 или 1011, изменение состояния вывода OC1A на противоположное при совпадении.
  • 10 — сброс вывода OC1A в 0 при совпадении во время увеличения значения счетчика, установка  вывода OC1A в 1  при совпадении во время уменьшения значения счетчика.
  • 11 — установка вывода OC1A в 1 при совпадении во время увеличения значения счетчика, сброс  вывода OC1A в 0  при совпадении во время уменьшения значения счетчика.

Биты COM1B1 и COM1B0 влияют на то, какой сигнал появится на выводе OC1B (16 ножка) при совпадении счетного регистра TCNT1 со значением регистра сравнения OCR1B:

При этом необходимо учитывать режим работы таймера:

  1. Обычный режим
  • 00 — вывод OC1B не функционирует.
  • 01 — изменение состояния вывода OC1B на противоположное при совпадении.
  • 10 — сброс вывода OC1B в 0 при совпадении.
  • 11 — установка вывода OC1B в 1 при совпадении.
  1. Режим ШИМ
  • 00 — вывод OC1B не функционирует.
  • 01 — вывод OC1B не функционирует.
  • 10 — сброс вывода OC1B в 0 при совпадении, установка  вывода OC1B в 1 если регистр TCNT1 принимает значение 0x00.
  • 11 — установка вывода OC1B в 1 при совпадении, установка  вывода OC1B в 0 если регистр TCNT1 принимает значение 0x00.
  1. Режим коррекции фазы ШИМ
  • 00 — вывод OC1B не функционирует.
  • 01 — вывод OC1B не функционирует.
  • 10 — сброс вывода OC1B в 0 при совпадении во время увеличения значения счетчика, установка  вывода OC1B в 1  при совпадении во время уменьшения значения счетчика.
  • 11 — установка вывода OC1B в 1 при совпадении во время увеличения значения счетчика, сброс  вывода OC1B в 0  при совпадении во время уменьшения значения счетчика.

Так как мы не будем использовать не какие режимы в регистр 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)):

  • 0000 — обычный режим.
  • 0001 — коррекция фазы PWM, 8-бит.
  • 0010 — коррекция фазы PWM, 9-бит.
  • 0011 — коррекция фазы PWM, 10-бит.
  • 0100 — режим счета импульсов (OCR1A) (сброс при совпадении).
  • 0101 — PWM, 8-бит.
  • 0110 — PWM, 9-бит.
  • 0111 — PWM, 10-бит.
  • 1000 — коррекция фазы и частоты PWM (ICR1).
  • 1001 — коррекция фазы и частоты PWM (OCR1A).
  • 1010 — коррекция фазы PWM (ICR1).
  • 1011 — коррекция фазы и частоты PWM (OCR1A).
  • 1100 — режим счета импульсов (ICR1) (сброс при совпадении).
  • 1101 — резерв.
  • 1110 — PWM (ICR1).
  • 1111 — PWM (OCR1A).

Бит ICNC1 регистра TCCR1B управляет схемой подавления помех блока захвата (0 — выключена / 1 — включена).
Бит ICES1 регистра TCCR1B выбирает активный фронт регистра захвата (0 — по спадающему фронту сигнала  и 1 — по нарастающему фронту сигнала).

Биты CS12 , CS11 , CS10 регистра TCCR1B устанавливают режим тактирования и предделителя тактовой частоты таймера/счетчика T1:

  • 000 — таймер/счетчик T1 остановлен.
  • 001 — тактовый генератор CLK.
  • 010 — CLK/8.
  • 011 — CLK/64.
  • 100 — CLK/256.
  • 101 — CLK/1024.
  • 110 — внешний источник на выводе T1 (11 ножка) по спаду сигнала.
  • 111 — внешний источник на выводе T1 (11 ножка) по возрастанию сигнала.

Таким образом мы записываем в регистр 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; // сбрасываем флаг
  }
}