;__________________________________________________________________________________________________ ;|-------------------------------------------------------------------------------------------------| ;| УСТРОЙСТВО: термометр с динамической индикацией ;|-------------------------------------------------------------------------------------------------| ;| АВТОР: picmaniac (picmaniac@rambler.ru) ВЕРСИЯ: 0.2b ДАТА: 15 февраля 2007 г. FREEWARE | ;|-------------------------------------------------------------------------------------------------| ;|-------------------------------------------------------------------------------------------------| ;| Работа устройства: | ;|-------------------------------------------------------------------------------------------------| ;| Замечания по аппаратной части: версия для PIC16F628A, годится и для PIC16F628 | ;|-------------------------------------------------------------------------------------------------| ;| Замечания по программе: создана в среде MPLAB 7.42 под WINDOWS XP SP1 | ;|_________________________________________________________________________________________________| LIST P=16F628A #include ;Добавляем стандартный файл заголовка MPLAB __CONFIG _CP_OFF & _PWRTE_ON & _WDT_ON & _XT_OSC & _BODEN_OFF & _LVP_OFF & _MCLRE_ON radix hex ; --- Константы --- ; --- Макрокоманды --- jnz MACRO metka1 ; условный переход, btfss STATUS,Z ; если не 0 goto metka1 endm jz MACRO metka2 ; условный переход, btfsc STATUS,Z ; если 0 goto metka2 endm jnc MACRO metka3 ; условный переход, btfss STATUS,C ; если нет переноса goto metka3 endm jc MACRO metka4 ; условный переход, btfsc STATUS,C ; если перенос goto metka4 endm mov MACRO DEST1,SOURCE1 ; пересылка регистр-регистр movf SOURCE1,W movwf DEST1 endm mvi MACRO DEST2,CONST2 ; пересылка константы в регистр movlw CONST2 movwf DEST2 endm ; --- ОПИСАНИЕ РЕГИСТРОВ --- W_copy EQU 020h ; В этих регистрах будет ST_copy EQU 021h ; сохраняться контекст FSR_copy EQU 022h ADDRESS EQU 023h ; Хранение адреса ячейки, задающей состояние светодиодов SELECTOR EQU 024h ; Код выбора знакоместа (т.е зажигаемого разряда) TH EQU 025h ; Температура - старший байт TL EQU 026h ; Температура - младший байт CRCPIC EQU 027h ; Контрольная сумма, подсчитанная микроконтроллером TRY EQU 028h ; Попытки чтения по 1-Wire COUNTER EQU 029h ; Счетчик (используется при передаче данных по 1-Wire) FIGX000 EQU 02Ah ; Ячейки для хранения FIG000X EQU 02Bh ; разрядов FIG00X0 EQU 02Ch ; двоично-десятичных цифр, FIG0X00 EQU 02Dh ; выводимых на индикатор OUTA EQU 02Eh ; Хранение состояний защелок порта TEMP1 EQU 03Ch ;Ячейки для врЕменного TEMP2 EQU 03Dh ; хранения данных FLAGS EQU 03Fh ;Флаги пользователя ; --- ПУСК --- ORG 0 ;Вектор сброса Reset bcf STATUS,RP0 ;Обращение к банку 0 bcf STATUS,RP1 goto Begin ;Обходим обработчик прерываний и подпрограммы ;-------------------------------------------------------------------------------------------------- ; --- ОБРАБОТЧИК ПРЕРЫВАНИЙ --- ;-------------------------------------------------------------------------------------------------- ORG 4 ;Вектор прерываний Interrupt movwf W_copy ;Сохраняем контекст mov ST_copy,STATUS mov FSR_copy,FSR movlw B'00010000' ;Маска для сохранения состояния бита 4 andwf OUTA,F ;Обнуляем биты, управляющие светодиодами (PORTA) movf OUTA,W ;Гасим movwf PORTA ;индикатор movf SELECTOR,W ;Подготавливаем к зажиганию текущий iorwf OUTA,F ;разряд индикатора movf ADDRESS,W ;Адрес ячейки с требуемыми на данном шаге данными movwf FSR ;заносим в FSR (косвенная адресация) movf INDF,W ;Переносим данные из ячейки с указанным адресом в W movwf PORTB ;И выдаем через PORTB на сегменты индикатора movf OUTA,W ;Зажигаем текущий movwf PORTA ;разряд индикатора bcf STATUS,C rrf SELECTOR,F ;Далее ветвление, посчитаем шаги и уравняем пути (a,b) btfss STATUS,C ;Цифра была последней в этом цикле, пропускаем переход (1) goto Int_label ;Цифра еще не последняя, переход (2a) / nop(1b) movlw FIGX000 ;Снова заносим адрес ячейки (1b) movwf ADDRESS ;в ячейку ADDRESS - начался следующий цикл (1b) movlw B'00001000' ;Снова готовимся к (1b) movwf SELECTOR ;выводу начальной цифры (1b) goto End_int ;(2b) Путь по b: 1b+1b+1b+1b+1b+2b = 7 шагов Int_label incf ADDRESS,F ;В следующем прерывании будем выводить следующий байт (1a) nop nop goto End_int ;(2a) Путь по а: 2a+1a+1a+1a+2a = 7 шагов End_int bcf INTCON,T0IF ;Сбрасываем флаг запроса на прерывание от таймера TMR0 mov FSR,FSR_copy mov STATUS,ST_copy ;Восстанавливаем контекст swapf W_copy,F ;Тут приходится хитрить, чтобы сохранить бит Z в STATUS swapf W_copy,W ;(movf его может изменить, а swapf - нет) retfie ;Возврат из обработчика с установкой бита GIE ;-------------------------------------------------------------------------------------------------- ; --- ПОДПРОГРАММЫ --- ;-------------------------------------------------------------------------------------------------- ;Подпрограмма дешифрации для 7-сегм. индикатора DC7 clrwdt clrf PCLATH addwf PCL,F ;Табличная конвертация retlw B'00100100' ; 0 retlw B'11100111' ; 1 retlw B'01001100' ; 2 retlw B'01000101' ; 3 retlw B'10000111' ; 4 retlw B'00010101' ; 5 retlw B'00010100' ; 6 retlw B'01100111' ; 7 retlw B'00000100' ; 8 retlw B'00000101' ; 9 retlw B'00010101' ; точка ;Конец подпрограммы дешифрации для 7-сегм. индикатора ;-------------------------------------------------------------------------------------------------- ;Подпрограмма паузы (используется ячейка TEMP1) Pause clrwdt movwf TEMP1 ;Длительность паузы задается числом в W (примерно в мс) movf TEMP1,F ;Проверка на 0 btfsc STATUS,Z ;Если длительность не равна 0, то Z=0 и пропускаем return return ;Если же длительность равна 0, то сразу возврат clrw ;Очищаем W P_label addlw 01h ;Внутренний цикл по W jnz P_label decfsz TEMP1,F ;Внешний цикл по TEMP1 goto P_label ; clrwdt return ;Конец подпрограммы паузы ;-------------------------------------------------------------------------------------------------- ;Подпрограмма проверки подключения DS18B20 (Используется ячейка TEMP1) TestDS movlw 038h ; btfss PORTA,4 ;Термометр в состоянии ожидания? goto Test4_label ;Нет, активен - переход, выходим bcf OUTA,4 ;Сброс 1-Wire movf OUTA,W movwf PORTA ;Импульс сброса (не менее 480 мкс) movlw 050h Test1_label addlw 01h btfss STATUS,Z ;(за время этой паузы 3 раза происходит goto Test1_label ; прерывание от TMR0) bsf OUTA,4 movf OUTA,W movwf PORTA ;Сброс 1-Wire выполнен movlw D'239' Test2_label addlw 01h ;Ждем ответа от DS18B20 (пауза ~70 мкс) btfss STATUS,Z goto Test2_label clrf TEMP1 movlw D'252' Test3_label btfss PORTA,4 ;Есть отклик (0) ? incf TEMP1,F ;Да - инкремент TEMP1 addlw 01h ; btfss STATUS,Z goto Test3_label goto Testend_label ; Test4_label addlw 01h ;Пауза для выравнивания путей jnz Test4_label movlw 0 Testend_label movlw 075h addlw 01h ;Ожидание окончания отклика DS18B20 jnz Testend_label+1 movlw 04h ;Если отклик DS18B20 правильный, то TEMP1 = 4 xorwf TEMP1,W btfsc STATUS,Z ;TEMP1 = 4 ? movlw 0FFh ;Да - установить признак присутствия (W=0FFh) Stop_label return ;Конец подпрограммы проверки ;-------------------------------------------------------------------------------------------------- ;Подпрограмма ввода/вывода по шине 1-Wire (для приема задаем W=0FFh). Используется TEMP1 RW_1Wire movwf TEMP1 ;Исходные данные в W movlw 08h ;8 бит movwf COUNTER RWLoop bcf INTCON,GIE ;Запрет всех прерываний bcf OUTA,4 ;0 --> 1-Wire mov PORTA,OUTA btfsc TEMP1,0 bsf OUTA,4 ;Установить,если младший бит TEMP1 = 1 mov PORTA,OUTA rrf TEMP1,F ;Подготовить следующий бит movlw 0FDh ;Пауза ~12 мкс RW_1label addlw 01h jnz RW_1label bcf TEMP1,7 ;Принимаем в тот же TEMP1 btfsc PORTA,4 bsf TEMP1,7 . movlw D'220' ;Время на освобождение линии ведомым RW_2label addlw 01h jnz RW_2label bsf OUTA,4 ;Отпускаем шину mov PORTA,OUTA bsf INTCON,GIE ;Разрешить прерывания decfsz COUNTER,F ;8 бит обработаны? goto RWLoop ;Еще нет - переход movf TEMP1,W ;Принятый байт в W return ;Конец подпрограммы ввода/вывода по шине 1-Wire ;-------------------------------------------------------------------------------------------------- ;Подпрограмма обновления CRC. Параметр в W. Используются ячейки TEMP1, TEMP2 NewCRC clrwdt movwf TEMP2 ;Сохранить W movlw 08h movwf COUNTER ;8 --> COUNTER movf TEMP2,W ;Восстановить значение W CRC_label xorwf CRCPIC,W ;Исключ.ИЛИ CRCPIC и W, результат в W movwf TEMP1 ;Скопировать результат в TEMP1 rrf TEMP1,W ;Сдвиг TEMP1 вправо на 1, результат в W, младший бит в С movf CRCPIC,W ;CRCPIC --> W (бит С не изменился) btfsc STATUS,0 xorlw 018h ;Если С=0, то эту инструкцию не выполнять movwf TEMP1 ;Результат в TEMP1 rrf TEMP1,W ;Снова сдвиг, результат в W movwf CRCPIC ;Сохранить результат в CRCPIC bcf STATUS,0 ;0 --> C rrf TEMP2,F ;Сдвиг TEMP2 вправо на 1 movf TEMP2,W ;И скопировать полученное значение в W clrwdt decfsz COUNTER,F goto CRC_label return ;Конец подпрограммы обновления CRC ;__________________________________________________________________________________________________ ; ; --- НАЧАЛО ОСНОВНОЙ ПРОГРАММЫ --- ;__________________________________________________________________________________________________ Begin movlw 7 movwf CMCON ;Отключаем компараторы movf PORTA,F ;Приводим в соответствие movf PORTB,F ;защелки портов clrf INTCON mvi OUTA,B'00010000' ;Гасим все movwf PORTA mvi PORTB,B'11111111' ;светодиоды clrwdt clrf TMR0 bsf STATUS,RP0 ;Обращение к банку 1 для доступа к TRIS и OPTION_REG clrf TRISA ;Устанавливаем выводы порта А как 'OOOOOOOO' (I-in, O-out) clrf TRISB ;Устанавливаем выводы порта B как 'OOOOOOOO' mvi OPTION_REG,B'00000111' ;Подключаем сначала TMR0 через делитель=256 к OSC clrwdt mvi OPTION_REG,B'00001101' ;Переключаем предделитель 32 к WDT, TMR0 - таймер bcf STATUS,RP0 ;Обращение к банку 0 mvi ADDRESS,FIGX000 ;Заносим в ADDRESS адрес ячейки с начальной цифрой mvi SELECTOR,B'00001000' ;Готовимся к выводу начальной цифры mvi FIGX000,B'11111111' ;Отображаем "_OFF" mvi FIG0X00,B'00100100' mvi FIG00X0,B'00011110' mvi FIG000X,B'00011110' bsf INTCON,T0IE ;Прерывания только от TMR0 bsf INTCON,GIE ;Глобальное разрешение прерываний ;------------------------------------------------ Online_label clrwdt ;Работа с термометром DS18B20 clrf TMR0 call TestDS ;Сброс и проверка термометра xorlw 0FFh jnz Begin ;Ошибка - переход на начало программы movlw 0CCh ;Команда "Skip ROM" call RW_1Wire movlw 044h ;Команда "Convert T" call RW_1Wire movlw 0FFh ;Идет процесс преобразования call Pause movlw 0FFh call Pause movlw 0FFh call Pause mvi TRY,08h ;8 попыток чтения Newtry_label clrwdt ;Начинаем чтение данных из DS18B20 clrf TMR0 call TestDS ;Сброс и проверка термометра xorlw 0FFh jnz Begin ;Ошибка - переход на начало программы movlw 0CCh ;Команда "Skip ROM" call RW_1Wire movlw 0BEh ;Команда "Read Scratchpad" call RW_1Wire clrf CRCPIC movlw 0FFh call RW_1Wire movwf TL ;Младший байт температуры call NewCRC movlw 0FFh call RW_1Wire movwf TH ;Старший байт температуры call NewCRC movlw 0FFh ;Шесть ненужных байт call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire call NewCRC movlw 0FFh call RW_1Wire xorwf CRCPIC,W ;Проверка совпадения CRC jz OKCRC_label ;Совпадение - переход decfsz TRY,F goto Newtry_label ;Несовпадение - следующая попытка goto Begin ;Попытки исчерпаны OKCRC_label clrwdt ; need read on vдja kommenteeritud, et kood tццtaks DS18S20 anduriga ; ; rrf TH,F ; rrf TL,F ; rrf TH,F ; rrf TL,F ; rrf TH,F ; rrf TL,F ; 64 | 32 | 16 | 8 || 4 | 2 | 1 | 0.5 movlw 0FFh movwf FIGX000 ;Гасим все цифры movwf FIG0X00 movwf FIG00X0 movwf FIG000X ;------------------------------------------------ btfss TH,0 ;Температура отрицательная? goto T100_label ;Нет - переход на проверку "больше/меньше 100" mvi FIGX000,B'11011111' ;Да - отображаем "-" в старшем разряде comf TL,F ;Преобразование для правильного incf TL,F ;отображения отрицательных температур goto Tshow_label ;Переход ;------------------------------------------------ T100_label bcf STATUS,C rrf TL,W ; W = <0> | 64 | 32 | 16 || 8 | 4 | 2 | 1 addlw D'156' jnc Tshow_label ;Если температура меньше 100 градусов - переход movlw D'200' ; subwf TL,F ;Т не менее 100 градусов - сотню вычитаем mvi FIG0X00,B'11100111' ;Отобразить "1" во втором разряде ;------------------------------------------------ Tshow_label bcf STATUS,C rrf TL,W ;W = <0> | 64 | 32 | 16 || 8 | 4 | 2 | 1 movwf TEMP2 Divide bcf INTCON,GIE clrf FIG00X0 movf TEMP2,W ;Делим TEMP2 на 10 - получаем десятки и единицы градусов movwf TEMP1 ;Заносим делимое TL в ТЕМР1 через W bsf STATUS,C ;Установим признак переноса Div_label movf TEMP1,W movwf FIG000X ;Заносим значение из ТЕМР через W в остаток incf FIG00X0,F ;Каждый проход цикла увеличивает частное на 1 movlw D'10' ;10 -> W заносим делитель в W subwf TEMP1,F ;(TEMP1 - W) -> TEMP1 и вычитаем его из делимого jc Div_label ;Если ТЕМР1 еще не стало <0, то вычитаем снова и т.д. decf FIG00X0,F movlw B'11100111' xorwf FIG0X00,W ;Сотня отображается? jnz High_label ;Нет - переход movf FIG00X0,W ;Да - отображаем десятки и единицы градусов call DC7 movwf FIG00X0 movf FIG000X,W call DC7 movwf FIG000X goto Finish_label ;------------------------------------------------ High_label movf FIG00X0,W ;Отображаем десятки и единицы градусов call DC7 movwf FIG0X00 movf FIG000X,W call DC7 movwf FIG00X0 bcf FIG00X0,2 ;Отображаем точку mvi FIG000X,B'00100100' ;В младшем разряде по умолчанию "0" movlw B'00010101' btfsc TL,0 movwf FIG000X ;А если установлен бит "0,5 С" - то "5" Finish_label bsf INTCON,GIE goto Online_label end