8051 Assembly Language Timer Question (AT89S52)

Thread Starter

Dseng

Joined Jan 13, 2016
26
I am having a problem with my timer interrupt routine for a project I am working on using an AT89S52. The program below should toggle the bits on ports 1 and 2 each second. It should call the timer interrupt 160 times per second and decrement R2 each time. When R2 reaches zero it should toggle the bits on ports 1 and 2. When I run the program it only calls the timer interrupt once and then gets stuck in the "HERE" loop.

Everything in my program seems to be OK. What's wrong? Is there something in the way I'm enabling or disabling the interrupt? I don't know where to begin looking. Everything seems correct to me.

Code:
ORG     0
            JMP    RESET
org    003                   
                        JMP     ISERV        ;NOT USED IN THIS EXAMPLE

org 000Bh                   
                       JMP    TCSERV      ;TIMER 0 INTERRUPT SERVICE ROUTINE
;         
org    0030h                   
;
RESET:   
                       MOV       P1,#00H         ;MAKE P1 AN OUTPUT PORT
                       MOV        P2,#00H        ;MAKE P2 AN OUTPUT PORT
                       MOV       P3,#0FFH      ;MAKE P3 AN INPUT PORT
      
;
START:         MOV    R2,#160        ;INITALIZE R2 (NUMBER OF TIMES WE CALL THE TIMER INTERRUPT BEFORE TOGGLING P1 AND P2)
                      MOV    A,#055H       ;INITAL PORT VALUE, HALF THE BITS OF THE PORT ARE ON, HALF ARE OFF.
                      MOV IE,#00000010b  ;SETS TIMER 0 OVERFLOW INTERRUPT BUT DOESN'T ENABLE IT. (THE GATE (IE.7) IS ZERO).
                      CALL    TINIT            ;INITALIZE THE TIMER
HERE:           MOV    P1,A              ;OUTPUT TO PORT 1
                      MOV    P2,A              ;OUTPUT TO PORT 2
                      SJMP    HERE          ;KEEP DOING IT, TOGGLING THE BITS AFTER EACH 160 TIMER INTERRUPTS.


TINIT:           MOV        TMOD, #01        ;Put Timer 0 into Mode 1, (16 bit timer)
                     MOV        TL0,#0B7H        ;XTAL FREQUENCY = 3.579545MHZ. CLOCK FREQUENCY=XTAL/12=3.579545M/12=298.295K
                     MOV        TH0, #0F8H       ;THE INTERUPT CALLS 160 TIMES/SEC SO IT SHOULD COUNT 298.295417K/30 = 1864 = 0748H
                                                               ;SINCE THE COUNTER COUNTS UP WE WILL NEED TO SUBTRACT 26D7 FROM FFFF. 0748H-                                                                                    ;FFFFH=F8B7H
                    SETB    TR0                       ;THIS SHOULD START THE TIMER
                    SETB    IE.7                       ;PUTS A 1 IN THE GATE BIT OF IE AND SHOULD ENABLE TIMER 0 OVERFLOW.
                    RET
TCSERV: ;
                   CLR        IE.7                       ;DISABLE INTERRUPTS
                   CLR        TR0                      ;STOPS TIMER 0
                   CLR        TF0                      ;RESETS TIMER 0 OVERFLOW FLAG
                   MOV       TMOD, #01H       ;PUTS TIMER 0 INTO MODE 1 (16 bit timer)
                   MOV       TL0,#0B7H          ;RELOAD THE TIMER
                   MOV       TH0,#0F8H       
                   DJNZ      R2,DONE            ;HAS ONE SECOND PASSED?
                  MOV        R2,#160               ;IF YES THEN RESET FOR ANOTHER SECOND AND
                  CPL    A                               ;TOGLE BITS ON OUTPUT PORT
DONE:       SETB    IE.7                        ;TURN THE TIMER INTERRUPT BACK ON
                  SETB    TR0                        ;RESTART THE TIMER
                  RET                                     ;RETURN TO "HERE" LOOP
ISERV:      RET                                     ;THIS DOES NOTHING
END
Moderators note: used code tags for asm
 
Last edited by a moderator:

Thread Starter

Dseng

Joined Jan 13, 2016
26
I can't get the program to align properly. Here is a PDF file for it.

Code:
  ORG   0
   JMP   RESET

   ORG   003 
   JMP   ISERV     ;NOT USED IN THIS EXAMPLE
   ORG 000Bh 
   JMP   TCSERV     ;TIMER 0 INTERRUPT SERVICE ROUTINE
 
   ORG   0030h 

RESET:
   MOV     P1,#00H     ;MAKE P1 AN OUTPUT PORT
   MOV     P2,#00H     ;MAKE P2 AN OUTPUT PORT
   MOV     P3,#0FFH   ;MAKE P3 AN INPUT PORT
 
START: 
   MOV     R2,#160     ;INITALIZE R2 (NUMBER OF TIMES WE CALL THE TIMER INTERRUPT BEFORE TOGGLING P1 AND P2)
   MOV     A,#055H     ;INITAL PORT VALUE, HALF THE BITS OF THE PORT ARE ON, HALF ARE OFF.
   MOV     IE,#00000010b ;SETS TIMER 0 OVERFLOW INTERRUPT BUT DOESN'T ENABLE IT. (THE GATE (IE.7) IS ZERO).
   CALL    TINIT     ;INITALIZE THE TIMER

HERE: 
   MOV     P1,A     ;OUTPUT TO PORT 1
   MOV     P2,A     ;OUTPUT TO PORT 2
   SJMP   HERE      ;KEEP DOING IT, TOGGLING THE BITS AFTER EACH 160 TIMER INTERRUPTS.
TINIT: 
   MOV     TMOD,#01   ;Put Timer 0 into Mode 1, (16 bit timer)
   MOV     TL0,#0B7H   ;XTAL FREQUENCY = 3.579545MHZ. CLOCK FREQUENCY=XTAL/12=3.579545M/12=298.295K
   MOV     TH0,#0F8H   ;THE INTERUPT CALLS 160 TIMES/SEC SO IT SHOULD COUNT 298.295417K/30 = 1864 = 0748H
             ;SINCE THE COUNTER COUNTS UP WE WILL NEED TO SUBTRACT 26D7 FROM FFFF. 0748H-FFFFH=F8B7H
   SETB   TR0       ;THIS SHOULD START THE TIMER
   SETB    IE.7     ;PUTS A 1 IN THE GATE BIT OF IE AND SHOULD ENABLE TIMER 0 OVERFLOW.
   RET

TCSERV:
   CLR     IE.7     ;DISABLE INTERRUPTS
   CLR     TR0       ;STOPS TIMER 0
   CLR     TF0       ;RESETS TIMER 0 OVERFLOW FLAG
   MOV     TMOD, #01H   ;PUTS TIMER 0 INTO MODE 1 (16 bit timer)
   MOV     TL0,#0B7H   ;RELOAD THE TIMER
   MOV     TH0,#0F8H 
   DJNZ    R2,DONE     ;HAS ONE SECOND PASSED?

   MOV     R2,#160     ;IF YES THEN RESET FOR ANOTHER SECOND AND
   CPL     A       ;TOGLE BITS ON OUTPUT PORT

DONE:
   SETB   IE.7     ;TURN THE TIMER INTERRUPT BACK ON
   SETB  TR0       ;RESTART THE TIMER
   RET           ;RETURN TO "HERE" LOOP

ISERV:   
   RET           ;THIS DOES NOTHING

   END
Mod edit: this one's a little better. PDF's are hard to deal with. Sometmes a member will want to load the code on his machine for debugging.
 

Attachments

Last edited by a moderator:

Thread Starter

Dseng

Joined Jan 13, 2016
26
I figured this out. I didn't realize that I needed a RETI instruction as opposed to a RET when returning from an interrupt. Also, I had the timer in mode 2 (8 bit auto reload) instead of mode 1 (16 bit).
 

cmartinez

Joined Jan 17, 2007
8,220
I figured this out. I didn't realize that I needed a RETI instruction as opposed to a RET when returning from an interrupt. Also, I had the timer in mode 2 (8 bit auto reload) instead of mode 1 (16 bit).
Thanks for posting your solution to your own question... I was going to dig into it until I saw your last post. Your gesture has saved the people in this forum a bit of valuable time.

I have some experience with 8051 MCU's, so I hope I can be of help to you in the future.
Welcome to AAC, btw.
 

Thread Starter

Dseng

Joined Jan 13, 2016
26
Thanks for your response. I will probably be needing more assistance in the future. I stumbled across the answer while looking at an example for a timer interrupt routine.
 

ErnieM

Joined Apr 24, 2011
8,377
Yes, for assembly you need a "return from interrupt" instead of just a simple "return" instruction.

If you are using a higher level language consult your compiler manual carefully. When I made the jump from assembly to C on one device I took the asm and wrote C to match which was a mistake because the compiler took care of some steps that needed explicit assembly steps to perform. (I believe it was reenabling the interrupt or some such.) My error lead to occasional stack overflows when a. Quick fast bunch of interrupt occurred.

No matter... Just read understand and follow what your compiler says to do when and if you upgrade.
 
Top