# AT89S52 Software Generated Interrupt

#### 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
22,537
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.

#### MrChips

Joined Oct 2, 2009
22,537
Maybe it works on a 8051 but not on the AT89S52.
Did you check to see if an external interrupt works?

#### 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.

#### MrChips

Joined Oct 2, 2009
22,537
Information on TCON can be found in the Atmel 8051 Microcontrollers Hardware Manual.

I suppose you can generate an external interrupt by connecting an I/O pin to the external interrupt pin /INT0.

Why do you want to generate a SWI?

#### Papabravo

Joined Feb 24, 2006
14,868
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:

#### 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
14,868
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
}
}

#### 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
14,868
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.

#### 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
14,868
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.

#### 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:

#### 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
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]