Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

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

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

Прежде всего энкодеры бывают нескольких типов, рассматриваемый в данной статье —  механический инкрементальный.  В качестве испытуемого, был использован pec12-4220f-s0024. Внешне он похож на переменный резистор, но, в отличие от резистора, он не имеет ограничителей, т.е. может крутиться бесконечно в любую сторону.

15-1

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

15-6

Теперь рассмотрим все более детально. Электрически он представляет собой 2 кнопки без фиксации, когда мы начинаем крутить они по очереди срабатывают — сначала одна, затем вторая. В зависимости от того, в какую сторону мы вращаем, одна из кнопок срабатывает раньше или позднее. Для того чтобы узнать, в каком состоянии находятся эти кнопки, ножки порта (к которому подсоединен энкодер) должны быть подтянуты к «+» питания.

15-4

На разобранном энкодере 1/3 площадки относится к 1 контакту, 1/3 к 2 контакту, сплошной участок — общий. Когда скользящие контакты попадают на изолированные участки (черные), слышны щелчки. В этот момент энкодер, находится в устойчивом состоянии, когда обе кнопки разомкнуты. На ножках порта будут лог единицы(состояние 11).

15-5

Как только мы начинаем вращать в какую либо сторону, один из контактов замыкается на землю. На этой ножеке появится лог 0, на второй ножке по прежнему будет лог1 (состояние 01). Если мы продолжаем вращать, на второй ножке появится лог0(состояние 00). Далее, на первой ножке пропадает контакт (состояние 10), в конце концов энкодер возвращается в устойчивое состояние (11). Т.е. на один щелчок приходится 4 изменения состояния. Временная диаграмма выглядит так:

15-7

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

Если выписать эти состояния в двоичной системе и перевести их в десятичную, то получится следующий порядок(для вращения в одну сторону):
11=3
01=1
00=0
10=2

При вращении в противоположную сторону:
11=3
10=2
00=0
01=1

Теперь осталось понять, как эти значение обрабатывать. Допустим, энкодер подключен к ножкам порта В0 и  В1. Нам нужно прочитать эти ножки. Есть довольно хитрый способ, но для начала нам нужно понять операцию «логического и» (&).

Результат будет равен единице, только если оба числа равны 1, т.е. результат операции 1&1, будет равен 1. Следовательно 1&0=0, 0&0=0, 0&1=0.

Логическое & поможет нам вычленить из целого порта, только интересующие нас ножки. Т.е. операция x=0b00000011 & PINB; позволит нам прочитать в переменную «х» состояние первых двух ножек, независимо от того, что находится на остальных ножках. Число 0b00000011 можно перевести в шестнадцатеричную систему 0х3.

Теперь все необходимые знания для написания прошивки у нас есть. Задача: увеличивать/уменьшать переменную Vol, при помощи энкодера, результат вывести на lcd дисплей.

#include <mega8.h>
int NewState,OldState,Vol,upState,downState;
#asm
   .equ __lcd_port=0x12 ;PORTD
#endasm
#include <lcd.h>
#include <stdio.h>
 
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{ 
NewState=PINB & 0b00000011;
if(NewState!=OldState)
{
switch(OldState)
	{
	case 2:
		{
		if(NewState == 3) upState++;
		if(NewState == 0) downState++; 
		break;
		}
 
	case 0:
		{
		if(NewState == 2) upState++;
		if(NewState == 1) downState++; 
		break;
		}
	case 1:
		{
		if(NewState == 0) upState++;
		if(NewState == 3) downState++; 
		break;
		}
	case 3:
		{
		if(NewState == 1) upState++;
		if(NewState == 2) downState++; 
		break;
		}
	}            
OldState=NewState;
}
TCNT1H=0x00;
TCNT1L=0x00;
}
 
void main(void)
{  
char lcd_buf[17];
 
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=P State0=P 
PORTB=0x03;
DDRB=0x00;
 
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x03;
OCR1AL=0xE8;
OCR1BH=0x00;
OCR1BL=0x00;
 
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;
 
// Global enable interrupts
#asm("sei")
lcd_init(8);
 
while (1)
      { 
      if (upState >= 4) 
      {                            
        Vol++;
        upState = 0;
      }
      if (downState >= 4) 
      {                              
        Vol--;
        downState = 0;
      }
      sprintf(lcd_buf,"vol=%d",Vol);
      lcd_gotoxy(0,0);
      lcd_clear();
      lcd_puts(lcd_buf);
 
      };
}

В качестве пояснений: таймер 1 настроен на срабатывание 1000 раз в секунду, строкой NewState=PINB & 0b00000011; считываем состояние ножек 0 и 1 портаВ. if(NewState!=OldState) если состояние не изменилось, значит вращения нет.
Если состояние изменилось конструкция switch определяет в какую сторону было произведено вращение, в зависимости от этого увеличивается значение переменной downState(влево) или upState(вправо).

От щелчка до следующего щелчка 4 изменения состояния, поэтому 1 раз за 4 импульса изменяем переменную Vol. Ее же и выводим на дисплей. Прошивка доступна здесь

17 комментариев: Урок 15.1 Управление инкрементальным энкодером при помощи AVR.

  • если использовать инструкцию А=PIND обязательно все выводы объявлять как ВВОДы
    или же в остальные биты запишутся нули

  • не обязательно

  • у меня энкодер работает без прерываний. На один вход Атмеги через электролит подается с одного контакта, (и на этот же контакт энкодера +5в через 4,7к)-и после элека на Атмегу тоже 4,7к-смысл в том что в каком положении энкодер не остановишь на Атмеге все равно поднимется 1, а второй контакт энкодера на вторую ногу Атмеги и 4,7к на +5в. в программе пишем если(на 1 ноге Атмеги 0 ) то проверить что на второй
    если 1- то вращение в перед, а если 0 то вращение на зад. преимущества-не задействованы прерывания, Недостаток- нельзя крутить очень быстро.

  • А с какого перелягу незадейсвованные прерывания это преимущество? Или загрузить контроллер по самые помидоры это норма? Как раз прерывания это и есть преимущество. Так как контроллер может выполнять параллельно задачу, пока не отработало прерывание.

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

  • Там 1 000 раз в секунду или 1 000 000 раз в секунду? Если я правильно понимаю, там 1000 килоГерц, что равно 1 мегаГерцу. И делитель вроде как 8 стоит же…

  • Извиняюсь, понял: проморгал что Compare A = 03E8 = 1000

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

  • Протестировал на Atmega16. Все получилось с первого раза.Спасибо.

  • Уважаемы admin подскажите у меня у энкодера вот такой алгоритм:
    в одну сторону: 11 00 01 11 00 01 11
    в другую сторону:11 01 00 11 01 00 11
    Не могу нормально настроить, получается только в одну сторону. Может энкодер поломан или это такая разновидность?

  • Все разобрался, вскрыть пришлось, у меня общий не по центру был, а крайний!

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

  • затирать только нужные символы, или реже очищать

  • Здравствуй, все сделал работает, решил перевесить на порт Д6 и Д7 выводы, изменил DDRD на входы, PORTD притянул и изменил тут NewState=PIND & 0b11000000; и не работает не могу понять почему

  • чудес не бывает :) проверяйте что не так

  • interrupt [TIM2_COMP] void timer2_comp_isr(void)
    {
    NewState=PINC & 0b00110000;

    if(NewState!=OldState)
    {
    switch(OldState)
    {
    case 2:
    {
    if(NewState == 3) upState++;
    if(NewState == 0) downState++;
    break;
    }

    case 0:
    {
    if(NewState == 2) upState++;
    if(NewState == 1) downState++;
    break;
    }
    case 1:
    {
    if(NewState == 0) upState++;
    if(NewState == 3) downState++;
    break;
    }
    case 3:
    {
    if(NewState == 1) upState++;
    if(NewState == 2) downState++;
    break;
    }
    }
    OldState=NewState;
    }

    TCNT2=0x00;

    }

    ASSR=0<<AS2;
    TCCR2=(0<<PWM2) | (0<<COM21) | (0<<COM20) | (0<<CTC2) | (0<<CS22) | (0<<CS21) | (1<<CS20);
    TCNT2=0x00;
    OCR2=0xFF;
    TIMSK=(1<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<TOIE0);

    помоги разобраться? не хочет работать на этом порту. где ошибка может

  • Результат надо сдвигать, т.е. после этого NewState=PINC & 0b00110000; NewState нужно сдвинуть вправо, чтобы получились 0, 1, 2, 3

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Свежие записи
Последние комментарии
  • Загрузка...
Архивы
Счетчик
Яндекс.Метрика