;Программа для спидометра-одометра ; кварц 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 ; в счетчиках не само значение, а десятичное sublw d'010' ; вычислили значение andlw 0x0F ; на всякий случай addwf PCL,f ; ebfdacgd, ноль в бите зажигает сегмент 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 ; 3210 единица в бите зажигает разряд 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 Chatod 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'-0 ; ** пробег ** eet_010t de d'10'-0 ; ** в данном случае 300000 ** 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 ;конец программы