Initial Problem
I'm trying to make a push-button incremented counter on two 7-segment displays ranging from 0-10.
- Ultimately I want Timer0 to handle the 7seg multiplexing and another timer for example Timer2 to handle button debouncing.
- The code works when I use only Timer0. The moment I try to configure any other timer than Timer0 the 7-segment display goes blank.
This code won't work:
#include <p18f45k22.inc> org 0x00 GOTO INIT counter EQU 0x20 tens EQU 0x21 compare EQU 0x25 compare1 EQU 0x26 compare2 EQU 0x27 compare3 EQU 0x28 change EQU 0x29 INIT MOVLW b'00110001' MOVWF T1CON (for some reason, when I comment two lines above here, the 7seg multiplexing part works - two zeroes show up on the 7seg display) MOVLW b'10000000' MOVWF T0CON CLRF TMR0L CLRF TMR0H BSF INTCON, TMR0IE BSF INTCON, PEIE BSF INTCON, GIE ;Inicjalizacja zmiennych BCF change, 0 ;Domylnie wywietlacz 0 (prawy) MOVLW 14 MOVWF compare ;Wpisujemy 14 (10) do zmiennej porwnawczej 0 MOVLW 0 MOVWF compare1 ;Wpisujemy 0 (0) do zmiennej porwnawczej 1 MOVLW 2 MOVWF compare2 ;Wpisujemy 2 (1) do zmiennej porwnawczej 2 MOVLW 13 MOVWF compare3 ;Wpisujemy 13 (9) do zmiennej porwnawczej 3 ;Domylnie 0 na jednociach i dziesitkach MOVLW 0 MOVWF counter MOVWF tens ;Czyszczenie latch'y portw CLRF LATA CLRF LATC CLRF LATD ;Ustawienie pinw 0 i 1 portu C na wejciowe BSF TRISC, 0 BSF TRISC, 1 ;Ustawienie portu A i D jako wyjciowe MOVLW 0x00 MOVWF TRISA MOVLW 0x00 MOVWF TRISD ;Ustawienie portw A,C i D na cyfrowe CLRF ANSELA CLRF ANSELC CLRF ANSELD MAIN BTFSC INTCON, 2 GOTO TIMINT0_ISR BTFSC PORTC, 0 ;Sprawdzenie nacinicia przycisku PC.0 (jeeli PC.0 HIGH to robi GOTO BUTTON_I) GOTO BUTTON_I BTFSC PORTC, 1 ;Sprawdzenie nacinicia przycisku PC.1 (jeeli PC.1 HIGH to robi GOTO BUTTON_D) GOTO BUTTON_D GOTO MAIN TIMINT0_ISR BCF INTCON, TMR0IF ; Clear Timer0 interrupt flag MOVF change, W ; Load current display state BTFSC WREG, 0 GOTO DISPLAY_TENS ; If change == 1, display tens GOTO DISPLAY_UNITS ; Otherwise, display units DISPLAY_UNITS MOVF counter, W ; Load units value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'8' ; Enable units display (digit 0) MOVWF PORTA BSF change, 0 ; Set change to 1 GOTO MAIN ; Return from interrupt DISPLAY_TENS MOVF tens, W ; Load tens value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'4' ; Enable tens display (digit 1) MOVWF PORTA BCF change, 0 ; Set change to 0 GOTO MAIN ; Return from interrupt BUTTON_I BSF T1CON, TMR1ON ; Enable Timer2 for debouncing BTFSC PIR1, TMR1IF ; Check if Timer2 has overflowed GOTO BUTTON_I ; If not, keep waiting (debouncing) BCF PIR1, TMR1IF ; Clear the Timer2 overflow flag BCF T1CON, TMR1ON MOVF tens, W ; Zaaduj aktualn warto dziesitek CPFSEQ compare2 ; Sprawd, czy warto dziesitek wynosi 2 (jeli tak, przejd do NORMAL) GOTO NORMAL ; Jeli dziesitki nie s rwne 2, przejd do normalnej obsugi MOVLW 0 ; Wczytaj 0 do rejestru W (wyzerowanie licznika jednoci) MOVWF counter ; Przypisz 0 do licznika jednoci MOVWF tens ; Przypisz 0 do licznika dziesitek GOTO MAIN ; Powrt do gwnej ptli programu NORMAL MOVF counter, W ; Zaaduj licznik jednoci CPFSEQ compare3 ; Porwnaj z wartoci 13 (maksymalna warto liczby) GOTO NEXT_INC ; Jeli liczba jest mniejsza ni 13, przejd do inkrementacji MOVLW 2 ; Jeli liczba przekroczya 13, ustaw dziesitki na 2 MOVWF tens ; Zapisz warto dziesitek MOVLW 0 ; Wyzeruj licznik jednoci MOVWF counter ; Zapisz 0 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NEXT_INC INCF counter, F ; Inkrementuj licznik jednoci GOTO MAIN ; Powrt do gwnej ptli programu BUTTON_D BSF T1CON, TMR1ON ; Enable Timer2 for debouncing BTFSC PIR1, TMR1IF ; Check if Timer2 has overflowed GOTO BUTTON_D ; If not, keep waiting (debouncing) BCF PIR1, TMR1IF ; Clear the Timer2 overflow flag BCF T1CON, TMR1ON MOVF tens, W ; Zaaduj aktualn warto dziesitek CPFSEQ compare2 ; Sprawd, czy warto dziesitek wynosi 2 (jeli tak, przejd do NORMAL2) GOTO NORMAL2 ; Jeli dziesitki s rwne 2, przejd do normalnej obsugi MOVLW 0 ; Wczytaj 0 do rejestru W (wyzerowanie licznika dziesitek) MOVWF tens ; Przypisz 0 do licznika dziesitek MOVLW 13 ; Ustaw licznik jednoci na 13 MOVWF counter ; Zapisz 13 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NORMAL2 MOVF counter, W ; Zaaduj licznik jednoci CPFSEQ compare1 ; Porwnaj z wartoci 0 GOTO NEXT_DEC ; Jeli licznik jednoci jest wikszy, przejd do dekrementacji MOVLW 2 ; Ustaw dziesitki na 2 MOVWF tens ; Zapisz warto dziesitek MOVLW 0 ; Wyzeruj licznik jednoci MOVWF counter ; Zapisz 0 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NEXT_DEC DECF counter, F ; Dekrementuj licznik jednoci GOTO MAIN ; Powrt do gwnej ptli programu Table ADDWF PCL, F ; Uyj wartoci w PCL (Program Counter Low) do adresowania tablicy dt 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F END However, if I use a simple incrementation based delay instead of a timer it works well:
#include <p18f45k22.inc> org 0x00 GOTO INIT org 0x08 GOTO TIMINT0_ISR counter EQU 0x20 ; Licznik jednoci (wywietlacz 7-segmentowy), przechowuje aktualn liczb jednoci tens EQU 0x21 ; Licznik dziesitek (wywietlacz 7-segmentowy), przechowuje aktualn liczb dziesitek delay_1 EQU 0x22 delay_2 EQU 0x23 delay_3 EQU 0x24 compare EQU 0x25 ; Zmienna porwnawcza (przechowuje warto 14, ktra jest uywana w porwnaniach) compare1 EQU 0x26 ; Zmienna porwnawcza (przechowuje warto 0, uywana w porwnaniach) compare2 EQU 0x27 ; Zmienna porwnawcza (przechowuje warto 2, uywana w porwnaniach) compare3 EQU 0x28 ; Zmienna porwnawcza (przechowuje warto 13, uywana w porwnaniach) change EQU 0x29 ; Zmienna sterujca, okrela, ktry wywietlacz (jednoci czy dziesitki) jest aktywny 1-aktywne dziesiatki, 0-aktywne jedności INIT MOVLW b'10000000' MOVWF T0CON CLRF TMR0L CLRF TMR0H BSF INTCON, TMR0IE BSF INTCON, PEIE BSF INTCON, GIE ;Inicjalizacja zmiennych BCF change, 0 ;Domylnie wywietlacz 0 (prawy) MOVLW 14 MOVWF compare ;Wpisujemy 14 (10) do zmiennej porwnawczej 0 MOVLW 0 MOVWF compare1 ;Wpisujemy 0 (0) do zmiennej porwnawczej 1 MOVLW 2 MOVWF compare2 ;Wpisujemy 2 (1) do zmiennej porwnawczej 2 MOVLW 13 MOVWF compare3 ;Wpisujemy 13 (9) do zmiennej porwnawczej 3 ;Domylnie 0 na jednociach i dziesitkach MOVLW 0 MOVWF counter MOVWF tens ;Czyszczenie latch'y portw CLRF LATA CLRF LATC CLRF LATD ;Ustawienie pinw 0 i 1 portu C na wejciowe BSF TRISC, 0 BSF TRISC, 1 ;Ustawienie portu A i D jako wyjciowe MOVLW 0x00 MOVWF TRISA MOVLW 0x00 MOVWF TRISD ;Ustawienie portw A,C i D na cyfrowe CLRF ANSELA CLRF ANSELC CLRF ANSELD MAIN BTFSC PORTC, 0 ;Sprawdzenie nacinicia przycisku PC.0 (jeeli PC.0 HIGH to robi GOTO BUTTON_I) GOTO BUTTON_I BTFSC PORTC, 1 ;Sprawdzenie nacinicia przycisku PC.1 (jeeli PC.1 HIGH to robi GOTO BUTTON_D) GOTO BUTTON_D GOTO MAIN TIMINT0_ISR BCF INTCON, TMR0IF ; Clear Timer0 interrupt flag MOVF change, W ; Load current display state BTFSC WREG, 0 GOTO DISPLAY_TENS ; If change == 1, display tens GOTO DISPLAY_UNITS ; Otherwise, display units DISPLAY_UNITS MOVF counter, W ; Load units value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'8' ; Enable units display (digit 0) MOVWF PORTA BSF change, 0 ; Set change to 1 RETFIE ; Return from interrupt DISPLAY_TENS MOVF tens, W ; Load tens value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'4' ; Enable tens display (digit 1) MOVWF PORTA BCF change, 0 ; Set change to 0 RETFIE ; Return from interrupt BUTTON_I CALL delay MOVF tens, W ; Zaaduj aktualn warto dziesitek CPFSEQ compare2 ; Sprawd, czy warto dziesitek wynosi 2 (jeli tak, przejd do NORMAL) GOTO NORMAL ; Jeli dziesitki nie s rwne 2, przejd do normalnej obsugi MOVLW 0 ; Wczytaj 0 do rejestru W (wyzerowanie licznika jednoci) MOVWF counter ; Przypisz 0 do licznika jednoci MOVWF tens ; Przypisz 0 do licznika dziesitek GOTO MAIN ; Powrt do gwnej ptli programu NORMAL MOVF counter, W ; Zaaduj licznik jednoci CPFSEQ compare3 ; Porwnaj z wartoci 13 (maksymalna warto liczby) GOTO NEXT_INC ; Jeli liczba jest mniejsza ni 13, przejd do inkrementacji MOVLW 2 ; Jeli liczba przekroczya 13, ustaw dziesitki na 2 MOVWF tens ; Zapisz warto dziesitek MOVLW 0 ; Wyzeruj licznik jednoci MOVWF counter ; Zapisz 0 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NEXT_INC INCF counter, F ; Inkrementuj licznik jednoci GOTO MAIN ; Powrt do gwnej ptli programu BUTTON_D CALL delay MOVF tens, W ; Zaaduj aktualn warto dziesitek CPFSEQ compare2 ; Sprawd, czy warto dziesitek wynosi 2 (jeli tak, przejd do NORMAL2) GOTO NORMAL2 ; Jeli dziesitki s rwne 2, przejd do normalnej obsugi MOVLW 0 ; Wczytaj 0 do rejestru W (wyzerowanie licznika dziesitek) MOVWF tens ; Przypisz 0 do licznika dziesitek MOVLW 13 ; Ustaw licznik jednoci na 13 MOVWF counter ; Zapisz 13 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NORMAL2 MOVF counter, W ; Zaaduj licznik jednoci CPFSEQ compare1 ; Porwnaj z wartoci 0 GOTO NEXT_DEC ; Jeli licznik jednoci jest wikszy, przejd do dekrementacji MOVLW 2 ; Ustaw dziesitki na 2 MOVWF tens ; Zapisz warto dziesitek MOVLW 0 ; Wyzeruj licznik jednoci MOVWF counter ; Zapisz 0 do licznika jednoci GOTO MAIN ; Powrt do gwnej ptli programu NEXT_DEC DECF counter, F ; Dekrementuj licznik jednoci GOTO MAIN ; Powrt do gwnej ptli programu Table ADDWF PCL, F ; Uyj wartoci w PCL (Program Counter Low) do adresowania tablicy dt 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F delay movlw d'2' ; Wczytaj warto 3 do rejestru W (czas opnienia) movwf delay_1 ; Zapisz do zmiennej delay_1 movwf delay_2 ; Zapisz do zmiennej delay_2 movwf delay_3 ; Zapisz do zmiennej delay_3 delay_loop decfsz delay_1, f ; Dekrementuj delay_1, jeli jest rne od 0, wr do delay_loop goto delay_loop ; Powtarzaj ptl, a delay_1 osignie 0 decfsz delay_2, f ; Dekrementuj delay_2 goto delay_loop ; Powtarzaj ptl, a delay_2 osignie 0 decfsz delay_3, f ; Dekrementuj delay_3 goto delay_loop ; Powtarzaj ptl, a delay_3 osignie 0 return END - Another question is why I must use magic numbers such as 14 instead of 10 and so on in order for comparisons to work properly - if I use 10 instead of 14, 7seg counting stops at 7?
I would be grateful for some advice.
New Approach
After considering your advice I have read the data sheet all over again and I have come back to the solution using high-priority interrupt for Timer0 and low-priority interrupt for the other timer. Also I have changed the Timer2 service routine to constantly blink diodes on PORTB. Here is the updated code:
#include <p18f45k22.inc> org 0x00 GOTO INIT org 0x08 GOTO TIMINT0_ISR ORG 0x18 GOTO TIMINT2_ISR counter EQU 0x20 ; Units counter (7-segment display), holds the current units value tens EQU 0x21 ; Tens counter (7-segment display), holds the current tens value delay_1 EQU 0x22 ; Delay variable 1 delay_2 EQU 0x23 ; Delay variable 2 delay_3 EQU 0x24 ; Delay variable 3 compare EQU 0x25 ; Comparison variable (stores the value 14, used in comparisons) compare1 EQU 0x26 ; Comparison variable (stores the value 0, used in comparisons) compare2 EQU 0x27 ; Comparison variable (stores the value 2, used in comparisons) compare3 EQU 0x28 ; Comparison variable (stores the value 13, used in comparisons) change EQU 0x29 ; Control variable, determines which display is active (1 - tens active, 0 - units active) INIT ;INTCON Config CLRF INTCON CLRF INTCON2 CLRF INTCON3 ;Enable High and Low priority interrupts BSF INTCON, GIEH BSF INTCON, GIEL BSF INTCON, TMR0IE BSF PIE1, TMR2IE ;Timer0 Config MOVLW b'10000000' MOVWF T0CON CLRF TMR0L CLRF TMR0H ;Timer2 Config MOVLW b'00000100' MOVWF T2CON BSF T2CON, TMR2ON BSF T2CON, T2CKPS1 BSF T2CON, T2CKPS0 MOVLW 0xFF MOVWF PR2 CLRF TMR2 ;Interrupt priorities BSF RCON, IPEN BSF INTCON2, TMR0IP BCF IPR1, TMR2IP ;Variable init BCF change, 0 ;Default digit display = 0 MOVLW 14 MOVWF compare ;We enter 14 (truly 10) into the comparison variable 0 MOVLW 0 MOVWF compare1 ;We enter 0 (truly 0) into the comparison variable 1 MOVLW 2 MOVWF compare2 ;We enter 2 (truly 1) into the comparison variable 2 MOVLW 13 MOVWF compare3 ;We enter 13 (truly 9) into the comparison variable 3 ;Default values for units and tens MOVLW 0 MOVWF counter MOVWF tens ;Port latch clearing CLRF LATA CLRF LATB CLRF LATC CLRF LATD ;Pins 0 and 1 of PORTC as input BSF TRISC, 0 BSF TRISC, 1 ;PORTS A, D and B as output MOVLW 0x00 MOVWF TRISA MOVLW 0x00 MOVWF TRISD MOVLW 0x00 MOVWF TRISB ;All ports as digital CLRF ANSELA CLRF ANSELC CLRF ANSELB CLRF ANSELD MAIN BTFSC PORTC, 0 ;Button PC.0 pooling GOTO BUTTON_I BTFSC PORTC, 1 ;Button PC.1 pooling GOTO BUTTON_D GOTO MAIN TIMINT0_ISR BTFSS INTCON, TMR0IF ; Check Timer interrupt flag RETFIE ; If flag not set, return BCF INTCON, TMR0IF ; Clear interrupt flag MOVF change, W ; Load current display state BTFSC WREG, 0 GOTO DISPLAY_TENS ; If change == 1, display tens GOTO DISPLAY_UNITS ; Otherwise, display units DISPLAY_UNITS MOVF counter, W ; Load units value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'8' ; Enable units display (digit 0) MOVWF PORTA BSF change, 0 ; Set change to 1 RETFIE ; Return from interrupt DISPLAY_TENS MOVF tens, W ; Load tens value CALL Table ; Get 7-segment code MOVWF PORTD ; Output to PORTD MOVLW d'4' ; Enable tens display (digit 1) MOVWF PORTA BCF change, 0 ; Set change to 0 RETFIE ; Return from interrupt TIMINT2_ISR BTFSS PIR1, TMR2IF ; Check Timer interrupt flag RETFIE ; If flag not set, return BCF PIR1, TMR2IF ; Clear interrupt flag MOVF PORTB, W ; Load current value of PORTB XORLW 0xFF ; Toggle all bits MOVWF PORTB ; Store the result back to PORTB RETFIE ; Return from interrupt BUTTON_I CALL delay MOVF tens, W ; Load the current value of tens CPFSEQ compare2 ; Check if the value of tens equals 2 (if yes, go to NORMAL) GOTO NORMAL ; If tens are not equal to 2, proceed with normal handling MOVLW 0 ; Load 0 into the W register (reset the units counter) MOVWF counter ; Assign 0 to the units counter MOVWF tens ; Assign 0 to the tens counter GOTO MAIN ; Return to the main program loop NORMAL MOVF counter, W ; Load the units counter CPFSEQ compare3 ; Compare with the value 13 (maximum number value) GOTO NEXT_INC ; If the number is less than 13, proceed to increment MOVLW 2 ; If the number exceeds 13, set tens to 2 MOVWF tens ; Save the value of tens MOVLW 0 ; Reset the units counter MOVWF counter ; Save 0 to the units counter GOTO MAIN ; Return to the main program loop NEXT_INC INCF counter, F ; Increment units GOTO MAIN ; Return to MAIN loop BUTTON_D CALL delay MOVF tens, W ; Load the current value of tens CPFSEQ compare2 ; Check if the value of tens equals 2 (if yes, go to NORMAL2) GOTO NORMAL2 ; If tens are equal to 2, proceed with normal handling MOVLW 0 ; Load 0 into the W register (reset the tens counter) MOVWF tens ; Assign 0 to the tens counter MOVLW 13 ; Set the units counter to 13 MOVWF counter ; Save 13 to the units counter GOTO MAIN ; Return to the main program loop NORMAL2 MOVF counter, W ; Load the units counter CPFSEQ compare1 ; Compare with the value 0 GOTO NEXT_DEC ; If the units counter is greater, proceed to decrement MOVLW 2 ; Set tens to 2 MOVWF tens ; Save the value of tens MOVLW 0 ; Reset the units counter MOVWF counter ; Save 0 to the units counter GOTO MAIN ; Return to the main program loop NEXT_DEC DECF counter, F ; Decrement units GOTO MAIN ; Return to MAIN loop Table ADDWF PCL, F ;Use PCL+position to address the table dt 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F delay movlw d'2' ; Load the value 2 into the W register (delay time) movwf delay_1 ; Save it to the variable delay_1 movwf delay_2 ; Save it to the variable delay_2 movwf delay_3 ; Save it to the variable delay_3 delay_loop decfsz delay_1, f ; Decrement delay_1, if it is not 0, return to delay_loop goto delay_loop ; Repeat the loop until delay_1 reaches 0 decfsz delay_2, f ; Decrement delay_2 goto delay_loop ; Repeat the loop until delay_2 reaches 0 decfsz delay_3, f ; Decrement delay_3 goto delay_loop ; Repeat the loop until delay_3 reaches 0 RETURN END The problem now is that:
- The multiplexing still doesn't work (blank display)
- The LEDS blink once on startup and then go blank forever