Handling MSP430 System Reset Events

From Texas Instruments Wiki
Jump to: navigation, search

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:

SYSRSTIV Register options.png


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:

[c]

   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. (For more information about MSP430 interrupts, grouped interrupts and IV registers, please refer to the Interrupts chapter in the MSP430 Workshop.)



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.
[c]

  1. 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: // Pin 1

                     GPIO_toggleOutputOnPin( GPIO_PORT_P1, GPIO_PIN0 );
                     break;

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(); } } 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(). Once again, for more information about MSP430 interrupts, grouped interrupts and IV registers, please refer to the Interrupts chapter in the MSP430 Workshop.)



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:
[c] 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.