Программа, рассмотренная в этой статье, разработана специально для самодельного ИК-пульта дистанционного управления на контроллере PIC12F629. Если вы измените аппаратную часть (например, будете использовать другой порядок ног, повесите внешний генератор), то программу также нужно будет переделать.
Прежде чем разбираться с программой, наверное каждому, будет полезно посмотреть описание протокола NEC.
Представленный вариант полностью написан на ассемблере, в конце статьи выложены сорцы (с комментариями) и прошивка. Чтобы проще было разбираться — добавлю немного текстухи. Итак, в данном случае для отсчёта времени импульса и паузы используется встроенный таймер TMR0. Кроме этого, в алгоритме используются два специальных управляющих регистра (это обычные регистры общего назначения, которые мы используем для управления передачей). Один из них — счётчик количества передаваемых импульсов и пауз, причём отсчёт начинается с паузы стартовой последовательности (нулевое значение счётчика). Соответственно, если значение счётчика нечётное — мы должны передать импульс, а если чётное — паузу. Второй регистр служит для указания позиции передаваемого бита (в нужной позиции у нашего регистра взведён бит) и сдвигается влево каждый раз при передаче паузы. Поскольку данные передаются младшим битом вперёд, то сначала в этом регистре установлен нулевой бит. После того, как регистр сдвинется влево 8 раз, указатель адреса передаваемого байта устанавливается на следующий предназначенный для передачи байт, а указатель позиции передаваемого бита снова позиционируется на нулевой бит.
Если интересно — можете посмотреть для сравнения аналогичную программу для ATtiny13 (алгоритм практически такой же, только управление ИК-диодом осуществляется другой ногой контроллера).
Ну, хватит слов, перейдём к алгоритму и программе (кому слов не хватит — пишите на форум).
Алгоритм:
Итак, пусть в аппаратной части мы имеем:
входы: GP5 — кнопка SB1, GP2 — кнопка SB2, GP4 — кнопка SB3, GP1 — кнопка SB4
выходы: GP0 — вывод информации по протоколу NEC.
MCLR внешне подтянут к питанию; используется внутренний генератор.
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-файл
Внимание. Во избежание затирания калибровочных констант — алгоритм заливки этой прошивки в контроллер следующий:
- Считываем текущую конфигурацию контроллера.
- Записываем значение битов калибровки схемы BOR (12-й, 13-й биты слова конфигурации, они же bandgap)
- Записываем значение последнего слова программного кода (слово по адресу 03FF) — биты калибровки генератора.
- Открываем нашу прошивку в программе программатора, и меняем в ней биты калибровки схемы BOR и генератора на считанные и записанные значения.
- Всё, теперь эту (исправленную для конкретного экземпляра PIC12) прошивку можно заливать в контроллер.