Администрация форума не несёт ответственности за достоверность информации и оставляет за собой право редактировать или в особых случаях даже удалять посты без предупреждения. Спасибо за понимание.

Программирование ATMEL в BASCOM.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.



Исследование энкодера

Сообщений 1 страница 6 из 6

1

Основу брал здесь

Огромное СПАСИБО  dmm!

0

2

Понадобилось 2 энкодера с кнопками на валу, может кому пригодится.

Обработка по прерываниям PCINT0 и PCINT2, кнопки переключают скорость регулировки х1, х10


Симуляция + исходник + фото + видео  -  скачать

В симуляции второй вывод кнопки энкодера висит в воздухе потому, что кнопка подключена в схему обработки дребезга (кнопки работают синхронно!)


Исходник

$map       ' Получаем карту распределения памяти компилятором Bascom
  $regfile = "m328pdef.dat"       ' http://www.atmel.com/images/Atmel-8272- … asheet.pdf
  $crystal = 8000000       ' 1 МГц
  $framesize = 64       ' Размер области используемой и необходимой для преобразований
  $hwstack = 64       ' Размер аппаратного стека
  $swstack = 64       ' Размер программного стека

  $baud = 38400
  'Ucsr0a.1 = 1       ' Baud = Baud * 2

'===============================================================================
'          Настройка МК для работы с LCD на базе контроллера hd44780
'   Обратите внимание, библиотеки не используются, выводы могут быть любые
'===============================================================================
  Config Lcdpin = Pin , Rs = Portc.0 , E = Portc.1 , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5
  Config Lcd = 20 * 4       ' Используемый LCD
  Initlcd       ' Инициализируем LCD
  Waitms 50       ' Ждем пока все запустится
  Cursor Off Noblink       ' Выключаем неморгающий курсор
'  Cursor On Noblink         ' Включаем неморгающий курсор
'  Cursor Blink       ' Включаем моргающий курсор
'  Upperline  1 строка, Lowerline  2 строка, Thirdline  3 строка, Fourthline 4 строка  - один из вариантов перехода на строку
  Cls       ' Чистим экран, на всякий случай

'===============================================================================
'                  Конфигурация МК для работы с энкодерами
'===============================================================================

'-----------------------------------------
'Конфигурация МК для работы с энкодером 1
'-----------------------------------------
  Encoder_1_port_1 Alias Pinb
  Encoder_1_pin_1 Alias 0

  Encoder_1_port_2 Alias Pinb
  Encoder_1_pin_2 Alias 1

  Config Pinb.2 = Input : Set Portb.2
  Key_encoder_1_port Alias Pinb
  Key_encoder_1_pin Alias 2

  Dim Кольцевое_нажатие_1 As Byte

  On Pcint0 Обработка_прерывания_pcint0 Nosave       ' Подпрограмма обработки прерывания PCINT0
  Pcmsk0 = &B_0000_0101       'PCINT0 (PINB.0); PCINT2 (PINB.2)

  Dim Счетчик_энкодера_1_влево As Byte
  Dim Счетчик_энкодера_1_вправо As Byte
  Dim Флаг_обновить_lcd_1 As Byte
  Dim Флаг_данные_энкодера_1 As Byte
  Dim Направление_вращения_энкодера_1 As Byte
  Dim Переменная_integer_1 As Integer
  Dim Переменная_str_1 As String * 10

'-----------------------------------------
'Конфигурация МК для работы с энкодером 2
'-----------------------------------------
  Encoder_2_port_1 Alias Pind
  Encoder_2_pin_1 Alias 5

  Encoder_2_port_2 Alias Pind
  Encoder_2_pin_2 Alias 6

  Config Pind.7 = Input : Set Portd.7
  Key_encoder_2_port Alias Pind
  Key_encoder_2_pin Alias 7

  Dim Кольцевое_нажатие_2 As Byte

  On Pcint2 Обработка_прерывания_pcint2 Nosave       ' Подпрограмма обработки прерывания PCINT2
  Pcmsk2 = &B_1010_0000       'PCINT21 (PIND.5); PCINT23 (PIND.7)

  Dim Флаг_обновить_lcd_2 As Byte
  Dim Флаг_данные_энкодера_2 As Byte
  Dim Счетчик_энкодера_2_влево As Byte
  Dim Счетчик_энкодера_2_вправо As Byte
  Dim Направление_вращения_энкодера_2 As Byte
  Dim Переменная_integer_2 As Integer
  Dim Переменная_str_2 As String * 10

  Locate 1 , 1
  Lcd "Encoder 1"
  Locate 2 , 1
  Lcd "     0"
  Locate 3 , 1
  Lcd "Encoder 2"
  Locate 4 , 1
  Lcd "     0"

'--------------  Разрешаем работу необходимых прерываний  ----------------------
  Enable Pcint0
  Enable Pcint2
'------------  Разрешаем работу всех включенных прерываний  --------------------
  Enable Interrupts
'===============================================================================
  Главный_цикл:
'===============================================================================

      If Флаг_данные_энкодера_1 <> 0 Then

          Gosub Обработка_данных_энкодера

          Флаг_данные_энкодера_1 = 0
          Goto Главный_цикл
      End If

      If Флаг_данные_энкодера_2 <> 0 Then

          Gosub Обработка_данных_энкодера

          Флаг_данные_энкодера_2 = 0
          Goto Главный_цикл
      End If

      If Флаг_обновить_lcd_1 <> 0 Then

          Переменная_str_1 = Str(переменная_integer_1)
          Переменная_str_1 = Format(переменная_str_1 , "+   0")

          If Переменная_integer_1 = 0 Then
              Mid(переменная_str_1 , 5 , 1) = " "
          End If

          Locate 2 , 1
          Lcd Переменная_str_1

          Флаг_обновить_lcd_1 = 0
          Goto Главный_цикл
      End If

      If Флаг_обновить_lcd_2 <> 0 Then

          Переменная_str_2 = Str(переменная_integer_2)
          Переменная_str_2 = Format(переменная_str_2 , "+   0")

          If Переменная_integer_2 = 0 Then
              Mid(переменная_str_2 , 5 , 1) = " "
          End If

          Locate 4 , 1
          Lcd Переменная_str_2

          Флаг_обновить_lcd_2 = 0
          Goto Главный_цикл
      End If



   Idle
'===============================================================================
  Goto Главный_цикл
End
'===============================================================================

'===============================================================================
  Обработка_данных_энкодера:
'-------------------------------------------------------------------------------
    If Счетчик_энкодера_1_вправо <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_вправо = Счетчик_энкодера_1_вправо * 10
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        End If
        Счетчик_энкодера_1_вправо = 0
        Флаг_обновить_lcd_1 = 1
    End If

    If Счетчик_энкодера_1_влево <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_влево = Счетчик_энкодера_1_влево * 10
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        End If
        Счетчик_энкодера_1_влево = 0
        Флаг_обновить_lcd_1 = 1
    End If

'----------------------------------

    If Счетчик_энкодера_2_вправо <> 0 Then
        If Кольцевое_нажатие_2 = 0 Then
            Переменная_integer_2 = Переменная_integer_2 + Счетчик_энкодера_2_вправо
        Elseif Кольцевое_нажатие_2 = 1 Then
            Счетчик_энкодера_2_вправо = Счетчик_энкодера_2_вправо * 10
            Переменная_integer_2 = Переменная_integer_2 + Счетчик_энкодера_2_вправо
        End If
        Счетчик_энкодера_2_вправо = 0
        Флаг_обновить_lcd_2 = 1
    End If

    If Счетчик_энкодера_2_влево <> 0 Then
        If Кольцевое_нажатие_2 = 0 Then
            Переменная_integer_2 = Переменная_integer_2 - Счетчик_энкодера_2_влево
        Elseif Кольцевое_нажатие_2 = 1 Then
            Счетчик_энкодера_2_влево = Счетчик_энкодера_2_влево * 10
            Переменная_integer_2 = Переменная_integer_2 - Счетчик_энкодера_2_влево
        End If
        Счетчик_энкодера_2_влево = 0
        Флаг_обновить_lcd_2 = 1
    End If
'-------------------------------------------------------------------------------
  Return
'===============================================================================

'-------------------------------------------------------------------------------
'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0 - ОТ ДВУХФАЗНОГО ИМПУЛЬСНОГО ЕНКОДЕРА
'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ
' (ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ
'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ. П/П РАБОТАЕТ ТАКЖЕ ПРИ ИСПОЛЬЗОВАНИИ
'ПРЕРЫВАНИЯ ОТ ЛЮБОГО ИЗМЕНЕНИЯ УРОВНЯ. НА СПАД СРАБАТЫВАЕТ, ФРОНТ ИГНОРИРУЕТ
'-------------------------------------------------------------------------------
  Const Con_irt = 5       '30       'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ
  Const Conirt1 = Con_irt * 1.5       'ПОЛТОРЫ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ
  Const Conirt2 = Con_irt * 2       'ДВЕ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ

   Pcint0_0:
    !Inc R31       'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА
    !Cpi R31 , Conirt1       'это Con_irt*1.5
    !Brne pcint0_1       'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД
    !Rjmp Выход_pcint0

   'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ
   Обработка_прерывания_pcint0:
    !Push R31       'сохраним регистры
    !In R31 , Sreg
    !Push R31

    !Sbic Key_encoder_1_port, Key_encoder_1_pin       ' Пропустить если бит в порту очищен
    !rjmp Прерывание_энкодера_1

    !lds R31 , {Кольцевое_нажатие_1}
    !inc R31
    !andi R31 , &B_1111_1101
    !sts {Кольцевое_нажатие_1} , R31
    !Rjmp Выход_pcint0

   Прерывание_энкодера_1:

   'ИНТЕГРИРУЮЩИЙ ФИЛЬТР (ФНЧ) ВХОДНОГО УРОВНЯ
    !Ldi R31 , Con_irt       'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ В СЧЕТЧИК
   Pcint0_1:
    !Sbic Encoder_1_port_1 , Encoder_1_pin_1       'ПРОВЕРИМ УРОВЕНЬ НА ВЫВОДЕ
    !Rjmp pcint0_15       'ЕСЛИ НА НОЖКЕ "1" - ПЕРЕХОД
    !Dec R31       'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ
    !Brne pcint0_1       'МНОГОКРАТНОГО ПОДТВЕРЖДЕНИЯ НИЗКОГО УРОВНЯ
    !Rjmp pcint0_2

   'ЕСЛИ УРОВНЬ ВЫСОКИЙ
   Pcint0_15:
    !Inc R31       'УВЕЛИЧИВАЕМ СЧЕТЧИК
    !Cpi R31 , Conirt2       'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО
    !Brne Выход_pcint0       'НА ВЫХОД
    !Rjmp pcint0_1
'---------------------------------------------
'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ
'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
'---------------------------------------------
'      |<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
'chA---     ---------     ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ
'      ¦   ¦  chA    ¦   ¦      ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА
'       ---           ---      ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ
'chB-----  |  ---------     ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ
'      | ¦ | ¦  chB    ¦   ¦    (ЩЕЛЧКА) В КАНАЛЕ "A"
'      |  ---           ---
'      |   |<------ ВРАЩЕНИЕ ВПРАВО
'---------------------------------------------
   Pcint0_2:
   'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ

    !Sbis Encoder_1_port_2 , Encoder_1_pin_2
    !Rjmp pcint0_3       'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ

   'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ)
    !Lds R31 , {Счетчик_энкодера_1_влево}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
    !Inc R31
    !Sts {Счетчик_энкодера_1_влево} , R31
    !Rjmp pcint0_4

   Pcint0_3:
   'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ)
    !Lds R31 , {Счетчик_энкодера_1_вправо}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
    !Inc R31
    !Sts {Счетчик_энкодера_1_вправо} , R31
   Pcint0_4:
    !Ldi R31 , 1
    !Sts {Флаг_данные_энкодера_1} , R31       'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА"

   Выход_pcint0:
    !Pop R31
    !Out Sreg , R31
    !Pop R31

    !Reti
'-------------------------------------------------------------------------------
  Return
'===============================================================================

'-------------------------------------------------------------------------------
'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0 - ОТ ДВУХФАЗНОГО ИМПУЛЬСНОГО ЕНКОДЕРА
'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ
' (ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ
'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ. П/П РАБОТАЕТ ТАКЖЕ ПРИ ИСПОЛЬЗОВАНИИ
'ПРЕРЫВАНИЯ ОТ ЛЮБОГО ИЗМЕНЕНИЯ УРОВНЯ. НА СПАД СРАБАТЫВАЕТ, ФРОНТ ИГНОРИРУЕТ
'-------------------------------------------------------------------------------
'  Const Con_irt_2 = 30       'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ
'  Const Conirt1_2 = Con_irt_2 * 1.5       'ПОЛТОРЫ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ
'  Const Conirt2_2 = Con_irt_2 * 2       'ДВЕ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ

   Pcint2_0:
    !Inc R31       'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА
    !Cpi R31 , Conirt1       'это Con_irt*1.5
    !Brne Pcint2_1       'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД
    !Rjmp Выход_pcint2

   'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ
   Обработка_прерывания_pcint2:
    !Push R31       'сохраним регистры
    !In R31 , Sreg
    !Push R31

    !Sbic Key_encoder_2_port, Key_encoder_2_pin       ' Пропустить если бит в порту очищен
    !rjmp Прерывание_энкодера_2

    !lds R31 , {Кольцевое_нажатие_2}
    !inc R31
    !andi R31 , &B_1111_1101
    !sts {Кольцевое_нажатие_2} , R31
    !Rjmp Выход_pcint2

   Прерывание_энкодера_2:

   'ИНТЕГРИРУЮЩИЙ ФИЛЬТР (ФНЧ) ВХОДНОГО УРОВНЯ
    !Ldi R31 , Con_irt       'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ В СЧЕТЧИК
   Pcint2_1:
    !Sbic Encoder_2_port_1 , Encoder_2_pin_1       'ПРОВЕРИМ УРОВЕНЬ НА ВЫВОДЕ
    !Rjmp Pcint2_15       'ЕСЛИ НА НОЖКЕ "1" - ПЕРЕХОД
    !Dec R31       'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ
    !Brne Pcint2_1       'МНОГОКРАТНОГО ПОДТВЕРЖДЕНИЯ НИЗКОГО УРОВНЯ
    !Rjmp Pcint2_2

   'ЕСЛИ УРОВНЬ ВЫСОКИЙ
   Pcint2_15:
    !Inc R31       'УВЕЛИЧИВАЕМ СЧЕТЧИК
    !Cpi R31 , Conirt2       'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО
    !Brne Выход_pcint2       'НА ВЫХОД
    !Rjmp Pcint2_1
'---------------------------------------------
'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ
'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
'---------------------------------------------
'      |<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
'chA---     ---------     ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ
'      ¦   ¦  chA    ¦   ¦      ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА
'       ---           ---      ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ
'chB-----  |  ---------     ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ
'      | ¦ | ¦  chB    ¦   ¦    (ЩЕЛЧКА) В КАНАЛЕ "A"
'      |  ---           ---
'      |   |<------ ВРАЩЕНИЕ ВПРАВО
'---------------------------------------------
   Pcint2_2:
   'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ

    !Sbis Encoder_2_port_2 , Encoder_2_pin_2
    !Rjmp Pcint2_3       'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ

   'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ)
    !Lds R31 , {Счетчик_энкодера_2_влево}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
    !Inc R31
    !Sts {Счетчик_энкодера_2_влево} , R31
    !Rjmp Pcint2_4

   Pcint2_3:
   'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ)
    !Lds R31 , {Счетчик_энкодера_2_вправо}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
    !Inc R31
    !Sts {Счетчик_энкодера_2_вправо} , R31
   Pcint2_4:
    !Ldi R31 , 1
    !Sts {Флаг_данные_энкодера_2} , R31       'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА"

   Выход_pcint2:
    !Pop R31
    !Out Sreg , R31
    !Pop R31

    !Reti
'-------------------------------------------------------------------------------
  Return
'===============================================================================

http://s6.uploads.ru/t/MoHQA.jpg http://s7.uploads.ru/t/MnZF9.jpg
http://s6.uploads.ru/t/roegG.jpg http://s7.uploads.ru/t/hrARk.jpg

+5

3


Энкодер + кнопка на валу (короткое + длинное нажатия), использовано прерывание PCINT0 + TIMER2.


Для чего нужен TIMER2, чтобы не было задержек в прерываниях

Короткие нажатия  -  меняется множитель incr и decr энкодера (х1; х10; х100)
Длинное нажатие  -  в примере реализована полуперезагрузка МК (Rjmp 0), в реальной конструкции, так делать, крайне нежелательно (неполная инициализация МК).


Прерывания на ASM

$map       ' Получаем карту распределения памяти компилятором Bascom
  $regfile = "m328pdef.dat"       ' http://www.atmel.com/images/Atmel-8272- … asheet.pdf
  $crystal = 8000000       ' 1 МГц
  $framesize = 64       ' Размер области используемой и необходимой для преобразований
  $hwstack = 64       ' Размер аппаратного стека
  $swstack = 64       ' Размер программного стека

'  $baud = 38400
  'Ucsr0a.1 = 1       ' Baud = Baud * 2

'===============================================================================
'          Настройка МК для работы с LCD на базе контроллера hd44780
'   Обратите внимание, библиотеки не используются, выводы могут быть любые
'===============================================================================
  Config Lcdpin = Pin , Rs = Portc.0 , E = Portc.1 , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5
  Config Lcd = 20 * 4       ' Используемый LCD
  Initlcd       ' Инициализируем LCD
  Waitms 50       ' Ждем пока все запустится
  Cursor Off Noblink       ' Выключаем неморгающий курсор
'  Cursor On Noblink         ' Включаем неморгающий курсор
'  Cursor Blink       ' Включаем моргающий курсор
'  Upperline  1 строка, Lowerline  2 строка, Thirdline  3 строка, Fourthline 4 строка  - один из вариантов перехода на строку
  Cls       ' Чистим экран, на всякий случай

'===============================================================================
'                  Конфигурация МК для работы с энкодерами
'===============================================================================
  Prescale_1024 Alias &B_0000_0111

  Config Timer2 = Timer , Prescale = 1024 , Clear Timer = 0       ' 16,384 ms при частоте МК 8МГц
  Stop Timer2
  Timer2 = 0
  On Timer2 Обработка_прерывания_timer2 Nosave

'-----------------------------------------
'Конфигурация МК для работы с энкодером 1
'-----------------------------------------
  Config Pinb.0 = Input : Set Portb.0
  Encoder_1_port_1 Alias Pinb
  Encoder_1_pin_1 Alias 0

  Config Pinb.1 = Input : Set Portb.1
  Encoder_1_port_2 Alias Pinb
  Encoder_1_pin_2 Alias 1

  Config Pinb.2 = Input : Set Portb.2
  Key_encoder_1_port Alias Pinb
  Key_encoder_1_pin Alias 2
  Set_isr_key_encoder Alias &B_0000_0100
  Reset_isr_key_encoder Alias &B_1111_1011

  On Pcint0 Обработка_прерывания_pcint0 Nosave       ' Подпрограмма обработки прерывания PCINT0
  Pcmsk0 = &B_0000_0101       'PCINT0 (PINB.0) - один из боковых выводов энкодера + вывод кнопки (PCINT2 (PINB.2))
  Enable_pcint0 Alias &B_0000_0001
  Disable_pcint0 Alias &B_1111_1110

  Dim Count As Byte       ' Временная переменная, в релизе удалить
  Dim Val_byte As Byte       ' Временная переменная, в релизе удалить
  Dim Long_press As Byte       ' Временная переменная, в релизе удалить
  Dim Short_press As Byte       ' Временная переменная, в релизе удалить
  Dim Флаг_обновить_lcd_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Кольцевое_нажатие_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Счетчик_длины_нажатий As Byte
  Dim Флаг_данные_энкодера_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Флаг_реакция_на_кнопки As Byte       ' Временная переменная, в релизе удалить
  Dim Переменная_integer_1 As Integer
  Dim Счетчик_энкодера_1_влево As Byte
  Dim Счетчик_энкодера_1_вправо As Byte
  Dim Длинное_нажатие_энкодер_1 As Byte
  Dim Уровень_кнопки_энкодера_1 As Byte : Уровень_кнопки_энкодера_1 = 255
  Dim Короткое_нажатие_энкодер_1 As Byte
  Dim Флаг_направл_кольц_нажатия As Byte

  Dim Short_press_str As String * 5
  Dim Переменная_str_1 As String * 10

'Начальное заполнение экрана
  Locate 1 , 1
  Lcd "Short_press  " ; "   x1"
  Locate 2 , 1
  Lcd "Encoder 1 " ; "       0"
  Locate 3 , 1
  Lcd "                    "

'--------------  Разрешаем работу необходимых прерываний  ----------------------
  Pcifr.pcif0 = 1 : Enable Pcint0       ' Сброс флаг прерывания и разрешение прерывания, иначе получается сразу +1 от энкодера
  Enable Timer2
'------------  Разрешаем работу всех включенных прерываний  --------------------
  Enable Interrupts
'===============================================================================
  Главный_цикл:
'===============================================================================

               If Флаг_данные_энкодера_1 <> 0 Then
                   Gosub Обработка_данных_энкодера
                   Флаг_данные_энкодера_1 = 0
                   Goto Главный_цикл
               End If

               If Флаг_обновить_lcd_1 <> 0 Then
                   Переменная_str_1 = Str(переменная_integer_1)
                   Переменная_str_1 = Format(переменная_str_1 , "+   0")
                   If Переменная_integer_1 = 0 Then
                       Mid(переменная_str_1 , 5 , 1) = " "
                   End If
                   Locate 2 , 13
                   Lcd Переменная_str_1
                   Флаг_обновить_lcd_1 = 0
                   Goto Главный_цикл
               End If

              If Флаг_реакция_на_кнопки <> 0 Then
                  If Короткое_нажатие_энкодер_1 <> 0 Then
                      Incr Short_press
                      Short_press_str = Str(short_press)
                      Short_press_str = Format(short_press_str , "  0")

                      If Флаг_направл_кольц_нажатия = 0 Then
                          Incr Кольцевое_нажатие_1
                          If Кольцевое_нажатие_1 > 1 Then
                              Флаг_направл_кольц_нажатия = 1
                          End If
                        Else
                          Decr Кольцевое_нажатие_1
                          If Кольцевое_нажатие_1 = 0 Then
                              Флаг_направл_кольц_нажатия = 0
                          End If
                      End If

                      If Кольцевое_нажатие_1 = 0 Then
                          Locate 1 , 15
                          Lcd "  x1"
                      Elseif Кольцевое_нажатие_1 = 1 Then
                          Locate 1 , 15
                          Lcd " x10"
                      Elseif Кольцевое_нажатие_1 = 2 Then
                          Locate 1 , 15
                          Lcd "x100"
                      End If
                      Короткое_нажатие_энкодер_1 = 0
                  End If

                  If Длинное_нажатие_энкодер_1 <> 0 Then
                      Incr Long_press
                      For Val_byte = 1 To 5
                          Locate 1 , 1
                          Lcd "     Save, or       "
                          Locate 2 , 1
                          Lcd "   Goto menu, or    "
                          Locate 3 , 1
                          Lcd "    Goto config     "
                          Locate 4 , 1
                          Lcd "                    "
                          Waitms 500
                          Locate 1 , 1
                          Lcd "                    "
                          Locate 2 , 1
                          Lcd "                    "
                          Locate 3 , 1
                          Lcd "                    "
                          Waitms 250
                      Next Val_byte
                      Locate 1 , 1
                      Lcd "                    "
                      Locate 2 , 1
                      Lcd "       Reboot       "
                      Locate 3 , 1
                      Lcd "                    "
                      Locate 4 , 1
                      Lcd "                    "
                      Waitms 1000
                      Длинное_нажатие_энкодер_1 = 0

                     'Reset Program
                      !jmp $00

                      Enable Pcint0       ' После реакции на длинное нажатие кнопки энкодера, обязательно включаем прерывания!!!

                  End If
                  Count = 0
                  Флаг_реакция_на_кнопки = 0
                  Goto Главный_цикл
              End If

'   Idle
'===============================================================================
  Goto Главный_цикл
End
'===============================================================================

'===============================================================================
  Обработка_данных_энкодера:
'-------------------------------------------------------------------------------
    If Счетчик_энкодера_1_вправо <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_вправо = Счетчик_энкодера_1_вправо * 10
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        Elseif Кольцевое_нажатие_1 = 2 Then
            Счетчик_энкодера_1_вправо = Счетчик_энкодера_1_вправо * 100
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        End If
        Счетчик_энкодера_1_вправо = 0
        Флаг_обновить_lcd_1 = 1
    End If

    If Счетчик_энкодера_1_влево <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_влево = Счетчик_энкодера_1_влево * 10
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        Elseif Кольцевое_нажатие_1 = 2 Then
            Счетчик_энкодера_1_влево = Счетчик_энкодера_1_влево * 100
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        End If
        Счетчик_энкодера_1_влево = 0
        Флаг_обновить_lcd_1 = 1
    End If
'-------------------------------------------------------------------------------
  Return
'===============================================================================

'===============================================================================
  Const Const_time_long_press_button = 40       ' Время выдержки длинного нажатия
  Const Const_time_short_press_button = 10       ' Время выдержки короткого нажатия
'===============================================================================
   Обработка_прерывания_timer2:
'-------------------------------------------------------------------------------
     !push R31
     !in R31 , sreg
     !push R31

    'Если кнопка нажата
     !SBIc Key_encoder_1_port , Key_encoder_1_pin       ' Пропустить если бит в порту очищен
     !rjmp Обработка_прерывания_timer2_1

     !lds R31 , {Счетчик_длины_нажатий}
     !inc R31
     !sts {Счетчик_длины_нажатий} , R31
     !cpi R31 , Const_time_long_press_button       ' Время выдержки длинного нажатия
     !BRLO Выход_прерывания_timer2       ' Перейти если меньше

     !ldi R31 , 1
     !sts {Длинное_нажатие_энкодер_1} , R31
     !ldi R31 , 0
     !sts {Короткое_нажатие_энкодер_1} , R31

    'Disable Pcint0
     !lds R31 , Pcicr
     !andi R31 , Disable_pcint0       '&B_1111_1110
     !sts Pcicr , R31

    'Pcmsk0.2 = 0
     !lds R31 , Pcmsk0
     !andi R31 , Reset_isr_key_encoder       '&B_1111_1011              ' Выключаем обработку прерывания с вывода PINB.2
     !sts Pcmsk0 , R31
     !rjmp Обработка_прерывания_timer2_6
     !rjmp Выход_прерывания_timer2

    Обработка_прерывания_timer2_1:
     !lds R31 , {Счетчик_длины_нажатий}
     !cpi R31 , Const_time_short_press_button       ' Время выдержки короткого нажатия
     !BRSH Обработка_прерывания_timer2_5

     !ldi R31 , 1
     !sts {Короткое_нажатие_энкодер_1} , R31
     !rjmp Обработка_прерывания_timer2_5

'============================
    Выход_прерывания_timer2:
     !pop R31
     !out Sreg , R31
     !pop R31

     !reti
'============================

    Обработка_прерывания_timer2_5:
     !lds R31 , Pcmsk0
     !ori R31 , Set_isr_key_encoder       ' Включаем обработку прерывания с вывода PINB.2
     !sts Pcmsk0 , R31

    Обработка_прерывания_timer2_6:
    'Stop TIMER2
     !lds R31 , TCCR2B
     !andi R31 , &B_1111_1000
     !sts TCCR2B , R31
     !lds R31 , {Счетчик_длины_нажатий}
     !sts {Count} , R31
     !ldi R31 , 0
     !sts {Счетчик_длины_нажатий} , R31
    'TIMER2 = 0
     !sts tcnt2 , R31
     !ldi R31 , 1
     !sts {Флаг_реакция_на_кнопки} , R31
     !rjmp Выход_прерывания_timer2
'===============================================================================

'===============================================================================
  'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0 - ОТ ДВУХФАЗНОГО ИМПУЛЬСНОГО ЕНКОДЕРА
  'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ
  ' (ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ
  'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
  'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ. П/П РАБОТАЕТ ТАКЖЕ ПРИ ИСПОЛЬЗОВАНИИ
  'ПРЕРЫВАНИЯ ОТ ЛЮБОГО ИЗМЕНЕНИЯ УРОВНЯ. НА СПАД СРАБАТЫВАЕТ, ФРОНТ ИГНОРИРУЕТ
'===============================================================================
  Const Con_irt = 5       'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ
  Const Conirt1 = Con_irt * 1.5       'ПОЛТОРЫ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ
  Const Conirt2 = Con_irt * 2       'ДВЕ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ
'===============================================================================
   Pcint0_0:
    !Inc R31       'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА
    !Cpi R31 , Conirt1       'это Con_irt*1.5
    !Brne pcint0_1       'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД
    !Rjmp Выход_pcint0

'===============================================================================
   Обработка_прерывания_pcint0:       'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ
'===============================================================================
     !push R31
     !in R31 , sreg
     !push R31

    'Если прерывание было от кнопки
     !Sbic Key_encoder_1_port, Key_encoder_1_pin       ' Пропустить если бит в порту очищен
     !rjmp Прерывание_энкодера_1

     !ldi R31 , 0
     !sts {Уровень_кнопки_энкодера_1} , R31
    'Start TIMER2
     !lds R31 , TCCR2B
     !andi R31 , &B_1111_1000
     !ori R31 , Prescale_1024       '&B_0000_0111       ' Выставляем Prescale = 1024, тем самым запуская TIMER2 (аналог "Start Timer2")
     !sts TCCR2B , R31
    'Pcmsk0.2 = 0
     !lds R31 , Pcmsk0
     !andi R31 , Reset_isr_key_encoder       ' Выключаем обработку прерывания с вывода PINB.2
     !sts Pcmsk0 , R31
     !rjmp Выход_pcint0

    Прерывание_энкодера_1:
    'ИНТЕГРИРУЮЩИЙ ФИЛЬТР (ФНЧ) ВХОДНОГО УРОВНЯ
     !Ldi R31 , Con_irt       'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ В СЧЕТЧИК
    Pcint0_1:
     !Sbic Encoder_1_port_1 , Encoder_1_pin_1       'ПРОВЕРИМ УРОВЕНЬ НА ВЫВОДЕ
     !Rjmp pcint0_15       'ЕСЛИ НА НОЖКЕ "1" - ПЕРЕХОД
     !Dec R31       'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ
     !Brne pcint0_1       'МНОГОКРАТНОГО ПОДТВЕРЖДЕНИЯ НИЗКОГО УРОВНЯ
     !Rjmp pcint0_2

    'ЕСЛИ УРОВНЬ ВЫСОКИЙ
    Pcint0_15:
     !Inc R31       'УВЕЛИЧИВАЕМ СЧЕТЧИК
     !Cpi R31 , Conirt2       'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО
     !Brne Выход_pcint0       'НА ВЫХОД
     !Rjmp pcint0_1
    '----------------------------------------------------------------
    'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ
    'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
    'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
    '----------------------------------------------------------------
    '|<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
    'chA---     ---------     ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ
    '      ¦   ¦  chA    ¦   ¦      ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА
    '       ---           ---      ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ
    'chB-----  |  ---------     ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ
    '      | ¦ | ¦  chB    ¦   ¦    (ЩЕЛЧКА) В КАНАЛЕ "A"
    '      |  ---           ---
    '      |   |<------ ВРАЩЕНИЕ ВПРАВО
    '----------------------------------------------------------------
    Pcint0_2:
    'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ
     !Sbis Encoder_1_port_2 , Encoder_1_pin_2
     !Rjmp pcint0_3       'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ

    'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ)
     !Lds R31 , {Счетчик_энкодера_1_влево}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
     !Inc R31
     !Sts {Счетчик_энкодера_1_влево} , R31
     !Rjmp pcint0_4

    Pcint0_3:
    'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ)
     !Lds R31 , {Счетчик_энкодера_1_вправо}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
     !Inc R31
     !Sts {Счетчик_энкодера_1_вправо} , R31
    Pcint0_4:
     !Ldi R31 , 1
     !Sts {Флаг_данные_энкодера_1} , R31       'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА"

   Выход_pcint0:

    !pop R31
    !out sreg , R31
    !pop R31

    !reti
'-------------------------------------------------------------------------------
  Return
'===============================================================================

Прерывания силами Bascom

$map       ' Получаем карту распределения памяти компилятором Bascom
  $regfile = "m328pdef.dat"       ' http://www.atmel.com/images/Atmel-8272- … asheet.pdf
  $crystal = 8000000       ' 1 МГц
  $framesize = 64       ' Размер области используемой и необходимой для преобразований
  $hwstack = 64       ' Размер аппаратного стека
  $swstack = 64       ' Размер программного стека

'  $baud = 38400
  'Ucsr0a.1 = 1       ' Baud = Baud * 2

'===============================================================================
'          Настройка МК для работы с LCD на базе контроллера hd44780
'   Обратите внимание, библиотеки не используются, выводы могут быть любые
'===============================================================================
  Config Lcdpin = Pin , Rs = Portc.0 , E = Portc.1 , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5
  Config Lcd = 20 * 4       ' Используемый LCD
  Initlcd       ' Инициализируем LCD
  Waitms 50       ' Ждем пока все запустится
  Cursor Off Noblink       ' Выключаем неморгающий курсор
'  Cursor On Noblink         ' Включаем неморгающий курсор
'  Cursor Blink       ' Включаем моргающий курсор
'  Upperline  1 строка, Lowerline  2 строка, Thirdline  3 строка, Fourthline 4 строка  - один из вариантов перехода на строку
  Cls       ' Чистим экран, на всякий случай

'===============================================================================
'                  Конфигурация МК для работы с энкодерами
'===============================================================================
  Config Timer2 = Timer , Prescale = 1024 , Clear Timer = 0       ' 16,384 ms при частоте МК 8МГц
  Stop Timer2
  Timer2 = 0
  On Timer2 Обработка_прерывания_timer2

'-----------------------------------------
'Конфигурация МК для работы с энкодером 1
'-----------------------------------------
  Config Pinb.0 = Input : Set Portb.0
  Encoder_1_port_1 Alias Pinb
  Encoder_1_pin_1 Alias 0

  Config Pinb.1 = Input : Set Portb.1
  Encoder_1_port_2 Alias Pinb
  Encoder_1_pin_2 Alias 1

  Config Pinb.2 = Input : Set Portb.2
  Key_encoder_1_port Alias Pinb
  Key_encoder_1_pin Alias 2

  On Pcint0 Обработка_прерывания_pcint0       ' Подпрограмма обработки прерывания PCINT0
  Pcmsk0 = &B_0000_0101       'PCINT0 (PINB.0) - один из боковых выводов энкодера + вывод кнопки (PCINT2 (PINB.2))

  Dim Count As Byte       ' Временная переменная, в релизе удалить
  Dim Val_byte As Byte       ' Временная переменная, в релизе удалить
  Dim Long_press As Byte       ' Временная переменная, в релизе удалить
  Dim Short_press As Byte       ' Временная переменная, в релизе удалить
  Dim Флаг_обновить_lcd_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Кольцевое_нажатие_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Счетчик_длины_нажатий As Byte
  Dim Флаг_данные_энкодера_1 As Byte       ' Временная переменная, в релизе удалить
  Dim Флаг_реакция_на_кнопки As Byte       ' Временная переменная, в релизе удалить
  Dim Переменная_integer_1 As Integer
  Dim Счетчик_энкодера_1_влево As Byte
  Dim Счетчик_энкодера_1_вправо As Byte
  Dim Длинное_нажатие_энкодер_1 As Byte
  Dim Уровень_кнопки_энкодера_1 As Byte : Уровень_кнопки_энкодера_1 = 255
  Dim Короткое_нажатие_энкодер_1 As Byte
  Dim Флаг_направл_кольц_нажатия As Byte

  Dim Short_press_str As String * 5
  Dim Переменная_str_1 As String * 10

'Начальное заполнение экрана
  Locate 1 , 1
  Lcd "Short_press  " ; "   x1"
  Locate 2 , 1
  Lcd "Encoder 1 " ; "       0"
  Locate 3 , 1
  Lcd "                    "

'--------------  Разрешаем работу необходимых прерываний  ----------------------
  Pcifr.pcif0 = 1 : Enable Pcint0       ' Сброс флаг прерывания и разрешение прерывания, иначе получается сразу +1 от энкодера
  Enable Timer2
'------------  Разрешаем работу всех включенных прерываний  --------------------
  Enable Interrupts
'===============================================================================
  Главный_цикл:
'===============================================================================

               If Флаг_данные_энкодера_1 <> 0 Then
                   Gosub Обработка_данных_энкодера
                   Флаг_данные_энкодера_1 = 0
                   Goto Главный_цикл
               End If

               If Флаг_обновить_lcd_1 <> 0 Then
                   Переменная_str_1 = Str(переменная_integer_1)
                   Переменная_str_1 = Format(переменная_str_1 , "+   0")
                   If Переменная_integer_1 = 0 Then
                       Mid(переменная_str_1 , 5 , 1) = " "
                   End If
                   Locate 2 , 13
                   Lcd Переменная_str_1
                   Флаг_обновить_lcd_1 = 0
                   Goto Главный_цикл
               End If

              If Флаг_реакция_на_кнопки <> 0 Then
                  If Короткое_нажатие_энкодер_1 <> 0 Then
                      Incr Short_press
                      Short_press_str = Str(short_press)
                      Short_press_str = Format(short_press_str , "  0")

                      If Флаг_направл_кольц_нажатия = 0 Then
                          Incr Кольцевое_нажатие_1
                          If Кольцевое_нажатие_1 > 1 Then
                              Флаг_направл_кольц_нажатия = 1
                          End If
                        Else
                          Decr Кольцевое_нажатие_1
                          If Кольцевое_нажатие_1 = 0 Then
                              Флаг_направл_кольц_нажатия = 0
                          End If
                      End If

                      If Кольцевое_нажатие_1 = 0 Then
                          Locate 1 , 15
                          Lcd "  x1"
                      Elseif Кольцевое_нажатие_1 = 1 Then
                          Locate 1 , 15
                          Lcd " x10"
                      Elseif Кольцевое_нажатие_1 = 2 Then
                          Locate 1 , 15
                          Lcd "x100"
                      End If
                      Короткое_нажатие_энкодер_1 = 0
                  End If

                  If Длинное_нажатие_энкодер_1 <> 0 Then
                      Incr Long_press
                      For Val_byte = 1 To 5
                          Locate 1 , 1
                          Lcd "     Save, or       "
                          Locate 2 , 1
                          Lcd "   Goto menu, or    "
                          Locate 3 , 1
                          Lcd "    Goto config     "
                          Locate 4 , 1
                          Lcd "                    "
                          Waitms 500
                          Locate 1 , 1
                          Lcd "                    "
                          Locate 2 , 1
                          Lcd "                    "
                          Locate 3 , 1
                          Lcd "                    "
                          Waitms 250
                      Next Val_byte
                      Locate 1 , 1
                      Lcd "                    "
                      Locate 2 , 1
                      Lcd "       Reboot       "
                      Locate 3 , 1
                      Lcd "                    "
                      Locate 4 , 1
                      Lcd "                    "
                      Waitms 1000
                      Длинное_нажатие_энкодер_1 = 0

                     'Reset Program
                      !jmp $00

                      Enable Pcint0       ' После реакции на длинное нажатие кнопки энкодера, обязательно включаем прерывания!!!

                  End If
                  Count = 0
                  Флаг_реакция_на_кнопки = 0
                  Goto Главный_цикл
              End If


   Idle
'===============================================================================
  Goto Главный_цикл
End
'===============================================================================

'===============================================================================
  Обработка_данных_энкодера:
'-------------------------------------------------------------------------------
    If Счетчик_энкодера_1_вправо <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_вправо = Счетчик_энкодера_1_вправо * 10
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        Elseif Кольцевое_нажатие_1 = 2 Then
            Счетчик_энкодера_1_вправо = Счетчик_энкодера_1_вправо * 100
            Переменная_integer_1 = Переменная_integer_1 + Счетчик_энкодера_1_вправо
        End If
        Счетчик_энкодера_1_вправо = 0
        Флаг_обновить_lcd_1 = 1
    End If

    If Счетчик_энкодера_1_влево <> 0 Then
        If Кольцевое_нажатие_1 = 0 Then
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        Elseif Кольцевое_нажатие_1 = 1 Then
            Счетчик_энкодера_1_влево = Счетчик_энкодера_1_влево * 10
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        Elseif Кольцевое_нажатие_1 = 2 Then
            Счетчик_энкодера_1_влево = Счетчик_энкодера_1_влево * 100
            Переменная_integer_1 = Переменная_integer_1 - Счетчик_энкодера_1_влево
        End If
        Счетчик_энкодера_1_влево = 0
        Флаг_обновить_lcd_1 = 1
    End If
'-------------------------------------------------------------------------------
  Return
'===============================================================================

'===============================================================================
   Обработка_прерывания_timer2:
'-------------------------------------------------------------------------------
     If Pinb.2 = 0 Then
         Incr Счетчик_длины_нажатий

         If Счетчик_длины_нажатий > 25 Then
             Gosub Обработка_прерывания_timer2_2
             Длинное_нажатие_энкодер_1 = 1
             Короткое_нажатие_энкодер_1 = 0
             Disable Pcint0
             Pcmsk0.2 = 0       ' Выключаем обработку прерывания с вывода PINB.2
         End If

       Else

         If Счетчик_длины_нажатий < 10 Then
             Короткое_нажатие_энкодер_1 = 1
         End If

         Gosub Обработка_прерывания_timer2_1
     End If

   Выход_прерывания_timer2:
'-------------------------------------------------------------------------------
  Return
'===============================================================================
  Обработка_прерывания_timer2_1:
'-------------------------------------------------------------------------------
    Pcmsk0.2 = 1       ' Включаем обработку прерывания с вывода PINB.2

  Обработка_прерывания_timer2_2:
    Stop Timer2
    Timer2 = 0
    Count = Счетчик_длины_нажатий
    Счетчик_длины_нажатий = 0
    Флаг_реакция_на_кнопки = 1
'-------------------------------------------------------------------------------
  Return
'===============================================================================

'-------------------------------------------------------------------------------
'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0 - ОТ ДВУХФАЗНОГО ИМПУЛЬСНОГО ЕНКОДЕРА
'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ
' (ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ
'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ. П/П РАБОТАЕТ ТАКЖЕ ПРИ ИСПОЛЬЗОВАНИИ
'ПРЕРЫВАНИЯ ОТ ЛЮБОГО ИЗМЕНЕНИЯ УРОВНЯ. НА СПАД СРАБАТЫВАЕТ, ФРОНТ ИГНОРИРУЕТ
'-------------------------------------------------------------------------------
  Const Con_irt = 5       '30       'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ
  Const Conirt1 = Con_irt * 1.5       'ПОЛТОРЫ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ
  Const Conirt2 = Con_irt * 2       'ДВЕ КОНСТАНТЫ ИНТЕГРИРОВАНИЯ

   Pcint0_0:
    !Inc R31       'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА
    !Cpi R31 , Conirt1       'это Con_irt*1.5
    !Brne pcint0_1       'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД
    !Rjmp Выход_pcint0

   'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ
   Обработка_прерывания_pcint0:
    If Pinb.2 = 0 Then

        Уровень_кнопки_энкодера_1 = 0
        Start Timer2
        Pcmsk0.2 = 0       ' Выключаем обработку прерывания с вывода PINB.2

      Else

        Прерывание_энкодера_1:
       'ИНТЕГРИРУЮЩИЙ ФИЛЬТР (ФНЧ) ВХОДНОГО УРОВНЯ
         !Ldi R31 , Con_irt       'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ В СЧЕТЧИК
        Pcint0_1:
         !Sbic Encoder_1_port_1 , Encoder_1_pin_1       'ПРОВЕРИМ УРОВЕНЬ НА ВЫВОДЕ
         !Rjmp pcint0_15       'ЕСЛИ НА НОЖКЕ "1" - ПЕРЕХОД
         !Dec R31       'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ
         !Brne pcint0_1       'МНОГОКРАТНОГО ПОДТВЕРЖДЕНИЯ НИЗКОГО УРОВНЯ
         !Rjmp pcint0_2

       'ЕСЛИ УРОВНЬ ВЫСОКИЙ
        Pcint0_15:
         !Inc R31       'УВЕЛИЧИВАЕМ СЧЕТЧИК
         !Cpi R31 , Conirt2       'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО
         !Brne Выход_pcint0       'НА ВЫХОД
         !Rjmp pcint0_1
       '---------------------------------------------
       'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ
       'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
       'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
       '---------------------------------------------
       '      |<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
       'chA---     ---------     ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ
       '      ¦   ¦  chA    ¦   ¦      ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА
       '       ---           ---      ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ
       'chB-----  |  ---------     ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ
       '      | ¦ | ¦  chB    ¦   ¦    (ЩЕЛЧКА) В КАНАЛЕ "A"
       '      |  ---           ---
       '      |   |<------ ВРАЩЕНИЕ ВПРАВО
       '---------------------------------------------
        Pcint0_2:
       'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ
         !Sbis Encoder_1_port_2 , Encoder_1_pin_2
         !Rjmp pcint0_3       'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ

       'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ)
         !Lds R31 , {Счетчик_энкодера_1_влево}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
         !Inc R31
         !Sts {Счетчик_энкодера_1_влево} , R31
         !Rjmp pcint0_4

        Pcint0_3:
       'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ)
         !Lds R31 , {Счетчик_энкодера_1_вправо}       'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
         !Inc R31
         !Sts {Счетчик_энкодера_1_вправо} , R31
        Pcint0_4:
         !Ldi R31 , 1
         !Sts {Флаг_данные_энкодера_1} , R31       'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА"

    End If       ' If Pinb.2 = 0 Then

   Выход_pcint0:
'-------------------------------------------------------------------------------
  Return
'===============================================================================


Симуляция (Протеус 7.10 sr0) + исходники (ASM; Bascom 2.0.7.1) + фото + видео  -  скачать


http://s2.uploads.ru/t/oWrEP.png http://s2.uploads.ru/t/MRX0i.jpg

+3

4

+4  -  ничего себе, такая популярная тема, а я думал никто и не заметит.

+2

5

Ещё вариант работы с энкодером, используется 1 таймер.

Мне нравится больше всех остальных. Работают даже убитые в хлам. Обвязка не нужна.

Симуляция (Протеус 7.10) + исходник (Bascom 2.0.7.1)  -  скачать, для форумчан из Украины качать через VPN, или пишите, закину на mail.

Что и как

в процессе вращения в переменной "Direction" получаете +1  или  -1, т.е., повернули вправо, повернули влево.
Если ±1 мало, умножаете на 2, 3, 4, ... 100, получаете шаг вращения 2, 3, 4, ...100

переменная "Direction", обязательно   Integer

Prev_encoder_value_int -  предыдущее значение вашей переменной
Encoder_value_int  -  текущее значение

для работы энкодера нужно прерывание по таймеру (антидребезг), написано на ASM для максимального быстродействия, можно использовать в обработчике 7-seg развёртки

в основном цикле нужен только этот кусочек:

        If Encoder_value_int <> Prev_encoder_value_int Then
            Prev_encoder_value_int = Encoder_value_int
        End If

главное, чтобы туда попадать, хоть изредка

Эти константы не трогать, это автомат текущего состояния энкодера
Const State_0 = 0
Const State_a = 1
Const State_b = 2
Const State_ab = 3

Направление вращения, есть энкодеры с реверсом.
Почему именно переменная, чтобы можно было менять из менюшки
Значение 0 или 1
Dim Enc_reverse As Byte : Enc_reverse = 1       'Переменная для хранения значения реверсивности энкодера (китайский вариант)

+2

6

Настройка для других выводов МК,

'===============================================================================
'           Н А С Т Р О Й К А     Э Н К О Д Е Р А
'===============================================================================
  Encoder_pin Alias Pind   'используемый порт
  Encoder_ab Alias &B_0000_1100   'используемые выводы

  Config Pind.2 = Input : Portd.2 = 1
  Config Pind.3 = Input : Portd.3 = 1

  Const State_0 = 0
  Const State_a = &B_0000_0100
  Const State_b = &B_0000_1000
  Const State_ab = &B_0000_1100

'===============================================================================
'           Н А С Т Р О Й К А     Э Н К О Д Е Р А
'===============================================================================
  Encoder_pin Alias Pinb      'используемый порт
  Encoder_ab Alias &B_1100_0000     'используемые выводы

  Config Pinb.6 = Input : Portb.6 = 1
  Config Pinb.7 = Input : Portb.7 = 1

  Const State_0 = 0
  Const State_a = &B_0100_0000
  Const State_b = &B_1000_0000
  Const State_ab = &B_1100_0000

+1