Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

8051 Interrupt Branching

  1. Jul 31, 2011 #1
    I'm messing around with an 8051 microcontroller (DS89C450 if it makes a difference) and I'm trying to get a handle on how interrupts work, and what they can and can't do.

    I'm working with Assembly right now, not C.

    The program I made has a few songs in it's memory (very simple ones like "We Three Kings" and "Star-Spangled Banner"), and will start playing one at start up. Which one it plays depends on how a couple switches are set.

    What I want to do is hook a pair of push buttons to the interrupt pins on P3; INT0 will reset and repeat the current song from the beginning regardless of the switch settings, and INT1 will reset and reload a new song.

    Details aside, the essence of what I want to do is to make the interrupts cause a permanent branch to a specific location, at which point the interrupt ends and the program keeps running from that point. I don't want it to pick up where it left off after the interrupt is done.

    So far, the only way I can think of doing this is something rather kludgy, like this:
    Code (Text):

    mov     DPTR, #Song
    pop     R1
    pop     R1                    ;  Popping the original return address from stack
    push   DPH                   ;  Push the new address onto the stack
    push   DPL                   ;  or whatever order these two need to be in
    * Song is a label in the code.

    Needs to be modified a bit to protect the original value of DPTR, but you should get the basic concept.

    For the record, I understand that polling the buttons might be a better solution,and would be trivially easy to do in my program. I already have a main loop that polls the timers (Though I might change one of them to an interrupt), so adding two more polls isn't much of a hassle. I'm not trying to do this for the most efficient or workable program, i'm trying to do this in order to understand interrupts better.
  2. jcsd
  3. Aug 1, 2011 #2
    This one has a nice description of the interrupt structure:

    The best way I can think of to do this would be just to set a flag in the hardware interrupt that keeps the status of the pin that caused the last interrupt.

    I would try to avoid having excessively large interrupts as well - I might be mistaken, but that's what it sounds like you are intending to do. Nesting interrupts can get very frustrating, very quickly.

    Bear in mind that you will have to adequately debounce your switches, or you may experience multiple interrupts with undesired results. A simple hardware LPF filter might help a bit as well.
  4. Aug 1, 2011 #3
    I'm not trying to have an excessively large interrupt. I want to interrupts to act kind of like RESET, except I want them to reset to different addresses, not just 0000h. I'm not sure how to do that with the "flag" thing you're talking about. I'm not trying to nest them either.

    Perhaps "branch" is the wrong word. I meant I wanted it to jump to the specified location and keep going from there.

    Debouncing shouldn't be an issue if I can get them working right. All that would happen is it would jump to the same place each time it bounces, which isn't problematic.

    And isn't the phrase "LPF filter" redundant?
  5. Aug 1, 2011 #4


    User Avatar
    Science Advisor

    The thing about hardware interrupts that makes this more tricky than jumping out of a normal subroutine or software interrupt is that, pretty much by definition, you don't know at what point in your "main loop" code that the interrupt was called from. So this usually means you wont have full knowledge of the state of memory and registers (including SP) when you enter the interrupt. So you'll probably have to be careful to re-initialize the state of everything , including the stack, after the interrupt jumps. Other than that it should work.
  6. Aug 1, 2011 #5
    This is a fairly simple program that only uses 6 registers + DPTR for data storage, and doesn't use the stack for anything.

    Here, I'll post the code:
    Code (Text):
    ;"We Three Kings" Music Program
    ;"Pallet Town"
    ;"The Star-Spangled Banner"
    ;"Route 1"
    ;by Mark Dowd

    ;   Double Whole    : 128
    ;   Whole Note  : 64
    ;   Half Note   : 32
    ;   Quarter Note    : 16
    ;   Eighth Note : 8
    ;   16th Note   : 4

    ;   F4  FAD7
    ;   Eb4 FA35
    ;   D4  F9DD
    ;   C4  F91D
    ;   Bb3 F845
    ;   A3  F7D0
    ;   G3  F6CF
    ;   F3  F5AF
    ;   E3  F513
    ;   Eb3 F46C
    ;   D3  F3BC
    ;   C3  F23C
    ;   B2  F16A
    ;   Bb2 F08C
    ;   A2  EFA1
    ;   G2  EDA0

    ;   E4  FA89
    ;   F#4 FB21
    ;   G4  FB67
    ;   A4  FBE7
    ;   B4  FC59
    ;   C5  FC8E
    ;   D5  FCEE
    ;   E5  FD44
    ;   F#5 FD90
    ;   G5  FDB3

        org 0000h
    Begin:  mov TMOD, #11h
        mov P1, #0FFh
        mov R2, #0BEh       ;32Hz, 16 reps = 120 bpm
        mov R3, #0C7h
        mov TL1, R2
        mov TH1, R3
    Song:   mov A, P1
        anl A, #03h     ;Masking all but the imporant swiches.
        mov DPTR, #WeThreeKings
        jz  Repeat
        dec A
        mov DPTR, #PalletTown
        jz  Repeat
        dec A
        mov DPTR, #TheStarSpangledBanner
        jz  Repeat
        dec A
        mov DPTR, #Route1
        jz  Repeat
        sjmp    Song
    Repeat:mov  R1, #0      ;Note data offset
    Main:   mov A, R1
        movc    A, @A+DPTR      ;Load Freq LOW byte
        mov R4, A
        mov TL0,A
        inc R1
        mov A, R1
        movc    A, @A+DPTR      ;Load Freq HIGH byte
        mov R5, A
        mov TH0, A
        inc R1
        mov A, R1
        movc    A, @A+DPTR      ;Load duration byte
        mov R0, A
        inc R1
        orl A, R4           ;Check all the bytes retreived.
        orl A, R5           ;If they are all 0 (end of song), repeat.
        jz  Repeat
        setb    TR1
        setb    TR0
    Loop:   jb  TF1, Note
        jb  TF0, Freq
        sjmp    Loop
    Note:   clr TR1     ;reset counter, decrement note duration counter, and go
        clr TF1     ;back to loop. If note is done, go back to main to relead new note.
        mov TL1, R2
        mov TH1, R3
        setb    TR1
        djnz    R0, Loop
        clr TR1
        sjmp    Main
    ;Freq:  clr TR0     ;Flip output, reset counter, go back to loop.
    ;   cpl P2.7
    ;   clr TF0
    ;   mov TL0, R4
    ;   mov TH0, R5
    ;   setb    TR0
    ;   sjmp    Loop

    Freq:   mov TL0, R4
        mov TH0, R5
        clr TF0
        cpl P2.7
        sjmp    Loop
        ;org    200h
        DB  013h, 0F5h, 32  ;  We
        DB  0BCh, 0F3h, 16  ;  three
        DB  03Ch, 0F2h, 32  ;  kings
        DB  0A1h, 0EFh, 16  ;  of
        DB  06Ah, 0F1h, 16  ;  or-
        DB  03Ch, 0F2h, 16  ;  -i-
        DB  06Ah, 0F1h, 16  ;  -ent
        DB  0A1h, 0EFh, 48  ;  are  /
        DB  013h, 0F5h, 32  ;  Bear-
        DB  0BCh, 0F3h, 16  ;  -ing
        DB  03Ch, 0F2h, 32  ;  gifts
        DB  0A1h, 0EFh, 16  ;  we
        DB  00, 00, 00          ;   End

        DB  ......
    At the label Song, it's the best way I could come up with to select the song based on the value of the switches.

    I commented out the first Freq to see if the second freq works better at higher note frequencies.

    DPTR contains the location of the song data in the ROM. This must be preserved if the song is to Repeat, but not for a new song.

    R0 contains the duration of the note, which is loaded from the ROM. It's the third byte in each line. 16 = quarter note. Does not need to be preserved.

    R1 contains the offset, which is incremented each time a value is read from the ROM. It's moved into A to use he movc A, @A+DPTR command. Does not need to be preserved.

    Registers 2 & 3 contain the TL1 and TH1 values for Timer 1, which sets the BPM for the song. After initialization, these never change, they are hard-coded in the program. Must be preserved.

    Registers 4 & 5 contain the TL0 and TH0 values for Timer 0, which are loaded from the ROM. They set the note frequency. Do not need to be preserved.

    What I am trying do is make it so that when INT0 is triggered, the program jumps to the Repeat label, ends the interrupt, and continues from there, or something like that.

    As I said in my OP, it would be trivial to add this to the polling loop at "Loop", bu I'm trying to discover the capabilities and limits of interrupts with this right now.
  7. Aug 23, 2011 #6
    I'm not sure what the etiquette is on bumping, but I still haven't gotten an answer for this.
Share this great discussion with others via Reddit, Google+, Twitter, or Facebook