3
\$\begingroup\$

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
\$\endgroup\$
8
  • 1
    \$\begingroup\$ I can't test your code, but try changing "GOTO MAIN" to "RETFIE" in routines called during TIMER0 interrupt (DISPLAY_UNITS and DISPLAY_TENS). \$\endgroup\$ Commented Jan 15 at 22:37
  • \$\begingroup\$ Sadly it did not help, same issue - nothing showing up on 7seg. I use a PICSimLab simulator, and looking into pinviewer tells me that PORTA and PORTA are all '0', and nothing happens after pressing PC0/1 \$\endgroup\$ Commented Jan 15 at 22:46
  • \$\begingroup\$ RCON.IPEN - Clearing IPEN (no priority levels) and setting GIEH and GIEL to 1 seems to be of no help. I have tried using TIMER0 as a high priority interrupt at 0x08, and TIMER1 as a low priority interrupt at 0x18, but same issue persisted. It's my first time using PIC architecture, and I haven't figured out two interrupts at the same time yet. \$\endgroup\$ Commented Jan 15 at 23:34
  • \$\begingroup\$ The idea of using a one, smaller timer multiplied if needed is something I figured out before as well, however wouldn't it make the multiplexing visibly flawed? The issue is that TIMER0 must always run in order to make the multiplexing work, however, the timer for button debouncing would only be started after pressing the button. \$\endgroup\$ Commented Jan 15 at 23:47
  • 1
    \$\begingroup\$ @periblepsis Yup, it's MPASM. Kinda old I know but have to use it. \$\endgroup\$ Commented Jan 16 at 22:58

3 Answers 3

1
\$\begingroup\$

I didn't want to write any of your polling or display code. I just wanted to find out if I could set up the two timers and have them run in the MPLAB X simulator and hopefully produce results I expected to see. I believe I got that far. So let me show you what I wrote:

 PROCESSOR PIC18F45K22 RADIX DEC #include <p18f45k22.inc> CONFIG WDTEN = OFF, BOREN = OFF, PWRTEN = ON CONFIG FOSC = HSMP, STVREN = ON, MCLRE = EXTMCLR ; -- -- -- -- -- -- -- -- -- PORSTARTUP CODE 0x0000 GOTO PORBEGIN HIGHPRIORITY CODE 0x0008 GOTO HIGHINTEVENT LOWPRIORITY CODE 0x0018 GOTO LOWINTEVENT ; -- -- -- -- -- -- -- -- -- CODE HIGHINTEVENT MOVLW #high -(50000-2) MOVWF TMR0H MOVLW #low -(50000-2) MOVWF TMR0L BCF INTCON, TMR0IF INCF 0 RETFIE FAST LOWINTEVENT MOVFF STATUS, 2 BCF PIR1, TMR2IF INCF 1 MOVFF 2, STATUS RETFIE PORBEGIN CLRWDT CLRF INTCON CLRF INTCON2 CLRF INTCON3 CLRF STKPTR MOVLW #B'10011111' MOVWF RCON BSF INTCON2, TMR0IP MOVLW #B'00000011' MOVWF T0CON MOVLW #high -50000 MOVWF TMR0H MOVLW #low -50000 MOVWF TMR0L BSF INTCON, TMR0IE BSF INTCON, GIEH BSF T0CON, TMR0ON BCF IPR1, TMR2IP MOVLW #B'00000011' MOVWF T2CON MOVLW #H'F0' MOVWF PR2 CLRF TMR2 BSF PIE1, TMR2IE BSF INTCON, GIEL BSF T2CON, TMR2ON CLRF 0 CLRF 1 HERE MOVFF 0, WREG MOVFF 1, WREG NOP GOTO HERE END 

Just by way of some explanation, I reserved GPR 0's access memory addresses 0 and 1 for two 8-bit counters.

Address 0 was incremented every time the high priority interrupt occurred (it was slower, since I used a 16-bit counter for Timer 0, which is the timer I selected for the high priority interrupt.)

Address 1 was incremented every time the low priority interrupt occurred (it was faster, since it is just an 8-bit counter for Timer 2, which is the timer I selected for the low priority interrupt.)

I initialized those values to 0 at the start, of course.

I also used address 2 as temporary storage of the STATUS in the low-priority interrupt, since it cannot depend upon the fast memory save. (Only the high-priority routine can bank on it working right.)

Then all I did was to run the simulator. An easy test was to tell it to run until it hit a break-point in the high priority interrupt. By then, many of the low priority interrupts should already have occurred. Once that break-point was reached (and it was, with a small delay on my computer), I then set a break-point on the instruction at HERE. When it arrived there, I just used F8 to single-step through the two reads of address 0 and 1 into WREG. What I saw was what I expected to see, which is the value "1" after the first instruction and a large value after the second instruction. And that is exactly what I did see happen.

I also made sure that both interrupts were occurring. I can use F4 while hovering over an instruction and it will break there. I could verify that both interrupt routines were executing periodically. I also made sure that the few instructions around HERE were returned to after these interrupts. And it was, as expected.

I didn't notice anything awry. It all just worked as I hoped it would and I didn't notice anything out of place from what I expected.

\$\endgroup\$
4
\$\begingroup\$

First version

Your not-working source has at least two apparent errors:

  1. You do not use the interrupt vectors at 0008h and 0018h.
  2. You return from the interrupt service routines by goto main instead of retfie.

There might be more, but these are reason enough.


Second version

Because I don't have the hardware at hand, the following statements are more or less guesses.

It seems as if your timer fire quite frequently. You might want to calculate the rate and add it to your question.

Anyway, your interrupt service routines use register w but don't save and restore its value on entry and exit, respectively. This way each running ISR destroys the value of the interrupted control flow. This certainly leads to all kind of misbehavior, as you observe.

\$\endgroup\$
1
  • \$\begingroup\$ I've changed my approach and edited the question. I have also applied your suggestions. Would be grateful for more help. \$\endgroup\$ Commented Jan 16 at 20:47
0
\$\begingroup\$

There is too much wrong with the code the Original Poster has provided so I created an example to show one way to multiplex a two digits LED display, show a decimal count and debounce two button inputs.

As the OP is compelled to use the obsolete MPASM assembler they should use it in a mode best for debug. That is the RELOCATABLE mode.

Here is an example that does that:

 list P=18F45K22, r=dec, n=0 , c=128 ; ; Filename: main.asm ; Date: 2025-JAN-22 ; Author: dan1138 ; Compiler: MPASM v5.51 ; IDE: MPLAB v8.92 ; ; Files required: P18F45K22.INC ; 18F45K22_G.LKR generic linker script ; ; Description: ; ; This is an example for a StackExchange question: ; https://electronics.stackexchange.com/questions/736089/using-multiple-timers-pic18f45k22 ; ; PIC18F45K22 ; +-------------:_:-------------+ ; ICD_VPP RE3 -> : 1 VPP PGD 40 : <> RB7 ICD_PGD ; LED_dig_0 RA0 <> : 2 AN0 PGC 39 : <> RB6 ICD_PGC ; LED_dig_1 RA1 <> : 3 AN1 T3CKI/CCP3/AN13 38 : <> RB5 ; RA2 <> : 4 AN2 AN11 37 : <> RB4 ; RA3 <> : 5 AN3 AN9 36 : <> RB3 ; RA4 <> : 6 INT2/AN8 35 : <> RB2 ; RA5 <> : 7 AN4 INT1/AN10 34 : <> RB1 ; RE0 <- : 8 AN5 INT0/AN12 33 : <> RB0 ; RE1 <> : 9 AN6 32 : <- VDD 5v0 ; RE2 <> : 10 AN7 31 : <- VSS GND ; 5v0 VDD -> : 11 AN27 30 : <> RD7 LED_seg_dp ; GND VSS -> : 12 AN26 29 : <> RD6 LED_seg_g ; RA7 -> : 13 OSC1 AN25 28 : <> RD5 LED_seg_f ; RA6 <- : 14 OSC2 AN24 27 : <> RD4 LED_seg_e ; BUTTON_I RC0 <> : 15 SOSCO AN19 26 : <> RC7 ; BUTTON_D RC1 <> : 16 SOSCI AN18 25 : <> RC6 ; RC2 <> : 17 AN14/CCP1 AN17 24 : <> RC5 ; RC3 <> : 18 AN15 AN16 23 : <> RC4 ; LED_seg_a RD0 <> : 19 AN20 AN23 22 : <> RD3 LED_seg_d ; LED_seg_b RD1 <> : 20 AN21 AN22 21 : <> RD2 LED_seg_c ; +-----------------------------+ ; DIP-40 ; #include <P18F45K22.INC> ; setup to use the 4xPLL with a 16MHz internal oscillator for a 64MHz system clock config FOSC = INTIO67 ; Internal oscillator block, port function on RA6 and RA7 config PLLCFG = ON ; Oscillator multiplied by 4 config PRICLKEN = ON ; Primary clock is always enabled config HFOFST = OFF ; HFINTOSC delayed by the oscillator stable status config FCMEN = OFF ; Fail-Safe Clock Monitor disabled config IESO = ON ; Oscillator Switchover mode enabled config PWRTEN = ON ; Power up timer enabled config BOREN = OFF ; Brown-out Reset disabled in hardware and software config BORV = 190 ; VBOR set to 1.90 V nominal ; config WDTEN = OFF ; Watch dog timer is always disabled. SWDTEN has no effect. config WDTPS = 32768 ; 1:32768 config CCP2MX = PORTC1 ; CCP2 input/output is multiplexed with RC1 config CCP3MX = PORTB5 ; P3A/CCP3 input/output is multiplexed with RB5 config PBADEN = OFF ; PORTB<5:0> pins are configured as digital I/O on Reset config T3CMX = PORTC0 ; T3CKI is on RC0 config P2BMX = PORTC0 ; P2B is on RC0 config LVP = ON ; Single-Supply ICSP enabled config STVREN = OFF ; Stack full/underflow will not cause Reset config MCLRE = EXTMCLR ; MCLR pin enabled; RE3 input pin disabled config XINST = OFF ; Instruction set extension and Indexed Addressing mode disabled ; Turn off all code protection config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF, CPB = OFF, CPD = OFF config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF, WRTB = OFF, WRTC = OFF config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF, EBTRB = OFF ; Specify the System clock frequency in Hz #define FOSC 64000000 ; Specify the Peripheral clock frequency in Hz #define FCYC (FOSC/4) ; Put ISR context save storage in the ACCESS bank INT_VAR UDATA_ACS W_TEMP: RES 1 ; w register for context saving (ACCESS) STATUS_TEMP: RES 1 ; status used for context saving BSR_TEMP: RES 1 ; bank select used for ISR context saving LED_digit_0_segments: res 1 LED_digit_1_segments: res 1 Button_sample: res 1 Button_change: res 1 Button_stable: res 1 Button_bounce: res 1 #define BUTTON_PORT PORTC #define BUTTON_BOUNCE_TICKS 20 #define BUTTON_I_POSITION 0 #define BUTTON_D_POSITION 1 #define BUTTON_MASK ((1 << BUTTON_I_POSITION) | (1 << BUTTON_D_POSITION)) TIMER2_ticks: res 1 TIMER2_flags: res 1 #define TIMER2_EVENTS_IN_ONE_SECOND 250 #define TIMER2_CLOCKS_IN_ONE_PERIOD 250 #define TIMER2_ONE_SECOND_FLAG_POSITION 0 ;------------------------------------------------------------------------------ ; RESET VECTOR ;------------------------------------------------------------------------------ POR CODE 0x0000 ; processor reset vector clrf TBLPTRU, ACCESS ; First silicon does not do this at POR GOTO Main ; ;------------------------------------------------------------------------------ ; HIGH PRIORITY INTERRUPT VECTOR ;------------------------------------------------------------------------------ ISRHV CODE 0x0008 ; Run the High Priority Interrupt Service Routine GOTO HIGH_ISR ;------------------------------------------------------------------------------ ; LOW PRIORITY INTERRUPT VECTOR ;------------------------------------------------------------------------------ ISRLV CODE 0x0018 ; Run the High Priority Interrupt Service Routine GOTO LOW_ISR ;------------------------------------------------------------------------------ ; HIGH PRIORITY INTERRUPT SERVICE ROUTINE ;------------------------------------------------------------------------------ ISRH CODE ; let linker place high ISR routine HIGH_ISR: ; Insert High Priority ISR Here btfsc INTCON,TMR0IE btfss INTCON,TMR0IF bra TIMER0_exit bcf INTCON,TMR0IF clrf LATD btfss LATA,0 goto Show_digit_0_segments Show_digit_1_segments: movf LED_digit_1_segments,W bcf LATA,0 bsf LATA,1 movwf LATD goto Poll_inputs Show_digit_0_segments: movf LED_digit_0_segments,W bsf LATA,0 bcf LATA,1 movwf LATD goto Poll_inputs Poll_inputs: movf Button_sample,W xorwf BUTTON_PORT,W andlw BUTTON_MASK bz Button_NoChange xorwf Button_sample,F movlw BUTTON_BOUNCE_TICKS movwf Button_bounce bra Poll_exit Button_NoChange: movf Button_bounce,F bz Button_Stable decfsz Button_bounce,F bra Poll_exit Button_Stable: movf Button_sample,W xorwf Button_stable,W andlw BUTTON_MASK iorwf Button_change,F xorwf Button_stable,F Poll_exit: TIMER0_exit: retfie FAST ;------------------------------------------------------------------------------ ; LOW PRIORITY INTERRUPT SERVICE ROUTINE ;------------------------------------------------------------------------------ ISRL CODE ; let linker place low ISR routine LOW_ISR: ; Context Saving for Low ISR MOVFF STATUS, STATUS_TEMP ; save status register MOVWF W_TEMP ; save W register MOVFF BSR, BSR_TEMP ; save bankselect register ; Insert Low Priority ISR Here btfsc PIE1,TMR2IE btfss PIR1,TMR2IF bra TIMER2_exit bcf PIR1,TMR2IF movf TIMER2_ticks bz TIMER2_exit decfsz TIMER2_ticks,F bra TIMER2_exit movlw TIMER2_EVENTS_IN_ONE_SECOND movwf TIMER2_ticks btfss TIMER2_flags,TIMER2_ONE_SECOND_FLAG_POSITION bsf TIMER2_flags,TIMER2_ONE_SECOND_FLAG_POSITION TIMER2_exit: ; Restore Context for Low ISR MOVFF BSR_TEMP, BSR ; restore bank select register MOVF W_TEMP, W ; restore W register MOVFF STATUS_TEMP, STATUS ; restore status register RETFIE ;------------------------------------------------------------------------------ ; MAIN PROGRAM ;------------------------------------------------------------------------------ ; This is a lame sample application to multiplex two 7-segment LED digits ; and debounce two button inputs. ; ;****************************************************************************** ; Data space MAIN_UDATA UDATA LED_count: res 1 ;****************************************************************************** ; Start of main program ; MAIN_CODE CODE ; let linker place main program Main: bcf INTCON,GIEH ; Disable all interrupts bsf RCON,IPEN ; Enable priority levels on interrupts movlw 0x70 ; Set primary oscillator as system clock source movwf OSCCON,ACCESS ; Use 16MHZ internal oscillator movlw 0x04 ; movwf OSCCON2,ACCESS ; movlw 0x40 ; movwf OSCTUNE,ACCESS ; Turn on 4x PLL MOVLW 0x00 ; Configure A/D MOVWF ANSELA ; for digital inputs MOVWF ANSELB ; for digital inputs MOVWF ANSELC ; for digital inputs MOVWF ANSELD ; for digital inputs MOVWF ANSELE ; for digital inputs CLRF LATA ; Initialize PORTA by clearing output data latches MOVLW 0x00 ; Value used to initialize data direction MOVWF TRISA ; Set RA<7:0> as outputs CLRF WPUB ; Turn off weak pull ups on PORTB CLRF IOCB ; Turn off interrupt on change of PORTB(7:4> CLRF LATB ; Initialize PORTB by clearing output data latches MOVLW 0x00 ; Value used to initialize data direction MOVWF TRISB ; Set RB<7:0> as outputs CLRF LATC ; Initialize PORTC by clearing output data latches MOVLW 0x03 ; Value used to initialize data direction MOVWF TRISC ; Set RC<7:2> as outputs RC<1:0> as inputs CLRF LATD ; Initialize PORTD by clearing output data latches MOVLW 0x00 ; Value used to initialize data direction MOVWF TRISD ; Set RD<7:0> as outputs CLRF LATE ; Initialize PORTE by clearing output data latches MOVLW 0x00 ; Value used to initialize data direction MOVWF TRISE ; Set RE<2:0> as outputs clrf LED_digit_0_segments clrf LED_digit_1_segments ; ; Setup TIMER0 to assert an interrupt evert 1.024 milliscronds with 64MHz Fosc. clrf TMR0L movlw 0xC5 movwf T0CON bcf INTCON,TMR0IF bsf INTCON,TMR0IE ; ; Setup TIMER2 to assert 250 interrupts per scronds with 64MHz Fosc. bcf PIE1,TMR2IE bcf PIR1,TMR2IF bcf IPR1,TMR2IP clrf T2CON movlw 0x7B movwf T2CON movlw TIMER2_CLOCKS_IN_ONE_PERIOD-1 movwf PR2 bcf PIR1,TMR2IF bsf PIE1,TMR2IE movlw TIMER2_EVENTS_IN_ONE_SECOND movwf TIMER2_ticks bsf T2CON,TMR2ON bsf INTCON,GIEL bsf INTCON,GIEH banksel LED_count clrf LED_count ; ; Application process loop AppLoop: TimeEvent: btfss TIMER2_flags,TIMER2_ONE_SECOND_FLAG_POSITION bra TimeEvent_Exit bcf TIMER2_flags,TIMER2_ONE_SECOND_FLAG_POSITION movlw 1 banksel LED_count addwf LED_count,W daw movwf LED_count call Hex2Segments xorwf LED_digit_0_segments,W andlw 0x7F xorwf LED_digit_0_segments,F swapf LED_count,W call Hex2Segments xorwf LED_digit_1_segments,W andlw 0x7F xorwf LED_digit_1_segments,F TimeEvent_Exit: ButtonEvent: btfss Button_change,BUTTON_I_POSITION bra BUTTON_I_Exit bcf Button_change,BUTTON_I_POSITION BUTTON_I_IsItOpen: btfsc Button_stable,BUTTON_I_POSITION bra BUTTON_I_IsItClosed ; BUTTON_I changed to open nop bra BUTTON_I_Exit BUTTON_I_IsItClosed: btfss Button_stable,BUTTON_I_POSITION bra BUTTON_I_Exit ; BUTTON_I changed to closed nop BUTTON_I_Exit: btfss Button_change,BUTTON_D_POSITION bra BUTTON_D_Exit bcf Button_change,BUTTON_D_POSITION BUTTON_D_IsItOpen: btfsc Button_stable,BUTTON_D_POSITION bra BUTTON_D_IsItClosed ; BUTTON_D changed to open nop bra BUTTON_D_Exit BUTTON_D_IsItClosed: btfss Button_stable,BUTTON_D_POSITION bra BUTTON_D_Exit ; BUTTON_D changed to closed nop BUTTON_D_Exit: goto AppLoop ; ; Function: Hex2Segments ; ; Input: WREG = LED segment table lookup index ; Range: 0x00 to 0x0F ; ; Output WREG = 7 low bits are LED segments gfedcba ; Hex2Segments: andlw 0x0F mullw 2 movlw LOW(SegTable) addwf PRODL,F movlw HIGH(SegTable) addwfc PRODH,W movwf PCLATH movf PRODL,W movwf PCL ; SegTable: ; .gfedcba ; _ RETLW B'00111111' ; | | ; |_| ; RETLW B'00000110' ; | ; | ; _ RETLW B'01011011' ; _| ; |_ ; _ RETLW B'01001111' ; _| ; _| ; RETLW B'01100110' ; |_| ; | ; _ RETLW B'01101101' ; |_ ; _| ; _ RETLW B'01111101' ; |_ ; |_| ; _ RETLW B'00000111' ; | ; | ; _ RETLW B'01111111' ; |_| ; |_| ; _ RETLW B'01101111' ; |_| ; _| ; _ RETLW B'01110111' ; |_| ; | | ; RETLW B'01111100' ; |_ ; |_| ; _ RETLW B'00111001' ; | ; |_ ; RETLW B'01011110' ; _| ; |_| ; _ RETLW B'01111001' ; |_ ; |_ ; _ RETLW B'01110001' ; |_ ; | end ; 

The OP has not defined how the button inputs act so the OP needs to modify this example to meet that requirement.

The basic operation just counts up from 01 to 99. At a rate of one second per count. The rollover from 99 will show 00.

This has only been tested with the MPLAB v8.92 simulator, changes may be required to work in real hardware or whatever the Proteus simulator schematic has connected.

\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.