В этой статье рассмотрена простейшая программа для самодельного ИК-приемника сигналов дистанционного управления на контроллере PIC12F629. Если вы измените аппаратную часть (например, будете использовать другой порядок ног, повесите внешний генератор), то программу так же нужно будет переделать.
Программа реализует следующие функции:
1) Прием кода NEC, определение версии (стандартный или расширенный NEC).
2) В рабочей части программы реализуется управление четырьмя светодиодами. При получении адреса CB04h и команды 4Ah инвертируется состояние выходов GP0, GP1, GP2, GP4 (при старте на выходах GP2, GP4 установлен высокий уровень, а на GP0, GP1 — низкий).
Прежде чем разбираться с программой, советую посмотреть описание протокола NEC.
Основные идеи алгоритма:
1) Выход ILMS1836 — инвертированный, т.е. когда принимается "1" — на выходе фотоприемника "0" (низкий уровень), когда приема нет или принимается "0" — на выходе фотоприемника "1" (высокий уровень).
2) При появлении прерывания от входа фотоприёмника устанавливается специальный флаг, сигнализирующий о том, что идёт приём и запускается таймер на 16,4 мс. При последующих прерываниях проверяется уровень сигнала на входе и в специальный буфер записывается время (по таймеру) между прерываниями, произошедшими в результате перехода входного сигнала с высокого уровня на низкий. Таким образом измеряется длина каждого бита. Переполнение таймера сигнализирует об ошибке (в течении 16 мс уровень на входе не изменяется). После приёма всех бит пакета, мы из записанных 32 байт с длительностями импульсов восстанавливаем 4 байта исходного пакета.
Общий размер буфера 33 байта: длительности 32-х битов + длительность стартового импульса.
3) Если третий байт равен инвертированному четвёртому байту, то приём осуществлён без ошибок.
4) Если первый байт равен инвертированному второму байту, то мы имеем дело с обычным NEC, в противном случае — с расширенным NEC.
Алгоритм:
![]() ![]() ![]() ![]() |
list p = 12f629 __config 01FC4h ;*** Переменные ****************************************** CBLOCK 0x20 ; Начальный адрес блока пользовательской памяти Flags ; регистр флагов ; 0 бит - приём, 1 бит - наличие необраб.инфы, 2 бит - наличие обработ.инфы ; 3-й бит Trial NEC, 4-й бит - Extended NEC Counter ; резервная переменная ADDRESS_LOW ; младший байт адреса ADDRESS_HIGH ; старший байт адреса COMMAND ; команда INV_COMMAND ; инвертированная команда ; далее 33 байт, в которые записываются длительности пауз ; адреса:26, 27-2Eh, 2Fh-36h, 37h-3eh, 3fh-46h ENDC ;**** Константы / Адреса регистров ************************ Status equ 03h ; выбор банка GPIO equ 05h ; регистр управления защелками порта. (банк 0) Cmcon equ 19h ; регистр Cmcon - компаратора (банк 0) TrisIO equ 05h ; выбор направления работы выводов порта INTCON equ 0Bh ; регистр разрешения(1)/запрета(0) прерываний IOCB equ 16h ; индивидуальная настройка прерываний для входов OPT_REG equ 01h ; настройка таймера (банк 1) TMR0 equ 01h ; регистр таймера (банк 0) OSCCAL equ 10h ; регистр хранения калибровочной константы INDF equ 0h ; регистр косвенной адресации FSR equ 04h ; регистр адреса при косвенной адресации AL equ b'00000100' ; адрес 04h (заданный младший байт адреса) AH equ b'11001011' ; адрес CBh (заданный старший байт адреса) COM equ b'01001010' ; команда 4Ah, inv B5h ; код команды, при получении которой должны выполняться действия ;********************************************************** ; Пусть у нас GP5 - вход фотоприёмника, GP0, GP1, GP2, GP4 - выходы ;********************************************************** org 0 ;********************************************************** ;*** КОНФИГУРИРОВАНИЕ КОНТРОЛЛЕРА ************************* ;*** Калибровка Генератора ******************************** start bsf Status,5 Call 3FFh ; Загрузить калибровочную константу в w movwf OSCCAL goto main ;*** ПОДПРОГРАММА ПРЕРЫВАНИЯ ****************************** org 4 movf PIO,0 btfss INTCON,0 ; если бит 0 установлен - было прерывание от входов goto errors ;--- Прерывание от входов --- btfsc GPIO,5 ; если на входе низкий уровень - начало импульса goto exit ;--- Проверка моих флагов --- btfss Flags,0 ; если сброшен флаг 0, то у нас новый приём goto new_rec ;--- Продолжение приёма --- movf TMR0,0 ; считать и сохранить время movwf INDF ;--- Перезапуск таймера --- clrf TMR0 ;--------------------------------------------------------- incf FSR,1 ; увеличить адрес movlw 47h ; проверяем значение указателя xorwf FSR,0 btfss Status,2 ; если указатель=47 - приём окончен goto exit ;--- Окончание приёма --- bcf Flags,0 bsf Flags,1 ; устанавливаем флаг наличия принятой информации bcf INTCON,5 ; запрещаем прерывания bcf INTCON,3 return ;--- Новый приём --- new_rec bsf Flags,0 movlw 26h movwf FSR ; установить адрес на приём ;--- Перезапуск таймера --- clrf TMR0 ;--------------------------------------------------------- bcf INTCON,2 ; сбросить флаг переполнения таймера bsf INTCON,5 ; разрешить прерывание от таймера exit bcf INTCON,0 ; сбросить флаг прерывания от входов retfie ;*** ПРОДОЛЖЕНИЕ КОНФИГУРИРОВАНИЯ ************************ ;--- Установка направления работы ног -------------------- main bcf Status,5 ; перейти в банк 0 clrf GPIO ; инициализация защелок (все выходы равны нулю) movlw .7 ; биты 0..2 поднять movwf Cmcon ; компаратор выкл., GP0, GP1, GP2 - цифровые вх/вых bsf Status,5 ; перейти в 1-й банк movlw b'00000101' ; настройка предделителя 1:64, 64*256=16,384 мс movwf OPT_REG movlw b'00101000' ; настройка выходов (GP5,GP3 - входы) movwf TrisIO ;--- Настройка прерываний -------------------------------- bsf IOCB,5 ; разрешить прерывание по входу GP5 bcf Status,5 ; перейти в банк 0 bsf INTCON,3 ; разрешить прерывания на входах GPIO ;--------------------------------------------------------- bsf GPIO,4 ; инвертированные выходы nop bsf GPIO,2 ;*** Включение прерываний ******************************** bsf INTCON,7 ; разрешить немаскированные прерывания ;*** РАБОЧАЯ ЧАСТЬ *************************************** work btfsc Flags,1 ; если есть необработ-е данные - надо их обработать call Convert btfsc Flags,2 ; если есть обработ-е данные - надо выполнить действие call action goto work ;************************************************ Convert movlw 26h movwf FSR ; устанавливаем указатель на регистр, ; в котором хранится длительность первого импульса movf INDF,0 addlw .53 ; 53 - это (инверсия 203)+1, получается равносильно ; вычитанию 211 (13,5мс=211 отсчётов TMR0) andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализир-е знач-е лежит в диапаз. от 203 до 203+15 btfss Status,2 ; установился флаг Z? goto errors ; если не попадаем в тайминг, то это не наш протокол ;--------------------------------------------------------- next1 bcf Status,0 ; сбрасываем флаг переноса (если он вдруг установлен) rrf ADDRESS_LOW,1 incf FSR,1 movf INDF,0 addlw .229 ; 229 - это (инверсия 27)+1, равносильно вычитанию 27 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 27 до 27+15 btfss Status,2 ; установился флаг Z? goto Nol1 ; если не попадаем в тайминг "1", то проверяем на "0" ;--------------------------------------------------------- bsf ADDRESS_LOW,7 ; пишем "1" в старший бит ADDRESS_LOW goto One1 Nol1 movf INDF,0 addlw .246 ; 246 - это (инверсия 10)+1, равносильно вычитанию 10 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 10 до 10+15 btfss Status,2 ; установился флаг Z? goto errors ; если не попадаем в тайминг "0", то ошибка ;--------------------------------------------------------- One1 movf FSR,0 sublw 2eh ; если отсчитали первые 8 бит - идём дальше btfss Status,2 ; установился флаг Z? goto next1 ; если не записали 8 бит - повторяем ;--------------------------------------------------------- next2 bcf Status,0 ; сбрасываем флаг переноса (если он вдруг установлен) rrf ADDRESS_HIGH,1 incf FSR,1 movf INDF,0 addlw .229 ; 229 - это (инверсия 27)+1, равносильно вычитанию 27 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 27 до 27+15 btfss Status,2 ; установился флаг Z? goto Nol2 ; если не попадаем в тайминг "1", то проверяем на "0" ;--------------------------------------------------------- bsf ADDRESS_HIGH,7 ; пишем "1" в старший бит ADDRESS_HIGH goto One2 Nol2 movf INDF,0 addlw .246 ; 246 - это (инверсия 10)+1, равносильно вычитанию 10 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 10 до 10+15 btfss Status,2 ; установился флаг Z? goto errors ; если не попадаем в тайминг "0", то ошибка ;--------------------------------------------------------- One2 movf FSR,0 sublw 36h ; если отсчитали вторые 8 бит - идём дальше btfss Status,2 ; установился флаг Z? goto next2 ; если не записали 8 бит - повторяем ;--------------------------------------------------------- next3 bcf Status,0 ; сбрасываем флаг переноса (если он вдруг установлен) rrf COMMAND,1 incf FSR,1 movf INDF,0 addlw .229 ; 229 - это (инверсия 27)+1, равносильно вычитанию 27 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 27 до 27+15 btfss Status,2 ; установился флаг Z? goto Nol3 ; если не попадаем в тайминг "1", то проверяем на "0" ;--------------------------------------------------------- bsf COMMAND,7 ; пишем "1" в старший бит COMMAND goto One3 Nol3 movf INDF,0 addlw .246 ; 246 - это (инверсия 10)+1, равносильно вычитанию 10 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 10 до 10+15 btfss Status,2 ; установился флаг Z? goto errors ; если не попадаем в тайминг "0", то ошибка ;--------------------------------------------------------- One3 movf FSR,0 sublw 3eh ; если отсчитали третьи 8 бит - идём дальше btfss Status,2 ; установился флаг Z? goto next3 ; если не записали 8 бит - повторяем ;--------------------------------------------------------- next4 bcf Status,0 ; сбрасываем флаг переноса (если он вдруг установлен) rrf INV_COMMAND,1 incf FSR,1 movf INDF,0 addlw .229 ; 229 - это (инверсия 27)+1, равносильно вычитанию 27 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 27 до 27+15 btfss Status,2 ; установился флаг Z? goto Nol4 ; если не попадаем в тайминг "1", то проверяем на "0" ;--------------------------------------------------------- bsf INV_COMMAND,7 ; пишем "1" в старший бит INV_COMMAND goto One4 Nol4 movf INDF,0 addlw .246 ; 246 - это (инверсия 10)+1, равносильно вычитанию 10 andlw b'11110000' ; если кроме 2-х младших битов все остальные равны нулю, ; то анализ-мое знач-е лежит в диапазоне от 10 до 10+15 btfss Status,2 ; установился флаг Z? goto errors ; если не попадаем в тайминг "0", то ошибка ;--------------------------------------------------------- One4 movf FSR,0 sublw 46h ; если отсчитали четвертые 8 бит - идём дальше btfss Status,2 ; установился флаг Z? goto next4 ; если не записали 8 бит - повторяем ;--------------------------------------------------------- bcf Flags,1 bsf Flags,2 return ;********************************************************* action comf INV_COMMAND,0 ; пишем в аккумулятор инвертир.команду INV_COMMAND xorwf COMMAND,0 ; сравниваем btfss Status,2 ; установился флаг Z? goto errors ; если команда не совпад. с инвертир.командой,- ошибка ;--------------------------------------------------------- comf ADDRESS_LOW,0 ; пишем в аккумулятор инвертированный адрес xorwf ADDRESS_HIGH,0 ; сравниваем btfss Status,2 ; установился флаг Z? goto ext_nec ; если не равны, то это Extended NEC ;--------------------------------------------------------- bsf Flags,3 ; если равны - поднимаем 3-й бит (Trial NEC) goto next_trial ext_nec bsf Flags,4 ; если не равны - поднимаем 4-й бит (Extended NEC) next_trial movf ADDRESS_LOW,0 ; проверка адреса xorlw AL btfss Status,2 ; установился флаг Z? goto errors ; если адрес не совпадает, то ошибка movf ADDRESS_HIGH,0 ; проверка адреса xorlw AH btfss Status,2 ; установился флаг Z? goto errors ; если адрес не совпадает, то ошибка movf COMMAND,0 ; проверка адреса xorlw COM btfss Status,2 ; установился флаг Z? goto errors ; если команда не совпадает, то ошибка ;--------------------------------------------------------- movlw b'00010111' ; инвертируем выходы GP0, GP1, GP2, GP4 xorwf GPIO,1 ;--------------------------------------------------------- errors clrf Flags clrf INTCON bsf INTCON,3 ; разрешить прерывания на входах GPIO retfie end ;--------------------------------------------------------- |
Скачать готовую прошивку и asm-файл
Спасибо товарищу jrman-у за доработку и тесты этой проги в железе.
Внимание. Во избежание затирания калибровочных констант — алгоритм заливки этой прошивки в контроллер следующий:
- Считываем текущую конфигурацию контроллера.
- Записываем значение битов калибровки схемы BOR (12-й, 13-й биты слова конфигурации, они же bandgap)
- Записываем значение последнего слова программного кода (слово по адресу 03FF) — биты калибровки генератора.
- Открываем нашу прошивку в программе программатора, и меняем в ней биты калибровки схемы BOR и генератора на считанные и записанные значения.
- Всё, теперь эту (исправленную для конкретного экземпляра PIC12) прошивку можно заливать в контроллер.