This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Mysterious: TAIE getting enabled somehow

Other Parts Discussed in Thread: MSP430F1101A

Setup is:

MSP430F1101A

Internal DCO at wakeup then switch to external 4.096MHz crystal.

AQ430 C compiler/IDE

MSP-FET430UIF

This processor keeps getting locked up. I've added all kinds of pin wiggles to watch whats happening, because it doesnt happen while connected to the debugger. As far as I can tell, it appears that a Timer A interrupt sometimes starts to repeatedly occur, and since originally there was no TimerA ISR, the code jumps to FFFF, executes the nonsense opode (actually the reset vector address) and goes off into la-la land.... The mystery is that as far as I can tell by breaking in the simulator and my own analysis of the code, I never enable TAIE. Note: there is now a Timer A ISR, but only for debuggin this problem. It is not normally there and as far as I can tell should not need to be there since I never enable TAIE.

To prove this, I added interrupt handler functions for every vector, and when any of them are activated (interrupt occurs), I wiggle a pin as many times as that vector. So if vector 8 occurs, I wiggle a pin 8 times so I can tell. Since it appeared Timer A interrupt was occuring, I added another debug feature that leaves the pin high if TAIE is enabled. And it is. How can this be?

The way this processor is used is:

Initial power on.

Setup hardware in a typical way, go to sleep LPM4.

Get awoken every 1000ms by a port 1 interrupt, switch oscillators, use timer A capture to measure something (without interrupt), go back to sleep.

The code is (not including cstart.asm):

// --------------
// Include Files
// --------------

#include <msp430x11X1.h>

// ---------------------
// Function Prototypes
// ---------------------

void main( void );
void init_hardware( void );
void check_cha_frequency( void );
void check_chb_frequency( void );
void isr(void);
unsigned char vect;

// ------------
// Definitions
// ------------

// To make the program easier to understand.
#define TRUE     1
#define FALSE     0

// For Time Delay Loops
#define HS_OSC_DELAY   0xFF
#define INTERRUPT_DELAY_TIME  100

// Define Window Limits
#define SMCLK_FREQ    4096000
#define SMCLK_FREQ_ERROR   40   // Allowable deviation.
#define MAX_BAD_COUNTS   100   // Used for filtering bad counts.
#define CHA_HIGH_LIMIT    ( SMCLK_FREQ / 4000 ) + SMCLK_FREQ_ERROR
#define CHA_LOW_LIMIT     ( SMCLK_FREQ / 4000 ) - SMCLK_FREQ_ERROR
#define CHB_HIGH_LIMIT   ( SMCLK_FREQ / 4000 ) + SMCLK_FREQ_ERROR
#define CHB_LOW_LIMIT   ( SMCLK_FREQ / 4150 ) - SMCLK_FREQ_ERROR

// Inputs
#define HV_ACTIVE    BIT3
#define CHA_HV_DETECT   BIT2
#define CHB_HV_DETECT   BIT1
#define CHA_AND_CHB    ( BIT1+BIT2 )

// Outputs
#define CHA_HV_GOOD    BIT2
#define CHB_HV_GOOD    BIT1
#define TEST_OUT    BIT0

// -------------
// Define Enums
// -------------

// ----------------------
// Structure Definitions
// ----------------------

// ------------------
// Global Variables
// ------------------

// ----------------
// Local Variables
// ----------------

// Channel A Related Variables
unsigned char ucChAError;
unsigned int uiChAFrequency;
unsigned int uiPrevChAFrequency;
unsigned int uiCurrentChAFrequency;
unsigned char ucChAHVPresentFlag;

// Channel B Related Variables
unsigned char ucChBError;
unsigned int uiChBFrequency;
unsigned int uiPrevChBFrequency;
unsigned int uiCurrentChBFrequency;
unsigned char ucChBHVPresentFlag;

// Diagnostics Related Variables
unsigned int ucNumberWdResets = 0;

// Store Version Number
unsigned int uiVersionNumber = 101; // Can only be seen with a debugger.

void main( void )
  { 
  init_hardware();
 
  while( TRUE )
    {
    WDTCTL = WDT_ARST_1000;     // Set Watchdog Timer interval.
    
 // So we can  tell that a watchdog reset occured.
    if(( IFG1 & WDTIFG ) == WDTIFG )
      {
      // Set the flag.
      ucNumberWdResets = 0xFF;
       P1OUT &= ~BIT0;
  P1OUT |= BIT0;
  P1OUT &= ~BIT0;
  IFG1 &= ~WDTIFG;
      }
   
    // Start monitoring the frequency for channels A and B.
    check_cha_frequency();  // CHA_HV_GOOD HIGH = LOW, HV OK.

    // -----------------
    // ENTER SLEEP MODE
    // -----------------
   
    // If HV_ACTIVE input is low, SLEEEEEPPPP.....  
    if(( P1IN & HV_ACTIVE ) == 0 )
      {
      P2OUT |= CHA_HV_GOOD;  // Set output high so we are in a known state.
      P2OUT |= CHB_HV_GOOD;  // Set output high so we are in a known state.
      _DINT();     // Momentarily disable interrupts.
      P1IE |= HV_ACTIVE;  // Enable HV_ACTIVE interrupts (disabled in ISR).
      P1IFG = 0;    // Clear HV interrupt flag.
      _EINT();     // Re-enable interrupts
      LPM4;      // Enter LPM4.     
      }
    }
  }
  
 
void init_hardware( void )
  {
  unsigned int i;
 
  WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
  BCSCTL1 |= ( XTS+XT2OFF+DIVA_3 );  // ACLK = LFXT1 = HF XTAL ACKL / 8.
  BCSCTL2 |= ( SELS+SELM_2 );   // Switch SMCLK, MCLK to HS Crystal.
 
  // If there is a problem with the HS Oscillator we will hang up here.
  do
    {
    IFG1 &= ~OFIFG;                     // Clear OSC Fault flag
    for( i = HS_OSC_DELAY; i>0; i-- ); // Time for flag to set
    }   
  while (( IFG1 & OFIFG ) != 0 );       // OSC Fault flag still set?
 
  // Setup Port 1 Inputs
  P1SEL |= CHA_HV_DETECT;    // Setup CCR1 - Capture mode.    
  P1SEL |= CHB_HV_DETECT;    // Setup CCR0 - Capture mode.
 
  P1DIR |= BIT0;      // Make unused I/O pins outputs.
  P1OUT &= ~BIT0;      // And set unused I/O pins Low.
 
  P1IES = 0;       // Interrupt on Rising Edge
  P1IE |= HV_ACTIVE;     // Enable HV_ACTIVE interrupts.
  P1IFG = 0;       // Clear HV_ACTIVE interrupt flag.

  // Setup Capture Compare Rregisters
  TACTL = ( TASSEL_2+MC1+TACLR );  // SMCLK - Clear TAR, Continuous Up mode.      
  TACCTL0 = ( CM_2+SCS+CAP );   // Falling edge, capture mode.
  TACCTL1 = ( CM_2+SCS+CAP );   // Falling edge, capture mode.
 
  // Setup Port 2 outputs
  P2DIR |= 0x3F;      // Set P2.0 - P2.6 to outputs.
  P2OUT = 0x06;       // Start CH A and CH B Good lines high.
 
  // Initialize global variables.
  ucChAError = 0; 
  uiChAFrequency = 0;
  uiPrevChAFrequency = 0;
  uiCurrentChAFrequency = 0;
  ucChAHVPresentFlag = FALSE;
 
  ucChBError = 0;
  uiChBFrequency = 0;
  uiPrevChBFrequency = 0;
  uiCurrentChBFrequency = 0;
  ucChBHVPresentFlag = FALSE;
  IE1 |= WDTIE;                         // Enable WDT interrupt
 
  //---------------------------
  // Get ready. Get set. Go!!!
  // --------------------------
 
  _EINT();                              // Enable interrupts
  }  
                      
void check_cha_frequency( void )
  {
  unsigned int i, n;
 
  // See if electrical stimulation is being produced on Channel A.
 
  // This loop is so that we can get a specified number of errors in a row
  // before concluding that the frequency is off.
 
  ucChAError = 0;
  for( i=0; i<MAX_BAD_COUNTS; i++ )
    {
    TACCTL1 &= ~CCIFG;      // Clear the CCR1 interrupt flag.
    n=0;         // Intialize delay loop counter.
    while( TRUE )
      {
      // This loop is so that we don't get hung up forever waiting for an
      // interrupt to occur. If it has not occured after n times through the
      // loop we just return.
     
      if(( TACCTL1 & CCIFG ) == CCIFG )  // CCR1 Interruptflag set?
        {
        uiCurrentChAFrequency = TACCR1;  // Yes, Save the TAR count.
        uiChAFrequency = uiCurrentChAFrequency - uiPrevChAFrequency;
       
        // Is the TAR count within prescribed limits?
        if(( uiChAFrequency < CHA_LOW_LIMIT ) || ( uiChAFrequency > CHA_HIGH_LIMIT ))
          {
          ucChAError++;      // Out of limits. Increment the error count.
          }
        uiPrevChAFrequency =  TACCR1;  // Read current TAR for next iteration.
        break;
        }
      else
        {
        n++;        // Increment delay loop counter.
        if( n > INTERRUPT_DELAY_TIME )
          {
    // Set output high because no frequency was detected.
     P2OUT |= CHA_HV_GOOD;
          return;       // Bail out.
          }
        }
      }   
    }
        
  // Evaluate results of frequency test for Channel A
  if( ucChAError < MAX_BAD_COUNTS )
    {
    P2OUT &= ~CHA_HV_GOOD;     // Set output LOW because frequency is within limits.
    }
  else 
    {
    P2OUT |= CHA_HV_GOOD;     // Set output high because frequency is out of bounds.
    }
  }

interrupt[ PORT1_VECTOR ] void hv_active_int( void )
  {
  unsigned int i;

  // Wake up if HV Active goes high. We should be running off the DCO
  // Clock at first.
  LPM4_EXIT;             // Resume normal operation. 
  WDTCTL = WDT_ARST_1000;       // Start Watchdog Timer interval
   
  // Switch to HS Osillator.
  BCSCTL1 |= ( XTS+XT2OFF+DIVA_3 );  // ACLK = LFXT1 = HF XTAL ACKL / 8.
  BCSCTL2 |= ( SELS+SELM_2 );   // Switch SMCLK, MCLK to HS Crystal.
 
  // Wait for HS Oscillator to stablize. 
  do
    {  
    IFG1 &= ~OFIFG;      // Clear OSC Fault flag
    for( i = HS_OSC_DELAY; i>0; i-- ); // Time for flag to set
    }   
  while (( IFG1 & OFIFG ) != 0 );       // OSC Fault flag still set?
 
  P1IFG = 0;       // Clear interrupt flag.
  }
 

void isr(void)
{
 unsigned char count;
 P1DIR |= BIT0;
 for(count = 0; count < vect; count++)
 {
  P1OUT &= ~BIT0;
  P1OUT |= BIT0;
  P1OUT &= ~BIT0;
 }
 if(TACTL |= 0x0002)
 {
  P1OUT &= ~BIT0;
  P1OUT |= BIT0;
 }
}
/*
interrupt [0] void int0(void)
{
isr();
}
*/
interrupt [2] void int1(void)
{
vect = 1;
isr();
}

interrupt [6] void int3(void)
{
vect = 3;
isr();
}

interrupt [8] void int4(void)
{
vect = 4;
isr();
}

interrupt [10] void int5(void)
{
vect = 5;
isr();
}

interrupt [12] void int6(void)
{
vect = 6;
isr();
}

interrupt [14] void int7(void)
{
vect = 7;
isr();
}

interrupt [16] void int8(void)
{
vect = 8;
isr();
}

interrupt [18] void int9(void)
{
vect = 9;
isr();
}

interrupt [20] void int10(void)
{
vect = 10;
isr();
}

interrupt [22] void int11(void)
{
vect = 11;
isr();
}

interrupt [24] void int12(void)
{
vect = 12;
isr();
}

interrupt [26] void int13(void)
{
vect = 13;
isr();
}

interrupt [28] void int14(void)
{
vect = 14;
isr();
}

  • Asa Cannell said:
      IE1 |= WDTIE;                         // Enable WDT interrupt

    You don't seem to have an ISR for the WDT initially. Well, now you have (even if this is just a dummy one). But since it now should be covered, it shouldn't execute nonsense that might set the timer IE bit.
    On second glance, you don't configure the WDT for delay mode, so this is as don't care anyway.

    The MSP has only 128 bytes ram. With all those global variables you have, and the stack space needed for the ISR, maybe you're experiencing a stack overflow. But then, your code shouldn't use that much ram (I didn't count)

    I don't see any other code that might cause a stack overflow, an array-out-of-bounds access or anything else that could cause this. So sorry, I don't know what happens.

  • I think you made a typo here:

     if(TACTL |= 0x0002)
     {
      P1OUT &= ~BIT0;
      P1OUT |= BIT0;
     }

    The condition, since it's an assignment, will always be true, and it will set the TAIE bit in TACTL.

    Tony

  • Thanks for trying!

     

    Hmm...yes I dont put the WDT in timer mode, yet I still disable WDTIE...thats unnecssary isnt it. Maybe that has unintended consequences? On the other hand, I do now have ISR's that would catch the timer WDT vector, even if it somehow happened, and I don't see evidence of it at the moment, only the Timer A vector. So maybe thats a dead end.

    Is there a way to detect a stack overflow without using the debugger?

    Is there any possibility that interrupts are nesting and actually occuring multiple times inside the PORT1_VECTOR ISR? (the only one that is in the original code).

    AQ430 says I am using 20 bytes of ram in the release version of the code (none of my debug ISRs and with another copy of the check_a_frequency function called check_b)

    HMMMM.....

    If I can start isolating this...assuming that TAIE is actually getting set somewhere, maybe I could search the hex dump of the code for a word that would set TAIE, even if its not meant to...in other words if the cpu starts going off into the nether world and executing rom bytes willy nilly, maybe I can figure out which rom byte ends up inadvertantly disassembling to TAIE = 1 (or equivalent), and work backwards from there?

     

     

  • TonyKao said:

    I think you made a typo here:

     if(TACTL |= 0x0002)
     {
      P1OUT &= ~BIT0;
      P1OUT |= BIT0;
     }

    The condition, since it's an assignment, will always be true, and it will set the TAIE bit in TACTL.

    Tony

     Whoops! Thanks. I changed it to:

     if(TACTL & 0x0002)
     {
      P1OUT &= ~BIT0;
      P1OUT |= BIT0;
     }

     Luckily that bad code shouldnt have even been reached unless an invalid ISR occured..

    So now with the code changed, it still experiences a Timer A ISR infinite loop, but its not indicating that TAIE is set anymore...WTF?? How could this be?

     

     

  • Asa Cannell said:
    yet I still disable WDTIE

    No, you enable it :) However, thsi bit is a don't care in watchdog mode, since a watchdog timeout will always trigger a reset :)

    And TonyKao got it right. Your if statement condition iside the dummy ISR is actually an assignment and sets the WDTIE bit. A self-fulfilling prophecy :)

    The IF must read

    if (TACTL & 0x0002)...

  • Asa Cannell said:

    So now with the code changed, it still experiences a Timer A ISR infinite loop, but its not indicating that TAIE is set anymore...WTF?? How could this be?

    Hi Asa, I don't see anywhere in your code a #pragma directive telling the linker where to find your ISR. You should do something like this:

    #pragma vector=SOME_INTERRUPT_VECTOR

    __interrupt void myISR(void) {

    \\ some code

    }

    Otherwise the MSP430 would keep on trying to fetch from the uninitialized interrupt vector, resulting in undefined behaviour.

    A different approach would be to use a pointer to assign the right function to the interrupt vector, or even better, to use a C++ class (gasp!) to model the entire interrupt vector space, but that's another story for another day...

    Tony

  • TonyKao said:
    Hi Asa, I don't see anywhere in your code a #pragma directive telling the linker where to find your ISR. You should do something like this:
    #pragma vector=SOME_INTERRUPT_VECTOR

    This is specific to IAR/CCS. The compiler used by Asa uses the 'interrupt[vector]' notation instead. So the ISR is properly registered.

**Attention** This is a public forum