Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Программа для самодельного ИК-пульта, протокол NEC (кодирование длиной паузы)

Программа, рассмотренная в этой статье, разработана специально для самодельного ИК-пульта дистанционного управления на контроллере PIC12F629. Если вы измените аппаратную часть (например, будете использовать другой порядок ног, повесите внешний генератор), то программу также нужно будет переделать.

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

Представленный вариант полностью написан на ассемблере, в конце статьи выложены сорцы (с комментариями) и прошивка. Чтобы проще было разбираться — добавлю немного текстухи. Итак, в данном случае для отсчёта времени импульса и паузы используется встроенный таймер TMR0. Кроме этого, в алгоритме используются два специальных управляющих регистра (это обычные регистры общего назначения, которые мы используем для управления передачей). Один из них — счётчик количества передаваемых импульсов и пауз, причём отсчёт начинается с паузы стартовой последовательности (нулевое значение счётчика). Соответственно, если значение счётчика нечётное — мы должны передать импульс, а если чётное — паузу. Второй регистр служит для указания позиции передаваемого бита (в нужной позиции у нашего регистра взведён бит) и сдвигается влево каждый раз при передаче паузы. Поскольку данные передаются младшим битом вперёд, то сначала в этом регистре установлен нулевой бит. После того, как регистр сдвинется влево 8 раз, указатель адреса передаваемого байта устанавливается на следующий предназначенный для передачи байт, а указатель позиции передаваемого бита снова позиционируется на нулевой бит.

Если интересно — можете посмотреть для сравнения аналогичную программу для ATtiny13 (алгоритм практически такой же, только управление ИК-диодом осуществляется другой ногой контроллера).

Ну, хватит слов, перейдём к алгоритму и программе (кому слов не хватит — пишите на форум).

Алгоритм:

Алгоритм реализации протокола NEC для ПДУ

Итак, пусть в аппаратной части мы имеем:

входы: GP5 — кнопка SB1, GP2 — кнопка SB2, GP4 — кнопка SB3, GP1 — кнопка SB4

выходы: GP0 — вывод информации по протоколу NEC.

MCLR внешне подтянут к питанию; используется внутренний генератор.

Текст программы под катом (в данной программе частота несущей 36 кГц)

 list    p = 12f629
 __config 01FE4h
;*** Переменные ******************************************
  CBLOCK 0x20  ; Начальный адрес блока пользовательской памяти
    T_Cr       ; таймер несущей
    C_imp      ; счетчик кол-ва импульсов. если 0-й бит (C_imp-1) = 0 - последним
               ; посылали единичный полубит, если единице - нулевой.
               ; биты 1-3 - № посылаемого бита. биты 4-7 - № посылаемого байта
    Nbit       ; позиция посылаемого бита
    Byte1      ; первый байт
    Byte2      ; второй байт
    Byte3      ; третий байт
    Byte4      ; четвёртый байт
  ENDC
;*** Константы / Адреса регистров ************************
 P1    equ  .200        ; константа для паузы
 P2    equ  .200        ; константа для паузы
 Cr1   equ  .2          ; длительность 1 несущей
 Cr0   equ  .4          ; длительность 0 несущей
 Th    equ  .246        ; 255-длительность высокого уровня
 Tl1   equ  .229        ; 255-длительность низкого уровня 1-го бита
 Tl0   equ  .246        ; 255-длительность низкого уровня 0-го бита
 Tsl   equ  .185        ; 255-длит-ть низкого уровня в старт. последов-ти
 Tsh   equ  .115        ; 255-длит-ть высокого уровня в старт. последов-ти
 AL    equ  b'00000100' ; адрес 04h (заданный младший байт адреса)
 AH    equ  b'11001011' ; адрес CBh (заданный старший байт адреса)
 CMD_1 equ  4Ah         ; код команды 1
 CMD_2 equ  4Bh         ; код команды 2
 CMD_3 equ  4Ch         ; код команды 3
 CMD_4 equ  4Dh         ; код команды 4
;---------------------------------------------------------
 Status  equ     03h    ; Регистр выбора банка
 GPIO    equ     05h    ; Регистр управления защелками порта
 Cmcon   equ     19h    ; Регистр Cmcon - компаратора
 TrisIO  equ     05h    ; выбор направления работы выводов порта
 OPT_REG equ     01h    ; Настройка таймера (банк 1)
 TMR0    equ     01h    ; Регистр таймера (банк 0)
 INTCON  equ     0Bh    ; Регистр разрешения(1)/запрета(0) прерываний
 INDF    equ     0h     ; регистр косвенной адресации
 FSR     equ     04h    ; регистр адреса при косвенной адресации
 OSCCAL  equ     10h    ; Регистр хранения калибровочной константы
 F       equ     1      ; Результат направить в регистр.
;*********************************************************
; Пусть у нас GP0 - выход на транзистор управления светодиода,
; GP1, GP2, GP4, GP5 - входы
;*********************************************************
 
              org 0
;*** НАЧАЛЬНАЯ НАСТРОЙКА КОНТРОЛЛЕРА *********************
;*** Калибровка Генератора *******************************
      bsf    Status,5
      Call   3FFh       ; Загрузить калибровочную константу в w
      movwf  OSCCAL
      goto   start
;*********************************************************
;*** ПОДПРОГРАММА ПРЕРЫВАНИЯ *****************************
      bcf    GPIO,0     ; выключить выход, если он был включен
      bcf    INTCON,2   ; сбросить флаг переполнения TMR0
;--------------------
      movf   C_imp,1    ; проверяем счётчик импульсов на ноль
      btfsc  Status,2   ; если счётчик равен 0 - переход на Send_Start0
      goto   Send_Start0
;--------------------
      btfsc  C_imp,0    ; проверяем 0-й бит счётчика, если 
                        ; он = 1 - посылаем высокий уровень,
      goto   Send_HL    ; который у нас всегда одинаковой длины
;--- если передали 65 имп-в (т.е. должны перед. 66-й) - передача окончена
      movlw  .66
      xorwf  C_imp,0
      btfss  Status,2   ; если флаг Z установился - пропускаем переход
                        ; к передаче низкого уровня и завершаем передачу
      goto   Send_LL
;-------------------
Send_end
      clrf   INTCON     ; сбрасываем все прерывания и флаги
      call   Pause      ; выдерживаем паузу
      goto   Scan       ; переходим к сканированию клавиш
;--- посылаем низкий уровень ---
Send_LL
      movf   INDF,0     ; читаем передаваемый байт
      andwf  Nbit,0     ; "И" передаваемого байта с указ.передаваем. бита
      btfss  Status,2   ; если Z=1 - в передаваемом байте, в поз.Nbit у нас 0
      goto   Send_L1
;--- загружаем длит-ть низкого уровня, соответствующую нулевому биту ---
Send_L0
      movlw  Tl0
      movwf  TMR0       ; загружаем Tl0 в TMR0
      goto   Next
;--- загружаем длит-ть низкого уровня, соответствующую единичному биту ---
Send_L1
      movlw  Tl1
      movwf  TMR0       ; загружаем Tl1 в TMR0
Next  incf   C_imp,1    ; увеличить счётчик импульсов
      bcf    Status,0   ; сбросить флаг переноса
      rlf    Nbit,1     ; сдвинуть указатель передаваемого бита
      btfss  Status,0   ; если С=1 - ещё раз сдв.влево указ.бита и увелич.FSR
      goto   Next2
      rlf    Nbit,1
      incf   FSR,1
Next2 bsf    INTCON,7   ; разрешить немаскированные прерывания
      goto   Llevel
;--------------------
Send_HL
      movlw  Th
      movwf  TMR0       ; загрузить Th в TMR0
      incf   C_imp,1    ; увеличить счётчик импульсов
      bsf    INTCON,7   ; разрешить немаскированные прерывания
      goto   Hlevel
;--------------------
Send_Start0
      clrf   Nbit       ; очищаем указатель позиции передаваемого бита
      bsf    Nbit,0     ; поднимаем в указателе 0-й бит
      movlw  Byte1
      movwf  FSR        ; адрес косвенной адресации ставим на первый байт
      movlw  Tsl
      movwf  TMR0       ; загрузить Tsl в TMR0
      incf   C_imp,1    ; увеличить счётчик импульсов
      bsf    INTCON,7   ; разрешить немаскированные прерывания
      goto Llevel
;*** КОНЕЦ ПРЕРЫВАНИЯ ************************************
 
;*** ПРОДОЛЖЕНИЕ НАЧАЛЬНОЙ НАСТРОЙКИ *********************
start bcf    Status,5   ; перейти в банк 0
      clrf   GPIO       ; инициализация защелок (нули на всех защелках)
      movlw  .7         ; биты 0..2 поднять
      movwf  Cmcon      ; компаратор выключен, GP0, GP1, GP2 - цифровые вх/вых
      bsf    Status,5   ; Перейти в 1-й банк
      movlw  .62        ; Записать конфигурацию GPIO в аккумулятор (W)
                        ; .62=00 111110 GP0 - выход, остальные - входы
      movwf  TrisIO     ; Скопировать конфигурацию GPIO из W в регистр TrisIO.
;--- Настраиваем предделитель таймера ---
      movlw  b'00000101'; настройка предделителя 1:64, 64*256=16,384 мс
      movwf  OPT_REG
      bcf    Status,5   ; Перейти в 0-й банк
;--- Формируем младший и старший байт адреса ---
      movlw  AL
      movwf  Byte1      ; младший байт адреса
      movlw  AH
      movwf  Byte2      ; старший байт адреса
;*** КОНЕЦ НАЧАЛЬНОЙ НАСТРОЙКИ ***************************
 
;*** СКАНИРОВАНИЕ КЛАВИАТУРЫ *****************************
Scan  btfss  GPIO,5     ; если на входе GP5 низкий уровень - нажата клавиша 1
      goto   Tx_CMD1
      btfss  GPIO,2     ; если на входе GP2 низкий уровень - нажата клавиша 2
      goto   Tx_CMD2
      btfss  GPIO,4     ; если на входе GP4 низкий уровень - нажата клавиша 3
      goto   Tx_CMD3
      btfss  GPIO,1     ; если на входе GP1 низкий уровень - нажата клавиша 4
      goto   Tx_CMD4
      goto   Scan
;*** КОНЕЦ СКАНИРОВАНИЯ **********************************
 
;*** ПОДГОТОВКА К НАЧАЛУ ПЕРЕДАЧИ ************************
Tx_CMD1
      movlw  CMD_1      ; загружаем номер команды в аккумулятор
      goto   Form_Com
;------------------------
Tx_CMD2
      movlw  CMD_2      ; загружаем номер команды в аккумулятор
      goto   Form_Com
;------------------------
Tx_CMD3
      movlw  CMD_3      ; загружаем номер команды в аккумулятор
      goto   Form_Com
;------------------------
Tx_CMD4
      movlw  CMD_4      ; загружаем номер команды в аккумулятор
Form_Com
      movwf  Byte3      ; пишем в третий байт номер команды
      movwf  Byte4
      comf   Byte4,1    ; в 4-й байт пишем инвертированный номер команды
;--- Сбрасываем счётчик импульсов, загружаем в таймер время передачи
;--- высокого уровня первого полубита и включаем прерывания от таймера
      clrf   C_imp      ; сбросить счётчик импульсов
      movlw  Tsh
      movwf  TMR0       ; загрузить Tsh в таймер
      bcf    INTCON,2   ; сбросить флаг переполнения таймера
      bsf    INTCON,5   ; разрешить прерывание от таймера
      bsf    INTCON,7   ; разрешить немаскированные прерывания
;--- goto Hlevel - эту команду не пишем, переход и так происходит на Hlevel
;*** КОНЕЦ ПОДГОТОВКИ ************************************
 
;*** ПРОЦЕДУРЫ ПЕРЕДАЧИ ВЫСОКОГО И НИЗКОГО УРОВНЯ СИГНАЛА
;*** Передача высокого уровня ***
Hlevel
      movlw  Cr1        ; поместить константу Cr1 в аккумулятор
      movwf  T_Cr       ; скопир-ть аккум-р в регистр T_Cr (таймер несущей)
      bsf    GPIO,0     ; установить на выходе GP0 единицу
Cr1_Y decfsz T_Cr,F     ; декремент содержимого регистра T_Cr с ветвлением
      goto   Cr1_Y      ; переход на метку Cr1_Y
      nop               ; для точной подстройки времени высокого уровня
                        ; (и соответственно частоты и скважности)
      bcf    GPIO,0     ; Установить на выходе GP0 ноль
      movlw  Cr0        ; поместить константу Cr0 в аккумулятор
      movwf  T_Cr       ; скопировать содержимое аккум-ра в регистр T_Cr
Cr0_Y decfsz T_Cr,F
      goto   Cr0_Y
      nop               ; для точной подстройки времени низкого уровня
      nop               ; для точной подстройки времени низкого уровня
      goto   Hlevel
;*** Передача низкого уровня ***
Llevel
      nop
      goto  Llevel
;*** КОНЕЦ ПРОЦЕДУР ПЕРЕДАЧИ *****************************
 
;*** ПОДПРОГРАММА ПАУЗЫ **********************************
Pause movlw  P1
      movwf  T_Cr
cikl2 movlw  P2
      movwf  C_imp
cikl1 nop
      decfsz C_imp,1
      goto   cikl1
      decfsz T_Cr,1
      goto   cikl2
      return
;*********************************************************
end
;---------------------------------------------------------

[свернуть]

Скачать готовую прошивку и asm-файл

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

  1. Считываем текущую конфигурацию контроллера.
  2. Записываем значение битов калибровки схемы BOR (12-й, 13-й биты слова конфигурации, они же bandgap)
  3. Записываем значение последнего слова программного кода (слово по адресу 03FF) — биты калибровки генератора.
  4. Открываем нашу прошивку в программе программатора, и меняем в ней биты калибровки схемы BOR и генератора на считанные и записанные значения.
  5. Всё, теперь эту (исправленную для конкретного экземпляра PIC12) прошивку можно заливать в контроллер.

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