Библиографическое описание:

Тихонов П. А. Измерение угла поворота диска с помощью энкодера и микроконтроллера Atmega16A // Молодой ученый. — 2016. — №5. — С. 82-86.

 

В данной статье будет рассмотрен метод измерения угла поворота диска с помощью энкодера и микроконтроллера Atmega16A. Вначале будут рассмотрены общие сведения об энкодерах, их классификация, далее будет рассмотрена непосредственно сама прошивка для микроконтроллера.

 

Энкодер/преобразователь угловых перемещений — устройство, предназначенное для преобразования угла поворота вращающегося объекта (вала) в электрические сигналы, позволяющие определить угол его поворота.

Существует несколько видов энкодеров (оптические, резистивные, магнитные, индуктивные, механические), работа каждого вида основана на своём принципе. Для решения данной задачи был выбран оптический энкодер.

Существует два типа оптических энкодеров, а именно: абсолютные и инкрементальные (относительные). Абсолютные энкодеры позволяют в любой момент времени знать текущий угол поворота оси, также после пропадания и восстановления питания. Инкрементальные определяют угол по количеству импульсов.

Простейшим типом инкрементного энкодера является одноканальный энкодер, обычно состоящий из механического прерывателя света, производящего определенное количество прямоугольных или синусоидальных импульсов, при каждом обороте вала. По количеству этих импульсов можно понять, на какой угол повернулся вал.

Одноканальный тахометр не позволяет определить направление вращения, поэтому используется только в качестве тахометра и не может быть использован в качестве датчика положения. Поэтому существуют двухканальные энкодеры, которые преодолели эти проблемы путём добавления второго канала, смещенного относительно первого таким образом, что два выходных канала сдвинуты по фазе на . Это позволяет определить, какой канал опережает другой и, следовательно, установить направление вращения. При этом увеличивается разрешение из-за увеличения количества различных состояний с двух (в одноканальном) до четырёх без изменения конструкции.

Для вывода информации об угле, в нашем расположении было четыре 7ми-сегментных дисплея.

Их принцип работы довольно прост. Существуют обозначения для каждого сегмента дисплея: последовательно буквы «A» — «G» и обозначение «DP» для точки. При подаче напряжения на сам ножку питания дисплея, загораются все его сегменты, поэтому, чтобы погасить неиспользуемые, на контакты, соответствующие своему сегменту, необходимо подать напряжение.

Зная это, можно перейти к программе для микроконтроллера.

«A» канал подключён к ножке Int0 микроконтроллера — контакту, отвечающему за внешние прерывания. Прерывание происходит на падающем фронте.

«B» канал подключён к одной из ножек, настроенной как вход.

Во время прерывания на Int0, проверяется логическое значение сигнала на канале «B». От этого значения зависит направление поворота (т. к. отставание у канала «B» на ).

В главном цикле программы происходит выполнение функции показа заданного числа: для каждого дисплея определяется цифра, которая должна быть выведена на него, далее, уже для каждого дисплея, в свою очередь, вызывается ещё одна функция, которая отвечает за само отображение цифры на дисплее.

По Int1 происходит прерывание по изменению сигнала. Оно отвечает за показ на дисплее угла/количества тиков.

Формула для преобразования количества тиков в угол следующая:

где  — текущее количество тиков,  — количество тиков на оборот

 

Код программы на C:

#include <avr/io.h>

#include <avr/sfr_defs.h>

#include <util/delay.h>

#include <avr/interrupt.h>

#include <stdlib.h>

 

int CURR_TICKS = 0;

bool B_LOGICAL_LEVEL = false;

 

// массив сегментов для каждой цифры

bool number_1[8] = {0, 1, 1, 0, 0, 0, 0, 0};

bool number_2[8] = {1, 1, 0, 1, 1, 0, 1, 0};

bool number_3[8] = {1, 1, 1, 1, 0, 0, 1, 0};

bool number_4[8] = {0, 1, 1, 0, 0, 1, 1, 0};

bool number_5[8] = {1, 0, 1, 1, 0, 1, 1, 0};

bool number_6[8] = {1, 0, 1, 1, 1, 1, 1, 0};

bool number_7[8] = {1, 1, 1, 0, 0, 0, 0, 0};

bool number_8[8] = {1, 1, 1, 1, 1, 1, 1, 0};

bool number_9[8] = {1, 1, 1, 1, 0, 1, 1, 0};

bool number_0[8] = {1, 1, 1, 1, 1, 1, 0, 0};

 

int pins_of_segments[8] = {1, 0, 5, 6, 7, 2, 3, 4};

 

// зажигает сегменты, согласно заданному массиву

void makeDisplay(int num, bool symbol[8]) {

    if (num != 1)

        PORTD &= ~_BV(PD7);

    if (num != 2)

        PORTD &= ~_BV(PD6);

    if (num != 3)

        PORTD &= ~_BV(PD5);

    if (num != 4)

        PORTD &= ~_BV(PD4);

 

    for (int i = 0; i < 8; i++) {

        if (!symbol[i])

            PORTB |= _BV(pins_of_segments[i]);

        else

            PORTB &= ~_BV(pins_of_segments[i]);

    }

 

    switch (num) {

        case 1:

            PORTD |= _BV(PD7);

            break;

        case 2:

            PORTD |= _BV(PD6);

            break;

        case 3:

            PORTD |= _BV(PD5);

            break;

        case 4:

            PORTD |= _BV(PD4);

            break;

    }

}

 

// выводит цифру на заданный дисплей

voidmakeDigit(inti,intdigit){

switch(digit){

case0:

makeDisplay(i,number_0);

break;

case1:

makeDisplay(i,number_1);

break;

case2:

makeDisplay(i,number_2);

break;

case3:

makeDisplay(i,number_3);

break;

case4:

makeDisplay(i,number_4);

break;

case5:

makeDisplay(i,number_5);

break;

case6:

makeDisplay(i,number_6);

break;

case7:

makeDisplay(i,number_7);

break;

case8:

makeDisplay(i,number_8);

break;

case9:

makeDisplay(i,number_9);

break;

}

}

 

// выводит число на дисплеи

voidmakeNumber(intnumber){

 

// разделение числа на разряды

for(inti=3;i>=0;i--){

makeDigit(i,number%10);

number/=10;

}

}

 

int main(void) {

    MCUCR |= (1 << ISC01); //настраиваем на срабатывание Int0 по падающем фронте

    MCUCR |= (1 << ISC10); // срабатывание Int1 по изменению сигнала

    GICR |= (1 << INT0); // разрешение внешнего прерывания Int0

    GICR |= (1 << INT1); // разрешение внешнего прерывания Int1

    DDRD &= ~_BV(PD2); // включение как вход Int0

    DDRD &= ~_BV(PD3); // включение как вход Int1

    sei(); // общеее разрешение прерываний

 

    while (1) {

        if ((1 << PD0) & PIND) // режим показа количества тиков

            makeNumber(CURR_TICKS);

        else // режим показа угла поворота в градусах

            makeNumber((int)(360 * (((float)(CURR_TICKS % 1000)) / 1000.0)));

    }

}

 

// обработчик внешнего прерывания INT0

ISR(INT0_vect) { //счётчик тактов

    if (!B_LOGICAL_LEVEL) {

        if (CURR_TICKS++ > 9999)

            CURR_TICKS = 0;

    }

    else {

        if (CURR_TICKS-- < 0)

            CURR_TICKS = 9999;

    }

}

 

// переключение режимов показа угол/количество тактов

ISR(INT1_vect) {

    B_LOGICAL_LEVEL = ((1 << PD3) & PIND);

}

 

В результате, получилась прошивка для микроконтроллера Atmega16A, позволяющая узнать количество тиков на оборот диска энкодера, а также угол поворота в количестве тиков и градусов и вывести эту информацию на семисегментные дисплеи.

 

Литература:

 

  1.                Atmel-8154C-8-bit-AVR-ATmega16A_Datasheet-07/2014 [Электронный ресурс] / Atmel Corporation — Электрон. дан. — Режим доступа: http://www.atmel.com/images/atmel-8154–8-bit-avr-atmega16a_datasheet.pdf, свободный
  2.                Изучаем AVR. Прерывания [Электронный ресурс] — Электрон. дан. — Режим доступа: http://makesystem.net/?p=1365, свободный
  3.                Горобец А. В. Энкодеры — Датчики линейных и круговых перемещений [Текст] / А. В. Горобец // Упаковка, 2008–3 с.
  4.                Оптические энкодеры [Электронный ресурс] / А. Антонов — Электрон. дан. — Режим доступа: http://robotosha.ru/robotics/optical-encoders.html, свободный

Обсуждение

Социальные комментарии Cackle