AT89S52 Software Generated Interrupt

Thread Starter

Dseng

Joined Jan 13, 2016
26
I am trying to generate an interrupt via software in an Atmel AT89S52. It's my understanding that I can set TCON.1 to generate a software interrupt but, for some reason, after I set it, it's not branching to the ISR. It works fine when I do a hardware interrupt. Here is some basic code I wrote to test it. Am I missing something?

org 0
SJMP RESET
org 3
LJMP ISERV

RESET: MOV IE,#81H ;ENABLE INTERRUPT
SETB TCON.1 ;GENERATE AN INTERRUPT

HERE: SJMP HERE

ISRV: MOV A,#0FFH ;IT NEVER GETS TO THIS POINT
 

MrChips

Joined Oct 2, 2009
21,370
Sorry, I could not find any software interrupt for the AT89S52.
Presumably you are attempting to set the External Interrupt flag IE0 in TCON.
That does not do it. The hardware has to set the flag.
 

Thread Starter

Dseng

Joined Jan 13, 2016
26
I am thinking that may be the case. The datasheet doesn't say squat about the TCON register. It's only mentioned once in the entire datasheet and that is to show it's location in memory.

The hardware interrupt works like a charm.
 

Papabravo

Joined Feb 24, 2006
13,962
The way I did it on multiple Atmel parts was by setting one of the 8-bit timers to expire after 1 tick of the clock. Setting the timer run bit is all you have to do to trigger an interrupt.
Also I did not see in your code where you set the GLOBAL interrupt enable bit.

EDIT: Sorry, the loading of IE with #81 sets the global enable.

The TCON register is not where the external interrupt flags are located. Bits 0 and 1 of TCON only configure the behavior of the external interrupt to various conditions on the pin. In fact I don't think it is possible to set the external interrupt flag with an instruction. Since vectoring to the ISR also clears the external interrupt flag there is no need to do it in the firmware. Timer flags are a different story and that is why I used them for that purpose.
 
Last edited:

Thread Starter

Dseng

Joined Jan 13, 2016
26
I'm using the interrupt routine for power up/down. I want to generate a software interrupt to power down if it's been left on too long with no user activity.

Writing 81H to the IE register should set the gate (IE.7) for all interrupts and enable INT.0 (IE.0). Writing this number makes the hardware interrupt work. Is there anotjer bit that should be set?

After sleeping on it, I think I will have the interrupt routine call a power up or power down routine and have the timer call the power down routine. The one click timer also looks like a good idea.
 
Last edited by a moderator:

Papabravo

Joined Feb 24, 2006
13,962
After sleeping on it, I think I will have the interrupt routine call a power up or power down routine and have the timer call the power down routine. The one click timer also looks like a good idea.
I looked at several Atmel datasheets and I could not find any evidence of a software accessible flag for either external interrupt 0 or external interrupt 1. I think this might be common to all 8051 derivatives.
 

dannyf

Joined Sep 13, 2015
2,197
Your issue is likely somewhere else.

The following code pieces for example flips an led within the timer0 isr:

Code:
...

//tmr0 isr
void _tmr0_isr(void) interrupt TF0_VECTOR {
    //clear the flag                    //automatically done by hardware
    IO_FLP(LED_PORT, LED);                //flip the led
    //_tmr0_isr_ptr();                    //call the handler
}

//other code pieces
...
int main(void) {
    //set up the mcu
    ...
    while (1) {
        //IO_FLP(LED_PORT, LED);        //flip the led
        delay(LED_DLY);
        TF0 = 1;                        //trigger the isr
    }
}
 

Thread Starter

Dseng

Joined Jan 13, 2016
26
I solved this problem by setting a "NO_INTERRUPT" flag when the time out timer ran out and then called the ISR. Before exiting the ISR I popped the stack and pushed a 0000 onto it so the program would return to the beginning of the program. Then I checked the flag and if it is high then left the routine using a RET instruction otherwise I left using RETI. It seems to work.
 

Papabravo

Joined Feb 24, 2006
13,962
I solved this problem by setting a "NO_INTERRUPT" flag when the time out timer ran out and then called the ISR. Before exiting the ISR I popped the stack and pushed a 0000 onto it so the program would return to the beginning of the program. Then I checked the flag and if it is high then left the routine using a RET instruction otherwise I left using RETI. It seems to work.
BEWARE! The 8051 can have priority levels assigned to interrupts. When there is an interrupt, the status of the priority level is saved to allow nested interrupts. Vectoring to address 0 will not handle the priority flag correctly. The only thing that can fix this problem is a HARD RESET. If this problem bites you, it will be a stone bitch to figure out. You really should avoid "cute" solutions like this. If you want to run the risk, it's all on you. That's why you get paid big bucks to take the call at 3 AM.
 

Thread Starter

Dseng

Joined Jan 13, 2016
26
But I didn't enter the ISR through an interrupt. I simply called the routine when the timer ran out. There was no interrupt.
 

Papabravo

Joined Feb 24, 2006
13,962
But I didn't enter the ISR through an interrupt. I simply called the routine when the timer ran out. There was no interrupt.
I have no idea what you think you are doing, and it sounds pretty off the wall. Maybe you could post you code so we can know what your actually doing.
 

Thread Starter

Dseng

Joined Jan 13, 2016
26
My previous post was a little confusing. When I say "timer" I don't mean an internal 8051 timer. I'm talking about three memory locations that count how long it has been since a button on the panel has been pressed. No interrupts are generated when these "timers" run out. My code is contained in the next post.
 
Last edited:

Thread Starter

Dseng

Joined Jan 13, 2016
26
Code:
THRM:   ;REGULATE THE TEMPERATURE   CALLS HERE ONCE PER SECOND.
            CLR     F1              ;RESET SECOND UP FLAG
            CLR        TCI                ;DISABLE TIMER INTERRUPT
            LCALL   THERMO         ;CHECK THE TEMPERATURE AND TURN THE HEATER ON OR OFF
            CLR        PSW.3            ;SELECT RB0
            DJNZ    SECCNT,THRM9    ;HAS IT BEEN 1 MINUTE SINCE A BUTTON HAS BEEN PRESSED?
            DJNZ    MINCNT,CONTINUE ;IF YES THEN HAS IT BEEN 20 MINUTES SINCE A BUTTON HAS BEEN PRESSED?
            CLR        TCI                ;IF YES THEN DISABLE TIMER INTERRUPT
            ORL        P1,#0FH            ;TURN OFF ALL DIGITS
            MOV        Y,#4            ;NUMBER OF TIMES TO BEEP 
BEEPAGN:    LCALL    BEEPT1            ;BEEP FOR 1 SECOND. THE BUZZER IS TOGGLED VIA T1.
            CLR        PSW.3            ;SELECT RB0
            MOV        R1,#250            ;WAIT 500 MSEC BETWEEN BEEPS
            LCALL    MSEC
            MOV        R1,#250
            LCALL    MSEC
            DJNZ    Y,BEEPAGN
          
            MOV        MINCNT,#TOM        ;RESET MINUTES FOR TIMEOUT COUNTER
            DJNZ    TIMEOUT,CONTINUE;HAVE NO BUTTONS BEEN PRESSED FOR 2 HOURS?
            SETB    SHDFLAG            ;IF YES THEN SET SHUT DOWN FLAG AND CALL INTERRUPT SERVICE ROUTINE
            LCALL    ISERV            ;GO TO INTERRUPT SERVICE ROUTINE AND SHUT DOWN.

CONTINUE:    MOV     SECCNT,#TOS     ;OTHERWISE RESET FOR ANOTHER MINUTE
THRM9:     SJMP    LOOP
;
;INTERRUPT SERVICE ROUTINE: POWER BUTTON.

ISERV:     CLR        TCI                    ;DISABLE TIMER
            ANL        P3,#WHOFF            ;TURN OFF THE WATER AND HEATERS
            ORL        P1,#1FH                ;TURN OFF ALL DISPLAYS
          
            CLR        PSW.3                ;SELECT RB0
            MOV        R1,#30    ;<---was 250            ;WAIT 1 SEC
release:    jnb        p3.2,release    ;wait for button release
            JB        PONOFF,OFF            ;IF WE ARE OFF THEN WE WANT TO TURN BACK ON.
            SETB    PONOFF                ;LET THE SYSTEM KNOW WE ARE TURNING ON THE POWER
            SJMP    ON
OFF:        CLR        PONOFF                ;LET THE SYSTEM KNOW WE ARE TURNING OFF.

ON:            POP        0E0H                ;CLEAR THE STACK - POP IT INTO THE ACCUMULATOR
            POP        0E0H
            MOV        A,#25H
            PUSH    0E0H                ;SET THE STACK TO RETURN TO BEGINING
            MOV        A,#00H
            PUSH    0E0H
          
            JB        SHDFLAG,SDOWN        ;IF WE HAVE NOT TIMED OUT THEN THIS ROUTINE WAS CALLD BY AN INTERRUPT
            RETI                        ;AND WE NEED THE RETI INSTRUCTION

SDOWN:        CLR        SHDFLAG                ;OTHERWISE, WE HAVE TIMED OUT AND NEED TO RESET THE TIME OUT SHUT DOWN FLAG
            RET                            ;AND RETURN VIA A RET INSTRUCTION BECAUSE THIS ROUTINE WAS NOT CALLED BY AN INTERRUPT.

[code]
 
Top