Handling System Reset Events
The MSP430 provides a number of system reset interrupt events. These events include items like Brown Out Reset (BOR), Watch Dog Timeout (WDTTO) or Supervisor High-side under-voltage (SVSH).
When a reset occurs, the cause of the event it logged by the MSP430 CPU’s System peripheral (SYS). Like other grouped interrupts on the MSP430, you can determine which event caused the interrupt by reading the associated Interrupt Vector register; in this case, the register is called SYSRSTIV (System Reset Interrupt Vector).
The interrupt vector register returns a number based on the highest priority event that occurred. For example, in the case of SYSRSTIV on the MSP430F5529 the events (and associated enumerations) include:
Note: Please check your device's associated User Guide and datasheet for an accurate SYSRSTIV Register Description.
Reading the SYSRSTIV Register
The most common way to handle grouped interrupts on the MSP430 is to read the associated interrupt vector register using a switch-case statement to decipher the currently-pending, highest-priority event.
An example of this for the ‘F5529 SYSRSTIV might look like:
switch ( __even_in_range( SYSRSTIV, SYSRSTIV_PMMKEY ) ) { case SYSRSTIV_NONE: break; // No Interrupt pending case SYSRSTIV_BOR: break; // SYSRSTIV : BOR case SYSRSTIV_RSTNMI: break; // SYSRSTIV : RST/NMI case SYSRSTIV_DOBOR: break; // SYSRSTIV : Do BOR case SYSRSTIV_LPM5WU: break; // SYSRSTIV : Port LPM5 Wake Up case SYSRSTIV_SECYV: break; // SYSRSTIV : Security violation case SYSRSTIV_SVSL: break; // SYSRSTIV : SVSL case SYSRSTIV_SVSH: break; // SYSRSTIV : SVSH case SYSRSTIV_SVML_OVP: break; // SYSRSTIV : SVML_OVP case SYSRSTIV_SVMH_OVP: break; // SYSRSTIV : SVMH_OVP case SYSRSTIV_DOPOR: break; // SYSRSTIV : Do POR case SYSRSTIV_WDTTO: break; // SYSRSTIV : WDT Time out case SYSRSTIV_WDTKEY: break; // SYSRSTIV : WDTKEY violation case SYSRSTIV_KEYV: break; // SYSRSTIV : Flash Key violation case SYSRSTIV_FLLUL: break; // SYSRSTIV : FLL unlock case SYSRSTIV_PERF: break; // SYSRSTIV : peripheral/config area fetch case SYSRSTIV_PMMKEY: break; // SYSRSTIV : PMMKEY violation default: break; }
To quickly created this switch statement by copy/pasting the SYSRSTIV_ values from the 'msp430f5529.h' file. Each MSP430 device has its own associated header file which defines the register symbols found in that device.
Where do you use the IV switch-case statement?
If you’ve already written switch-case statement to handle interrupts, such as the GPIO Ports or the Timers, then you probably already know how to use them. As a quick refresher, here’s an example of handling a GPIO interrupt.
You usually find the switch-case statement inside of the interrupt service routine. You create an interrupt service routine by doing two things: (1) create an interrupt function by using the __interrupt keyword; (2) the vector pragma tells the MSP430 code generation tools to put the address of the next function into the interrupt vector table.
#pragma vector=PORT1_VECTOR __interrupt void pushbutton_ISR (void) { switch(__even_in_range( P1IV, 10 )) { case 0x00: break; // None case 0x02: break; // Pin 0 case 0x04: break; // Pin 1 case 0x06: break; // Pin 2 case 0x08: break; // Pin 3 case 0x0A: break; // Pin 4 case 0x0C: break; // Pin 5 case 0x0E: break; // Pin 6 case 0x10: break; // Pin 7 default: _never_executed(); GPIO_toggleOutputOnPin( GPIO_PORT_P1, GPIO_PIN0 ); break; }
On a side-note, since interrupts (by default) are disabled when running your interrupt service routine (ISR), you need to be careful when your interrupt response code is more than simply reading or writing to a register (as we did in the example above). If your code is more involved, we highly recommend that you set a flag in your ISR function and handle the event in main().
Where should you handle SYSRSTIV?
Your first reaction might be to create a reset ISR function similar to the example shown above for GPIO interrupts. (In fact, this is what we tried to do… and it failed.)
The problem ends up being that the following line is ignored by the tools:
-
#pragma vector=RESET_VECTOR
-
Even though the device header file defines RESET_VECTOR the tools ignore the pragma… and that’s a good thing. The reset vector is handled automatically by the C compiler using the function c_int00(). This function handles various things like setting up the stack and heap, initializing global and static variables, and branching to main(). Since you probably don’t want to re-write this function from scratch, it’s good that the tools ignored the RESET_VECTOR #pragma. (If you’re really curious, you can find this function defined in the source file boot.c shipped with the compiler’s Run-Time Support (RTS) library.)
The easiest way to handle SYSRSTIV is to place the code towards the beginning of your main() function. In our case, we decided to create a Reset_ISR() function and call it from main(), as shown here:
void main (void) { // Stop watchdog timer WDT_A_hold(WDT_A_BASE); // Determine the cause of previous reset event RESET_ISR(); // Initialize GPIO initGPIO(); while( 1 ) { __no_operation(); } } void Reset_ISR(void) { switch (__even_in_range(SYSRSTIV, SYSRSTIV_PMMKEY)) { case SYSRSTIV_NONE: // No Interrupt pending __no_operation(); break; case SYSRSTIV_BOR: // SYSRSTIV : BOR __no_operation(); break; case SYSRSTIV_RSTNMI: // SYSRSTIV : RST/NMI __no_operation(); break; case SYSRSTIV_DOBOR: // SYSRSTIV : Do BOR __no_operation(); break; case SYSRSTIV_LPM5WU: // SYSRSTIV : Port LPM5 Wake Up __no_operation(); break; case SYSRSTIV_SECYV: // SYSRSTIV : Security violation __no_operation(); break; case SYSRSTIV_SVSL: // SYSRSTIV : SVSL __no_operation(); break; case SYSRSTIV_SVSH: // SYSRSTIV : SVSH __no_operation(); break; case SYSRSTIV_SVML_OVP: // SYSRSTIV : SVML_OVP __no_operation(); break; case SYSRSTIV_SVMH_OVP: // SYSRSTIV : SVMH_OVP __no_operation(); break; case SYSRSTIV_DOPOR: // SYSRSTIV : Do POR __no_operation(); break; case SYSRSTIV_WDTTO: // SYSRSTIV : WDT Time out __no_operation(); break; case SYSRSTIV_WDTKEY: // SYSRSTIV : WDTKEY violation __no_operation(); break; case SYSRSTIV_KEYV: // SYSRSTIV : Flash Key violation __no_operation(); break; case SYSRSTIV_FLLUL: // SYSRSTIV : FLL unlock __no_operation(); break; case SYSRSTIV_PERF: // SYSRSTIV : peripheral/config area fetch __no_operation(); break; case SYSRSTIV_PMMKEY: // SYSRSTIV : PMMKEY violation __no_operation(); break; default: break; } }
Of course, you would want to replace __no_operation() calls above with the code needed for your application.
As a final note, the compiler does provide a way for you to insert code into the c_int00() reset function. If defined, the reset function calls a function named _system_pre_init(). While our preference is to explicitly handle the reset evaluation in main(), using the pre-init function allows you to handle events earlier, if there is a critical need to do so.