;******************************************
;* 2 в 1 СПИД-ОДО-МЕТР PIC 16F873 *
;******************************************
;=====================================================================================
; кварц 10 Мгц
; датчик 6 импульсов на метр (для Дмитрия)
list p=16F873
#include
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _LVP_OFF
; Константы
RCSwitch equ 1 ; RC1
RCPower equ 0 ; RC0
RCButton equ 3 ; RA3
RCPoint equ 4 ; RA4
; Переменные
; Номер активного сегмента
INDNO equ 0x20
; Буфер дисплея одометра, разряды в виде комбинаций сегментов
IND01 equ 0x21
IND02 equ 0x22
IND03 equ 0x23
IND04 equ 0x24
IND05 equ 0x25
IND06 equ 0x26
; Буфер дисплея спидометра
INDS1 equ 0x27
INDS2 equ 0x28
INDS3 equ 0x29
; основной счетчик полного пробега
tot_prev equ 0x2A
tor_k001 equ 0x2B
tot_k010 equ 0x2C
tot_k100 equ 0x2D
tot_001k equ 0x2E
tot_010k equ 0x2F
tot_100k equ 0x30
tot_001t equ 0x31
tot_010t equ 0x32
tot_100t equ 0x33
; дополнительный счетчик суточного пробега
aux_prev equ 0x34
aux_k001 equ 0x35
aux_k010 equ 0x36
aux_k100 equ 0x37
aux_001k equ 0x38
aux_010k equ 0x39
aux_100k equ 0x3A
IPM equ 0x3B
SCR_MODE equ 0x3C
BTN_MODE equ 0x3D
BTN_CNTR equ 0x3E
BTN_STAT equ 0x3F
cntr2 equ 0x40
iHCNT equ 0x41
iLCNT equ 0x42
cHCNT equ 0x43
cLCNT equ 0x44
PreDIG equ 0x45
EspDIG equ 0x46
DspDIG equ 0x47
HspDIG equ 0x48
; BTN_MODE - описывает, что произошло с кнопкой
; 00 - кнопка находится в верху
; 01 - нажатие (ход вниз)
; 11 - находится внизу
; 10 - отпускание (ход вверх)
; SCR_MODE - указывает процедуре FILL_VB, чем заполнять видеобуфер
; 0 - полный пробег
; 1 - суточный
; 2 - пусто
; 3 - слово СБРОС
; Программа
org 0x00
goto START
; перекодировка цифры в код 7-сегментного индикатора
SEG7 ; в счетчиках не само значение, а (10-значение)
sublw d'010' ; вычислили значение
andlw 0x0F ; на всякий случай
addwf PCL,f
; ebfdacg ноль в бите зажигает сегмент
retlw b'00000011' ; 0
retlw b'10111011' ; 1
retlw b'00100101' ; 2
retlw b'10100001' ; 3
retlw b'10011001' ; 4
retlw b'11000001' ; 5
retlw b'01000001' ; 6
retlw b'10110011' ; 7
retlw b'00000001' ; 8
retlw b'10000001' ; 9
retlw b'11110111' ; 10
retlw b'10111111' ; 11
retlw b'11111011' ; 12
retlw b'11101111' ; 13
retlw b'01111111' ; 14
retlw b'11011111' ; 15
; вычисление бита включения разряда по номеру активного анода
ANOD_C
andlw 0x07 ;на всякий случай
addwf PCL,f
; 123465 единица в бите зажигает разряд
retlw b'00000000' ; 0
retlw b'10000000' ; 1
retlw b'01000000' ; 2
retlw b'00100000' ; 3
retlw b'00010000' ; 4
retlw b'00000100' ; 5
retlw b'00001000' ; 6
retlw b'00000000' ; 7
; вычисление бита включения разряда по номеру активного анода
ANOD_A
andlw 0x03 ;на всякий случай
addwf PCL,f
; 321 единица в бите зажигает разряд 1-сотни 2-десятки 3-единицы
retlw b'00010000' ; 0
retlw b'00010001' ; 1
retlw b'00010010' ; 2
retlw b'00010100' ; 3
START
bcf STATUS, RP0 ; Банк 0
bcf STATUS, RP1
clrf T2CON
clrf TMR0
clrf TMR2
clrf INTCON
clrf PORTA
clrf PORTB
clrf PORTC
bsf STATUS, RP0 ; банк 1
movlw 0x06 ; все ноги А на цирфу
movwf ADCON1
movlw b'00001000'
movwf TRISA ; RA3 - кнопка, остальные вывод
movlw b'00000001'
movwf TRISB ; RB0 - датчик, остальные вывод
movlw b'00000001'
movwf TRISC ; RC0 - зажигание, остальные вывод
; включаем подтягивающие резисторы, TMR0 на внутренний такт с предделителем 16 (4 ms на цифру)
movlw b'01000011'
movwf OPTION_REG
; включаем TIMER2
clrf PIE1
movlw d'249' ; при кварце 10 МГц частота clock = Fosc/4 = 2,5 МГц 0,4 мкс
movwf PR2 ; регистр периода TIMER2 = 250 clock = 100 мкс на цикл
; цикл = 100 мкс * 1(PRE) * 1 (POST) = 100 мкс
bcf STATUS,RP0 ; банк 0
movlw b'00000100' ; none:7 (POST-1):6-3 On/Off:2 PRE: 1-0
movwf T2CON ; b0000 = 1:1 1=On b00 = 1
clrf PIR1
bsf PORTC, RCSwitch
call READ_EEPROM
movlw d'10'
movwf HspDIG
movwf DspDIG
movwf EspDIG
; ***
movlw 1 ; было 8
movwf PreDIG
movlw 9
movwf INDNO
clrf TMR0
bcf INTCON,T0IF ; обнулить таймер
call PROLOG
clrf BTN_MODE
clrf SCR_MODE
call FILL_VB
call FILL_SPD
; ***
movlw d'23' ; было d'37' для 30 имп/м
movwf iHCNT
; ***
movlw d'112' ; было d'128' для 30 имп/м
movwf iLCNT
clrf cHCNT
movlw 1
movwf cLCNT
MAIN_LOOP
; проверить питание
btfss PORTC,RCPower; обходим отключение, если 1
goto PowerDown
PowerRise
; проверить мерный таймер
btfsc PIR1,TMR2IF ; если 0 (нет сигнала от TIMER2), от обход
call DO_TICK ; а если 1, то идем увеличивать счетчики
; проверить датчик
btfsc INTCON,INTF ; если 0 (нет сигнала от датчика), от обход
call DO_INCR ; а если 1, то идем увеличивать счетчики
; продублировать состояние датчика
; btfsc PORTB,0
; bsf PORTA,5
; btfss PORTB,0
; bcf PORTA,5
; проверить таймер для динамической индикации
btfss INTCON,T0IF ; если таймер закончил отсчет, то идем менять разряд
goto MAIN_LOOP ; иначе повторяем проверки в цикле
; все, что ниже, выполняется 1 раз в 1,6 мс (на 10МГц)
call ChkButton ; обработка кнопи
call ChAnod ; переключение разрядов индикатора
bcf INTCON,T0IF ; обнулить таймер
goto MAIN_LOOP
; проверить кнопку и отреагировать
ChkButton
rlf BTN_MODE,f ; каждый раз BTN_MODE сдвигаем на 1 бит влево
bcf BTN_MODE,0 ; а в 0й бит заносим
btfss PORTA, RCButton ; 0, если кнопка вверху
bsf BTN_MODE,0 ; 1, если внизу
movf BTN_MODE,w ; затем вырезаем младшие два бита
andlw 3 ; и по ним выбираем вариант обработки
addwf PCL,f ;
goto Btn_UpUp ; 00 - кнопка была и остается наверху (постоянно отжата)
goto Btn_UpDn ; 01 - перешла сверху вниз (нажали)
goto Btn_DnUp ; 10 - перешла снизу вверх (отпустили)
goto Btn_DnDn ; 11 - была и остается внизу (постоянно прижата)
Btn_UpDn ; НАЖАЛИ
clrf BTN_CNTR ; в момент нажатия кнопки
clrf BTN_STAT ; обнуляем счетчики времени удержания
goto Btn_UpUp
Btn_DnDn ; УДЕРЖИВАЮТ продолжаем увеличивать счетчик
incfsz BTN_CNTR,f ; когда счетчик досчитает до 256 (0,4сек)
goto TestSTATUS
incf BTN_STAT,f ; изменим код реакции на подъем кнопки
TestSTATUS ; в зависимости от времени удержания
btfss BTN_STAT,2 ; 0, 1 , 2, 3 ( до 0,4 до 0,8 до 1,2 до 1,6 сек)
goto Btn_UpUp
decf BTN_STAT,f ; если код дошел до 4, то вернем его в 3
bsf SCR_MODE,1 ; изменим режим отображения данных на индикаторе
call FILL_VB ; теперь для ПОЛНОГО на экране НИЧЕГО
goto Btn_UpUp ; для СУТОЧНОГО на экране слово СБРОС
Btn_DnUp ; ОТПУСТИЛИ
btfss SCR_MODE,1 ; если был короткий тычок
goto SwitchMode ; то идем на переключение режима отображения
TestReset
bcf SCR_MODE,1 ; для длинного тычка
btfss SCR_MODE,0 ; вернули нормальный режим индикатора ПОЛНЫЙ/СУТОЧНЫЙ
goto ODOmode
movf IPM,w ; а для суточного кроме того
movwf aux_prev ; сделали сброс разрядов в 000.00
movlw d'10'
movwf aux_100k
movwf aux_010k
movwf aux_001k
movwf aux_k100
movwf aux_k010
movwf aux_k001
goto ODOmode
SwitchMode ; переключение режима индикатора ПОЛНЫЙ/СУТОЧНЫЙ
movlw 1
xorwf SCR_MODE,f
ODOmode ; восстановление буфера индикатора
call FILL_VB
Btn_UpUp ; если кнопка постоянно на верху, то ничего делать не надо
return
FILL_SPD ; переносим цифры скрости предыдущего замера в видеобуфер
movf HspDIG,w
call SEG7 ; вывод сотен
movwf INDS1 ;
movf DspDIG,w
call SEG7 ; вывод десятков
movwf INDS2
movf EspDIG,w ; вывод единиц всегда
call SEG7
movwf INDS3
call CLR_LEAD0 ; погасить незначащие нули
return
; заполнение видеобуфера кодами сегментов
FILL_VB
movfw SCR_MODE ; выбираем, чем заполнить буфер индикатора
andlw b'00000011' ; в зависимости от значения переменной SRC_MODE
addwf PCL,f
goto Fill_ODOM
goto Fill_TRIP
goto Fill_NONE
goto Fill_TEXT
Fill_ODOM ; заполняем буфер
movfw tot_100t ; цифрами полного пробега
call SEG7
movwf IND01
movfw tot_010t
call SEG7
movwf IND02
movfw tot_001t
call SEG7
movwf IND03
movfw tot_100k
call SEG7
movwf IND04
movfw tot_010k
call SEG7
movwf IND05
movfw tot_001k
call SEG7
movwf IND06
return
Fill_TRIP ; цифрами суточного пробега
movlw b'11111111' ; старший разряд темный
movwf IND01
movfw aux_100k
call SEG7
movwf IND02
movfw aux_010k
call SEG7
movwf IND03
movfw aux_001k
call SEG7
movwf IND04
movfw aux_k100
call SEG7
movwf IND05
movfw aux_k010
call SEG7
movwf IND06
bcf IND04,0 ; поставили признак "нужна точка" для второго разряда
return
Fill_TEXT ; слово СБРОС
; ebfdacg ноль зажигает
movlw b'11111111'
movwf IND01
movlw b'01000111' ; С
movwf IND02
movlw b'01000001' ; Б
movwf IND03
movlw b'00010101' ; Р
movwf IND04
movlw b'00000011' ; 0
movwf IND05
movlw b'01000111' ; С
movwf IND06
return
Fill_NONE ; погасили все разряды
movlw b'11111111' ; это при длительном удержании
movwf IND01 ; кнопки в режиме полного пробега
movwf IND02
movwf IND03
movwf IND04
movwf IND05
movwf IND06
return
ChAnod
;обслуживание индикации (каждый раз после отсчета таймером полного цикла 1,6мс х 6 разрядов = 100Гц)
movlw IND01-1
movwf FSR
movlw 0x03
andwf PORTC,f ; выключили разряд
movlw 0xF8
andwf PORTA,f
bsf PORTA, RCPoint
movlw 9 ; вычисление номера следующего разряда
decfsz INDNO,f ; циклически
movfw INDNO ; 9 8 7 6 5 4 3 2 1 и по кругу 9 8 7 6 5 ...
movwf INDNO
addwf FSR,f ; загружаем комбинацию катодов
movfw INDF ; для следующего разряда
movwf PORTB
movfw INDNO
sublw 6
btfss STATUS,C
goto ANOD_SPD
btfss INDF,0 ; если признак "нужна точка" сброшен, то обход включения
bcf PORTA, RCPoint ; включили точку
movfw INDNO
call ANOD_C
iorwf PORTC,f ; включаем анод следующего разряда
return
ANOD_SPD
call ANOD_A
iorwf PORTA,f ; включаем анод следующего разряда
return
; пришел тик от мерного таймера (прошло 100 мкс)
DO_TICK
bcf PIR1,TMR2IF ; сбросить флаг прерывания
decfsz cLCNT,f
return
; закончился очередной маленький счетчик
movf cHCNT,f ; проверяем большой счетчик
btfsc STATUS,Z ; если cHCNT=0
goto ResetCNTS ; то идет инициализируем счетчики на новый отсчет
movf iLCNT,w
decfsz cHCNT,f ; cHCNT=cHCNT-1
movlw 0 ; если <>0, то cLCNT=256
movwf cLCNT ; =0, =iLCNT
return
ResetCNTS
movf iHCNT,w
movwf cHCNT ; cHCNT=iHCNT
clrf cLCNT ; cLCNT=256
call FILL_SPD
; call FILL_VB
; обнуление для нового замера
movlw d'10'
movwf HspDIG
movwf DspDIG
movwf EspDIG
; ***
movlw 1 ; было 8
movwf PreDIG
return
; Приращение счетчиков
DO_INCR ; 123 456 789 10
movlw d'10' ; 10 разрядов для полного пробега 999 999 км (видны). 999 м +IPM (не видны)
movwf cntr2
movlw tot_prev-1
call CH_CNTR ; 123 45 6 7
movlw 7 ; 7 разрядов для суточного 999 км .99 (видны) 9м + IPM (не видны)
movwf cntr2
movlw aux_prev-1
call CH_CNTR
movlw 4 ; сначала 3 разряда спидометра
movwf cntr2
movlw PreDIG-1
movwf FSR
; ***
movlw 1 ; было 8
call CH_LOOP
call FILL_VB ; обновили коды катодов в буфере индикатора
; call FILL_SPD
bcf INTCON,INTF ; сбросили флаг внешнего прерывания
return
CH_CNTR
movwf FSR
movf IPM,w ; для предварительного счетчика метров IPM (impacts per meter)
CH_LOOP
incf FSR,f
decfsz INDF,f
return
movwf INDF
movlw d'10' ; для всех остальных разрядов счетчика
decfsz cntr2,f
goto CH_LOOP
return
PowerDown
call WRITE_EEPROM ; записываем счетчики в энергонезависимую память
; слово ЗАПИСЬ в буфер индикатора
; ebfdacg ноль в бите зажигает сегмент
movlw b'10100001' ; 3
movwf IND01
movlw b'00010001' ; A
movwf IND02
movlw b'00010011' ; П
movwf IND03
movlw b'00001011' ; И
movwf IND04
movlw b'01000111' ; С
movwf IND05
movlw b'01001001' ; Ь
movwf IND06
SDeadLoop
movlw D'255'
movwf cntr2
bcf INTCON,T0IF ; обнулить таймер
DeadLoop
btfss INTCON,T0IF
goto DeadLoop
call ChAnod
decfsz cntr2,f
goto DeadLoop-1
; goto SDeadLoop
btfsc PORTC,RCPower ; если питание не восстановилось (0), то обход возврата
goto PWRRestoted ; если восстановилось (1), то возвращаемся
; все отключаем питание
bcf PORTC,RCSwitch
goto SDeadLoop
PWRRestoted
call FILL_VB
bsf PORTC,RCSwitch ; придется опять включить
goto PowerRise
READ_EEPROM ; загрузка счетчиков из энергонезависимой памяти
movlw d'18'
movwf cntr2
movlw tot_prev
movwf FSR
RD_LOOP
movfw FSR
addlw -tot_prev
bsf STATUS,RP1 ; банк 2 - на входе банк 0
movwf EEADR
bsf STATUS,RP0 ; банк 3
bcf EECON1,EEPGD
bsf EECON1,RD
bcf STATUS,RP0 ; банк 2
movfw EEDATA
bcf STATUS,RP1 ; банк 0
movwf INDF
incf FSR,f
decfsz cntr2,f
goto RD_LOOP
return
WRITE_EEPROM ; сохранение счетчиков в энергонезависимой памяти
movlw d'17'
movwf cntr2 ; cntr2 = 15
movlw tot_prev
movwf FSR ; FSR = @tot_prev = адреса регистров RAM
WR_LOOP
movfw FSR
addlw -tot_prev ; W = 0, 1, ... , 14 = адреса регистров EEPROM
; Процедура записи байта в EEPROM
bsf STATUS,RP1 ; банк 2
movwf EEADR ; Переносим W в EEADR
bcf STATUS,RP1 ; банк 0
movfw INDF ; Заносим в W содержимое регистра RAM
bsf STATUS,RP1 ; банк 2
movwf EEDATA ; Из W в EEDATA
bsf STATUS,RP0 ; банк 3
bcf EECON1,EEPGD; выбираем EEPROM
bsf EECON1,WREN ; Разрешаем запись
movlw h'55' ; ** Обязательная **
movwf EECON2 ; ** процедура **
movlw h'AA' ; ** без **
movwf EECON2 ; ** комментариев **
bsf EECON1,WR ; Команда начала записи
btfsc EECON1,WR ; цикл ожидания завершения записи
goto $-1 ; повторяем
bcf EECON1,WREN
bcf STATUS,RP1
bcf STATUS,RP0 ; БАНК 0
incf FSR,f ; FSR = @tot_prev +1, +2, ..., + 15
decfsz cntr2,f
goto WR_LOOP ; повторяем 17 раз
return
CLR_LEAD0
movf HspDIG,w
sublw d'10'
btfss STATUS,Z
return
movlw b'11111111'
movwf INDS1
movf DspDIG,w
sublw d'10'
btfss STATUS,Z
return
movlw b'11111111'
movwf INDS2
return
PROLOG
clrf IND01
clrf IND02
clrf IND03
clrf IND04
clrf IND05
clrf IND06
clrf INDS1
clrf INDS2
clrf INDS3
clrf cLCNT
movlw 3
movwf cHCNT
PRO_LOOP
btfss INTCON,T0IF
goto PRO_LOOP
call ChAnod
bcf INTCON,T0IF ; обнулить таймер
decfsz cLCNT,f
goto PRO_LOOP
decfsz cHCNT,f
goto PRO_LOOP
return
org 0x2100
; цифры по умолчанию ( МЕСТАМИ НЕ МЕНЯТЬ! ПОРЯДОК ВАЖЕН!)
; в EEPROM они хранятся в виде 10-х
; полный пробег
; ***
eet_prev de d'06' ; предварительные неотображаемые
eet_k001 de d'10'-0
eet_k010 de d'10'-0
eet_k100 de d'10'-0 ; отображаемые разряды ниже
eet_001k de d'10'-0 ; 1 км
eet_010k de d'10'-0
eet_100k de d'10'-0
eet_001t de d'10'-5
eet_010t de d'10'-9
eet_100t de d'10'-0 ; 100 000 км
;суточный пробег
; ***
eea_prev de d'06' ; предварительные неотображаемые
eea_k001 de d'10'-0
eea_k010 de d'10'-0 ; 10 м
eea_k100 de d'10'-0
eea_001k de d'10'-0 ; 1 км
eea_010k de d'10'-0
eea_100k de d'10'-0 ; 100 км
; число импульсов на 1 метр
; ***
eeIPM de d'06'
end ;конец программы