Quick refresher from last time: an interrupt is an event that causes a microcontroller to stop what it’s doing, handle the interrupt by running a special function called an interrupt service routine (ISR), and then pick up where it left off before the interrupt happened.
This week I’ll start hooking up the button on the MSP430 LaunchPad to an interrupt and using that interrupt to turn a light on and off.
Each GPIO pin on the MSP430 (and most microcontrollers) can be configured as an interrupt. I’ll configure GPIO P1.1 as an interrupt. This GPIO is connected to a button on the LaunchPad, as explained in the GPIO inputs series of posts. Note that the end result will be similar to what we did in that GPIO inputs post, except that this example will use an interrupt instead of constantly reading the state of the P1.1 pin.
Let’s start the example with the same setup code as I used in the GPIO inputs post:
; Set GPIO P1.0 to be an output (P1DIR bit 0 == 1) BIS.B #1, &P1DIR ; Set GPIO P1.1 to be an input (P1DIR bit 1 == 0) BIC.B #2, &P1DIR ; Set GPIO P1.1 to be pulled up or down (P1REN bit 1 == 1) BIS.B #2, &P1REN ; Set GPIO P1.1 as a pull-up resistor (P1OUT bit 1 == 1) BIS.B #2, &P1OUT
Note: I’ve changed the “PA” references to “P1”: previously I used PADIR_L instead of P1DIR, PAREN_L instead of P1REN, etc. I switched to using P1 because it makes reading TI’s documentation more consistent. Remember that Port 1 (P1) and Port 2 (P2) combine to form Port A (PA). Some TI documentation refers to P1 and P2, and some refers to PA. It’s a bit confusing until you remember that PA = P1 + P2.
Here we setup P1.0 as an output (it’s connected to the red LED), and P1.1 as an input with an internal pull-up resistor (it’s connected to the button “S2”).
Now for the new stuff: configuring P1.1 as an interrupt. On the MSP430, you can choose if an interrupt should happen when a GPIO goes from low-to-high, or high-to-low. The Port 1 Interrupt Edge Select register (P1IES) controls which edge an interrupt happens on. We use the term edge to mean the transition from when a signal changes from low-to-high or high-to-low. When a signal goes from low to high we call that a rising edge, since the signal value “rises up” to a higher level. When a signal goes from high to low we call that a falling edge, since the signal value “falls down” to a lower level.
In our case, we want to get an interrupt each time the button is pressed. As you may remember from the GPIO inputs post, when the button is not pressed P1.1 is high, and when the button is pressed then P1.1 goes low. So the not-pressed to pressed transition will have P1.1 go from a high value to a low value; we want to trigger the interrupt on a high-to-low edge. We set P1.1’s bit in the P1IES register to 1 to use a high-to-low edge, as described in the MSP430F5529 User’s Guide Chapter 12, “Digital I/O Module”:
So to set P1.1 as a high-to-low edge interrupt, we set P1IES bit 1 to 1:
; Set interrupt on high-to-low transition of P1.1 BIS.B #2, &P1IES
Now we’ve configured P1.1 as a high-to-low interrupt, but how do we actually enable the interrupt? There are two more registers we need to modify:
The Port 1 Interrupt Enable register (P1IE) enables or disables the interrupt for each pin in P1. When an interrupt happens on a P1 pin, the corresponding bit in the Port 1 Interrupt Flag register (P1IFG) will be set by hardware. Software is responsible for clearing P1IFG after handing the interrupt in the ISR.
So we need to enable the interrupt on P1.1, which is done by setting a bit in the P1IE register - easy enough.
But here’s a nasty surprise about this interrupt: when we changed P1IES to set the interrupt edge to high-to-low, it might have set P1IFG! TI mentions this in a “NOTE” in their User’s Guide:
“...setting the corresponding interrupt flags” means that P1IFG will be set. If P1IFG is set when you enable interrupts in P1IE, then an interrupt will immediately occur even though you didn’t push the button or otherwise change the value on P1.1."
This maybe very confusing! You may wonder why an interrupt happened even though you didn’t push the button. So you’ll hunt through the documentation and find this little tidbit labeled “NOTE” and wish TI would have worked around this problem in hardware instead of forcing you to deal with it in your software. Oh well, such is life with interrupts.
The way to workaround this is to clear the P1IFG bit after changing P1IES to make sure that P1IFG is clear right before you enable interrupts in P1IE. Here’s the new code:
; Set interrupt on high-to-low transition of P1.1 BIS.B #2, &P1IES ; Clear any interrupts that happened when changing P1IES BIC.B #2, &P1IFG ; Enable P1.1 interrupts BIS.B #2, &P1IE
It’s always a good idea to carefully read the interrupt documentation for your microcontroller at least a couple times, especially when you see unexpected behavior. Don’t just skim interrupt documentation, since it usually contains dangerous gotchas that are too easy to overlook.
Now we’ve configured P1.1 as an interrupt when it goes from high to low, made sure there are no pending interrupts (P1IFG is clear), and finally enabled the P1 interrupt.
Are we done enabling interrupts yet? Not quite, but we’re close!
The MSP430 has a special bit to allow any interrupts to happen: it’s called the General Interrupt Enable bit (GIE), and it lives in the SR register. GIE is not an interrupt itself; rather, it’s the bit that controls whether interrupts can occur.
If GIE == 0 then no interrupts can happen. Only when the GIE == 1 will any interrupts actually happen, as long as the appropriate local interrupt is enabled, like P1IE.
We set GIE == 1 and allow interrupts to happen like this:
; Enable interrupts BIS #GIE, SR
Now we have fully enabled the P1.1 interrupt. The next step is to figure out how to write an ISR to handle the P1.1 interrupt - tune in next time for that!
This post is part of a series. Check out the complete Embedded Software Engineering 101 series here.