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.

MSP430G2553: MSP430 Timer/ADC10 Interrupt Conflict

Part Number: MSP430G2553

Hi Team,

Need your expertise on our customer's query. Posting on their behalf:

I am confronted with an interrupt conflict regarding ADC10 and Timer_A1. At first I built a voltage control loop with ADC10 for the voltage measurements. As found in an example the ADC10 works on its own clock and the CPU is switched OFF at ADC10-start in order to reduce noise. When the ADC10 is ready, the CPU is activated again. --- This loop works fine so far.

Secondly, I built a kind of time base with the timer_A1. With 16 Mhz DSO clock and divider 8 the timer clock  is 2 MHz resp. 0.5 ms period. With Timer set to 20000 I got the expected 10 ms. The related ISR increments a long integer timeCount and with modulo 100 operation, the expected ONE SECOND could be observed at a port.

When running both together, the CPU got trapped when the timer interrupt occurred during the ADC10-CPUOFF time frame. This problem could be resolved successfully by masking the timer interrupt during ADC10 usage However, now the timer does not generate ONE SECOND any longer, because the masking hampers the timer interrupt very often ! Now the result is close to  three seconds and this "time base" is unstable as well.

If you know any trick to resolve this problem, would really appreciate if you could share it. Otherwise, I guess, I need to generate an external "one second" signal and feed that into an unused port.


Thanks in advance!


Kind Regards,

Jejomar

  • Can you post some relevant code fragments? Sometimes all it takes is a typo.

    That said, I will now conjure a first-guess from thin air:

    A sequence you'll see frequently looks something like:

    ADC10CTL0 |= ADC10SC;   // Start conversion
    __bis_SR_register(LPM0_bits | GIE);   // Wait for ADC ISR to wake us up.

    This usually works, but is (strictly speaking) incorrect. If the ADC finishes between these two lines (it's actually quite fast) the wakeup will happen before the CPU sleeps, so there will never be a wakeup. Throwing in a timer interrupt at the right moment (from your description) will dilate time as it were, practically guaranteeing a missed wakeup.

    The pedantically-correct sequence would be:

    __bic_SR_register(GIE);   // Disable interrupts to close the window
    ADC10CTL0 |= ADC10SC;   // Start conversion
    __bis_SR_register(LPM0_bits | GIE);   // Open the window and wait for ADC ISR to wake us up.
    

  • Hi Bruce,

    Many thank for looking into this. Attached is a doc-file with code fragments related to the timer conflict problem.


    Kind Regards,

    Jejomar

    1462.measure_mWs_12a.doc

  •       ADC10CTL0 |= ENC + ADC10SC;         // Sampling and conversion start
         // LPM0, ADC10_ISR will force exit from LPM0, General Interrupt Enabled !
         __bis_SR_register(CPUOFF + GIE);     

       

    This is the sequence I referred to above. Did you try inserting "__bic_SR_register(GIE);" just before it?

  • Hello Bruce,

    Apologies for the late update.

    The extra line was included successfully, but the timer interaction problem remained.

    ADC10CTL0 |= ADC10IE; // interrupt enabled

    __bic_SR_register(GIE); // Disable interrupts to close the window; Bruce McKenney47378

    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit from LPM0,
    // General Interrupt Enabled ! (open the window)

    Since several events of the 10ms-events are suppressed by the masking
    of the timer A1 interrupts

    TA1CCTL0 &= ~CCIE; // TA1CCR0 interrupt disabled (UKA20201130)
    measureVbat();
    TA1CCTL0 = CCIE; // TA1CCR0 interrupt enabled (UKA20201130)

    a proper one-second-clock is not possible.

    It would be helpful, if I could "store & delay" all the 10ms-events of TA1, so that the
    interrupt events don't get lost. If there is any trick to do that kind of event delay,
    i.e. delayed interrupt, I could obtain a stable one-second-clock...


    Kind Regards,

    Jejomar

  • Hello Bruce,

    Just an additional update. Another experiment was performed:

    The masking was disabled

    // TA1CCTL0 &= ~CCIE; // TA1CCR0 interrupt disabled (UKA20201130)
    measureVbat();
    // TA1CCTL0 = CCIE; // TA1CCR0 interrupt enabled (UKA20201130)

    I am very astonished now that the program works as expected !

    While running the loop over night and day no interrupt conflict occured.
    The one-second-events are ok and the very short window with CPUOFF-state
    was not hit by the 10ms-interrupt of Timer_1 . That is very surprising, isn't it?


    Kind Regards,

    Jejomar

  • Hello Bruce, E2E team,

    Just an added follow-up question. Attached is a small demo program regarding the timer conflict issue. Is this demo program fail-safe against unwanted  continuous CPUOFF?


    Kind Regards,

    Jejomar

    Interrupt_Test.c
    // ***************************************************************************
    //
    //  pgm:	Interrupt_Test.c
    //
    //  auth:   Dr. Ing. Ulrich Kaiser ( retired from TID )
    //
    //  date:    see below
    //  version: see below
    //
    //
    //******************************************************************************
    //      P10 output      RED LED only
    //      P11 output
    //		P12 output 
    //		P13 input       ADC10	 VBAT+ measurement
    //		P14 output
    //		P15 output
    //		P16 output      green LED
    //		P17 output		beeper  
    //
    // *************************************************************************
    //
    // UKA20210107 	first version
    //				ADC10 measurement is included in measureVbat()
    //				  and there is a small window with GIE & CPUOFF.  ADC10
    //				  has its own clock; 29 such clocks are used for one conversion.
    //				  The green LED demonstrates the conversion rate.
    //				Timer1 generates interrupts of 10ms  that are derived from
    //				  16MHz DSO clock. The red LED is toggled @ one second rate.
    //				Is it possible that the 10ms-interrupts interact in the small
    //				  window and bring the CPU to a total hang-up ???
    // -------------------------------------------------------------------------
    #define  version   	"version 1.0"
    #define  date		"07.Jan.2021"
    
    #include <msp430g2553.h>
    
    // P1.0 drives red LED
    #define REDLED_OFF   	P1OUT &= ~BIT0
    #define REDLED_ON    	P1OUT |= BIT0
    #define REDLED_TGL   	P1OUT ^= BIT0
    
    // P1.6 drives green LED 
    #define GREENLED_OFF   	P1OUT &= ~BIT6
    #define GREENLED_ON    	P1OUT |= BIT6
    #define GREENLED_TGL   	P1OUT ^= BIT6
    
    #define BLINKDELAY		25				// 25 ms
    
    #define BEEPER_OFF   	P1OUT &= ~BIT7
    #define BEEPER_ON    	P1OUT |= BIT7
    #define BEEPER_TGL   	P1OUT ^= BIT7
    
    #define VBAT_FACTOR		3494	// correction 23.11.
    
    
    // #####  function prototypes   #####
    void delay_ms( unsigned int delay );
    void blinkGreen( int count );
    void blinkRed( int count );
    void measureVbat();
    
    
    // the following value is to be measured !
    unsigned int Vbat    =  0000; 	//  
    
    // counting the events of 10 ms from Timer_A1
    unsigned long timeCounter = 0;	   // 32 bits  !!!
    
    
    
    
    
    // ---------------- begin main ------------------------------------------------------
    int main(void) 
    {
        // Watchdog control
        WDTCTL = WDTPW | WDTHOLD;		// Stop watchdog timer
    
    ///*
    	    if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF)                                     
    	     {  
    	         while(1);                          // If calibration constants erased
    	                                            // do not load, trap CPU!!
    	     } 
    	    BCSCTL1 = CALBC1_16MHZ;                 // Set DCO to 16MHz
    	    DCOCTL = CALDCO_16MHZ;
    //*/
    #define CLOCKSPEED_MS      16000
    #define CLOCKSPEED_US         16
    #define clockFrequencyKHZ  16000
    
    
    
    
    	// #####  ADC10 Settings  #####
    	//  SREF_1 : V_R+ = VREF+ , V_R- = VSS
    	//  ADC10SHT_2 :  S&H is 16 ADC10CLKs long
    	//  REFON :  Reference generator ON	    // Vref=1.5V is default, i.e. REF2_5V=0
    	//  ADC10ON : ADC10 is ON
    	ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON;
    	// need Vref settling time = 30 us !!!
    
    
    	// ----- begin PORT 1 -----
    
    	P1DIR |= BIT0;			// Set P1.0 to output direction - launchpad LED red
    	REDLED_OFF;				// red LED off !
    
    	// P1.3   INPUT   used for A3  ADC10   (see below)
    	
      	P1DIR |= BIT6;			// Set P1.6 to output direction - LED green  and  pulses
    	GREENLED_OFF;			// Green LED off !
       	
     	P1DIR |= BIT7;			// set P1.7 to output direction - for BEEPER
        BEEPER_OFF;
    
      	// ----------- end  Port 1  -----------------
      	
      	
        // ----------------- TIMER_A_1  -----------------------------------
        
      	TA1CCR0 = 20000;			// period of Timer_A   for 10ms !!!
     	TA1CTL = TASSEL_2 + MC_1 + ID_3 ;  // SMCLK, up-mode, divide clock by 8
      	TA1CCTL0 = CCIE;            // TA1CCR0 interrupt enabled  (UKA20201129)    
        
        // ----------------- end TIMER_A_1  -------------------------------
        
      	
      	
      while(1)		// endless loop !!!
      {
    
    		measureVbat();		// contains small window with GIE and CPUOFF !!!
    		
    		blinkGreen( 1 );
    		
    		delay_ms( 151 );   //  prime number !!!
    
      	
      }// while(1)    endless loop !
        
    }// end ----------- main -----------------------------------------------------------
     	
     	
      	
    // #############################################################
    // #####    Subroutines for Interrupt Service   (ISR)      #####
    // #############################################################
    
    // ADC10 interrupt service routine
    //      starts the CPU again after ADC10 has finished
    #pragma vector=ADC10_VECTOR
    
    __interrupt void ADC10_ISR(void)
    {
        _bic_SR_register_on_exit( CPUOFF );   // Clear CPUOFF bit from (SR)
      
    }// ---------------------- ADC10_ISR ----------------------------------------
    
    
    
    // Timer A1 interrupt service routine  for overflow of TA1CCR0
    #pragma vector=TIMER1_A0_VECTOR
    __interrupt void Timer_A1 (void)
    {
        timeCounter++;  // overflow of Timer_A1,  i.e. count events of 10 ms
        
        if( timeCounter %  100L  == 0 )  REDLED_TGL;
        //if( timeCounter %  100L  == 0 )  BEEPER_TGL;
        //BEEPER_TGL;		
             
    }// -------------------- Timer_A1 ISR ------------------------------------
    
    
    
    
    
    // #############################################################
    // ### Subroutines for   delay,  i.e. idle cycles of the CPU ###
    // #############################################################
    
    void delay_ms( unsigned int delay )
     {
            while (delay--)
            {
                //__delay_cycles( PUT_CPU_CLOCK_SPEED_IN_HZ_DIVIDED_BY_1000_HERE );
                __delay_cycles( CLOCKSPEED_MS );
            }
     }// -----------------------------------------------
    
    // #############################################################
    // @@@@@@@  Subroutines for the control of the RG-LED  #########
    // #############################################################
    
    void blinkGreen( int count )
    {
    	int j=999;
    	
    	if( (count > 0) && (count < 44) )
    	{
    		for( j=0; j< count; j++ )
    		{
    			GREENLED_ON	;						
    			delay_ms( BLINKDELAY );
    			GREENLED_OFF ;							
    			delay_ms( BLINKDELAY );
    		}//for j
    	}// if
    }// ---------------- blinkGreen ----------------------
    
    
    void blinkRed( int count )
    {
    	int j=999;
    	
    	if( (count > 0) && (count < 44) )
    	{
    		for( j=0; j< count; j++ )
    		{
    			REDLED_ON ;	
    			delay_ms( BLINKDELAY );
    			REDLED_OFF ;		
    			delay_ms( BLINKDELAY );
    		}//for j
    	}// if
    }// ---------------- blinkRed ----------------------
    
    
    
    
    // #############################################################
    // ####### Subroutines for the measurements with ADC10 #########
    // #############################################################
    
    // measure the  voltage at input A3  ( pin 1.3 )
    void measureVbat()
    {
    	long int value;
    	
    	ADC10CTL0 &= ~ENC;						// ADC10 disabled
    	
      	ADC10CTL1 = INCH_3;                     // input A3
      	ADC10AE0 = BIT3;                        // PA.3 ADC option select
    	ADC10CTL0 |=  ADC10IE;     				// interrupt enabled	
    
    //REDLED_ON;
    //TA1CCTL0 &= ~CCIE;   // TA1CCR0 interrupt disabled  (UKA20201219) 
    
    	__bic_SR_register(GIE); 				// Disable interrupts to close the window; 
    											// 20201218 ;  Bruce McKenney47378
    	
    	ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit from LPM0,  
        										// General Interrupt Enabled !   (open the window)
    //REDLED_OFF;
    //TA1CCTL0 = CCIE;     // TA1CCR0 interrupt enabled  (UKA20201219)
      	
    	value = (long int)ADC10MEM;
    	
    	value = value * VBAT_FACTOR / 1000;
    	
    	Vbat = (unsigned int)value;
    	
      	ADC10CTL1 &= ~INCH_3;                   // input A3   off
      	ADC10AE0 &= ~BIT3;                      // PA.3 ADC option select  off
    	
    	ADC10CTL0 &= ~ENC;						// ADC10 disabled !!!!!!!!!!
    } // ------------------------- measureVbat ------------------------------------------
    
     
    


  • I would say yes: The ADC fast-completion window is closed, and there is only one wakeup source.

    Even without the protection, this test case could take a very long time for the meta-cycles to line up. To make a more interesting experiment, I put GREENLED_ON/_OFF around the critical sequence, and reduced the TA1CCR0 value down to 58.The failure case shows up as the P1.6 LED being stuck on. I then tried it with and without the GIE guard. (To remove the guard, I changed the "bic" to "bis" so the timing didn't change.)

    It runs much slower, of course, since it's spending most of its time in the timer ISR, so the P1.6 LED only blinks once every 3-4 seconds, Nonetheless, without the GIE guard it fails in <30 seconds. With the guard, it's been running for about 15 minutes without a failure.

  • Hello Bruce,

    Our customer modified the small demo program accordingly and could reproduce the modifications and results.

    a) green led around critical ADC10 sequence ; 
    b) TA1CCRO to 58 ;  ==>  interrupts 345x faster
    c) experiment with _bic versus _bis i.e. guard on/off
    Result: the red LED is ON, the green LED is OFF (with guard);
        without guard  the green LED goes ON after some time (randomly)

    Then, they added further modifications:

    d) removed the 151ms-delay  ==> much higher ADC10 rate ;
    e) changed 0 to %34483 w.r.t. "one second clock" ;

    Results:  the red LED blinks with "one second" rate,
              the green LED "glows",
              the program runs ok with the "guard" !

    However, the concerns are not fully removed....
    - The ADC10 has its own clock that is probably asynchronous
      to the DSO  and is between 3.7 and 6.3 Mhz.
    - With 13+16 = 29 ADC10-clocks we have a conversion time of
      4.6 to 7.8 us !
    - So, the critical window with CPUOFF ist 4.6 to 7.8 us wide.
    - The Timer1 interrupts have a rate of  2 MHz / 58 = 35 kHz
    - The Timer1 interrupt has a higher priority (29) than the
      ADC10 interrupt (21).

    Why is the critical window not hit by the higher interrupt ?


    Kind Regards,

    Jejomar


    Timer_Conflict_4.txt
    Dear Jejomar,
    
    thank you very much for your ongoing support !
    
    
    After reading Bruce's latest advice, I modified 
    the small demo program accordingly and could 
    reproduce his modifications and results.
    
    a) green led around critical ADC10 sequence ;  
    b) TA1CCRO to 58 ;  ==>  interrupts 345x faster 
    c) experiment with _bic versus _bis i.e. guard on/off
    Result: the red LED is ON, the green LED is OFF (with guard);
        without guard  the green LED goes ON after some time (randomly)
    
    Then, I added further modifications:
    
    d) removed the 151ms-delay  ==> much higher ADC10 rate ;
    e) changed %100 to %34483 w.r.t. "one second clock" ;
    
    Results:  the red LED blinks with "one second" rate,
              the green LED "glows", 
              the program runs ok with the "guard" !
    
    However, my concerns are not fully removed....
    - The ADC10 has its own clock that is probably asynchronous 
      to the DSO  and is between 3.7 and 6.3 Mhz.
    - With 13+16 = 29 ADC10-clocks we have a conversion time of 
      4.6 to 7.8 us !
    - So, the critical window with CPUOFF ist 4.6 to 7.8 us wide.
    - The Timer1 interrupts have a rate of  2 MHz / 58 = 35 kHz
    - The Timer1 interrupt has a higher priority (29) than the 
      ADC10 interrupt (21).
    Q Why is the critical window not hit by the higher interrupt ?
    
    
    With thanks in advance & best regards, Uli
    
    

  • I'm not sure I understand the question.

    I made the two changes (d)/(e) described in the .txt, and (1) without the guard, it fails (P1.6 LED stuck on) in <10 seconds (2) with the guard, it ran for 15 minutes before I got bored and stopped it. (I wouldn't say the P1.0 LED glows, rather it blinks at about 0.75Hz.)

    Was a different result expected?

    [Edit: For clarity, here is the main.c I used:

    /cfs-file/__key/communityserver-discussions-components-files/166/2311.main.c

  • Dear Bruce,

    thanks a lot for the new update of our thread !


    Please, look into this new version of the demo program, because


     I could force resp. demonstrate the CPU-stuck  (i.e. program crash)  now !

    What did I do to force the failure ?

    I programmed a loop  from 58 down to 10  instead of  the endless loop used before.
    o  Now, TA1CCR0 is looped down  from 58 down to 10 ...
    o  The ADC10- is still looped by a fixed number of 5000.
    o  I also played with introducing a random delay inside of the ADC-loop ...

    RESULT: The program crashes with TA1CCR0=55

    I don't know why - it can be reproduced easily...  even with random delay (flickering green).

    With  56 or 54 there is no crash. Why ?


    Thanks again for your valuable support !

    1024 greetings, Uli

    8664.Interrupt_Test.c
    // ***************************************************************************
    //
    //  pgm:	Interrupt_Test.c
    //
    //  auth:   Dr. Ing. Ulrich Kaiser ( retired from TID )
    //
    //  date:    see below
    //  version: see below
    //
    //
    //******************************************************************************
    //      P10 output      RED LED only
    //      P11 output
    //		P12 output 
    //		P13 input       ADC10	 VBAT+ measurement
    //		P14 output
    //		P15 output
    //		P16 output      green LED
    //		P17 output		beeper  
    //
    // *************************************************************************
    //
    // UKA20210107 	first version
    //				ADC10 measurement is included in measureVbat()
    //				  and there is a small window with GIE & CPUOFF.  ADC10
    //				  has its own clock; 29 such clocks are used for one conversion.
    //				  The green LED demonstrates the conversion rate.
    //				Timer1 generates interrupts of 10ms  that are derived from
    //				  16MHz DSO clock. The red LED is toggled @ one second rate.
    //				Is it possible that the 10ms-interrupts interact in the small
    //				  window and bring the CPU to a total hang-up ???
    // UKA20210109 After advice from Bruce  today:
    //                  green led around critical sequence ;  TA1CCRO to 58 ;
    //                  removed the 151ms-delay  ==> higher ADC10 rate ;
    //                  changed %100 to %34483 w.r.t. "one second clock" ;
    //					experiment with _bic versus _bis i.e. guard on/off   (OK)
    // UKA20210111 Made timer1 interrupt even faster : rate 100 kHz
    //                 Got failure with   TA1CCRO=20 !!!  need to investigate deeper ...
    // UKA20210112 Added option with random delay  ( using srand, rand )
    // UKA20210113 Decreased TA1CCR0 from 58 down to 10   ( for j; j-- )
    //               Runnning fixed number of ADC10 loops  ( for k )
    //              Can switch of random delay easily !
    // -------------------------------------------------------------------------
    #define  version   	"version 1.3"
    #define  date		"13.Jan.2021"
    
    #include <msp430g2553.h>
    
    
    // P1.0 drives red LED
    #define REDLED_OFF   	P1OUT &= ~BIT0
    #define REDLED_ON    	P1OUT |= BIT0
    #define REDLED_TGL   	P1OUT ^= BIT0
    
    // P1.6 drives green LED 
    #define GREENLED_OFF   	P1OUT &= ~BIT6
    #define GREENLED_ON    	P1OUT |= BIT6
    #define GREENLED_TGL   	P1OUT ^= BIT6
    
    #define BLINKDELAY		25				// 25 ms
    
    #define BEEPER_OFF   	P1OUT &= ~BIT7
    #define BEEPER_ON    	P1OUT |= BIT7
    #define BEEPER_TGL   	P1OUT ^= BIT7
    
    #define VBAT_FACTOR		3494	// correction 23.11.
    
    
    // #####  function prototypes   #####
    void delay_ms( unsigned int delay );
    void blinkGreen( int count );
    void blinkRed( int count );
    void measureVbat();
    
    void srand( unsigned n );
    int  rand();
    
    
    // the following value is to be measured !
    unsigned int Vbat    =  0000; 	//  
    
    // counting the events of 10 ms from Timer_A1
    unsigned long timeCounter = 0;	   // 32 bits  !!!
    
    
    
    int r;
    int j, k;
    
    
    
    
    // ---------------- begin main ------------------------------------------------------
    int main(void) 
    {
        // Watchdog control
        WDTCTL = WDTPW | WDTHOLD;		// Stop watchdog timer
    
    ///*
    	    if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF)                                     
    	     {  
    	         while(1);                          // If calibration constants erased
    	                                            // do not load, trap CPU!!
    	     } 
    	    BCSCTL1 = CALBC1_16MHZ;                 // Set DCO to 16MHz
    	    DCOCTL = CALDCO_16MHZ;
    //*/
    #define CLOCKSPEED_MS      16000
    #define CLOCKSPEED_US         16
    #define clockFrequencyKHZ  16000
    
    
    
    
    	// #####  ADC10 Settings  #####
    	//  SREF_1 : V_R+ = VREF+ , V_R- = VSS
    	//  ADC10SHT_2 :  S&H is 16 ADC10CLKs long
    	//  REFON :  Reference generator ON	    // Vref=1.5V is default, i.e. REF2_5V=0
    	//  ADC10ON : ADC10 is ON
    	ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON;
    	// need Vref settling time = 30 us !!!
    
    
    	// ----- begin PORT 1 -----
    
    	P1DIR |= BIT0;			// Set P1.0 to output direction - launchpad LED red
    	REDLED_OFF;				// red LED off !
    
    	// P1.3   INPUT   used for A3  ADC10   (see below)
    	
      	P1DIR |= BIT6;			// Set P1.6 to output direction - LED green  and  pulses
    	GREENLED_OFF;			// Green LED off !
       	
     	P1DIR |= BIT7;			// set P1.7 to output direction - for BEEPER
        BEEPER_OFF;
    
      	// ----------- end  Port 1  -----------------
      	
      	
        // ----------------- TIMER_A_1  -----------------------------------
        
      	//TA1CCR0 = 20000;			// period of Timer_A   for 10ms !!!
      	TA1CCR0 =    58;			// period of Timer_A   VERY HIGH EVENT RATE
      	//TA1CCR0 =    20;			// period of Timer_A   VERY HIGH EVENT RATE
     	TA1CTL = TASSEL_2 + MC_1 + ID_3 ;  // SMCLK, up-mode, divide clock by 8
      	TA1CCTL0 = CCIE;            // TA1CCR0 interrupt enabled  (UKA20201129)    
        
        // ----------------- end TIMER_A_1  -------------------------------
        
      	
      	
      	srand( 101 );
      	
      //while(1)		// endless loop !!!
      //{
      
       //for( j=58 ;  j>10 ;  j-- )
       //for( j=100 ;  j>10 ;  j-- )
       for( j=58 ;  j>10 ;  j-- )
       {
       	    TA1CCR0 =    j;		// <===  setting the period of the timer1  !!!
    		
    		
    		for( k=0; k<5000; k++)
    		{
    			measureVbat();		// contains small window with GIE and CPUOFF !!!
    		
    			if( 1 )
    			{
    			   r = rand();
    			   r = r % 97;
    			   r = abs( r );
            	   while( r-- )
            	   {
                	  __delay_cycles( 1 );
            	   }
    			}//if				
    		
    			//delay_ms( 151 );   //  prime number !!!
    		}// for k
    		
    		TA1CCR0 = 0;  // timer off
    		REDLED_OFF;
    		delay_ms( 4000 ); // long pause
    		
       }//for j
    
      	
     // }// while(1)    endless loop !
        
    }// end ----------- main -----------------------------------------------------------
     	
     	
      	
    // #############################################################
    // #####    Subroutines for Interrupt Service   (ISR)      #####
    // #############################################################
    
    // ADC10 interrupt service routine
    //      starts the CPU again after ADC10 has finished
    #pragma vector=ADC10_VECTOR
    
    __interrupt void ADC10_ISR(void)
    {
        _bic_SR_register_on_exit( CPUOFF );   // Clear CPUOFF bit from (SR)
      
    }// ---------------------- ADC10_ISR ----------------------------------------
    
    
    
    // Timer A1 interrupt service routine  for overflow of TA1CCR0
    #pragma vector=TIMER1_A0_VECTOR
    __interrupt void Timer_A1 (void)
    {
        timeCounter++;  // overflow of Timer_A1,  i.e. count events of 10 ms
        
        //if( timeCounter %  100000L  == 0 )  REDLED_TGL; // 20210111  CCR0=20
        if( timeCounter %  34483L  == 0 )  REDLED_TGL;	  // new for CCRO=58
        //if( timeCounter %  100L  == 0 )  REDLED_TGL;
        //if( timeCounter %  100L  == 0 )  BEEPER_TGL;
        //BEEPER_TGL;		
             
    }// -------------------- Timer_A1 ISR ------------------------------------
    
    
    
    
    
    // #############################################################
    // ### Subroutines for   delay,  i.e. idle cycles of the CPU ###
    // #############################################################
    
    void delay_ms( unsigned int delay )
     {
            while (delay--)
            {
                //__delay_cycles( PUT_CPU_CLOCK_SPEED_IN_HZ_DIVIDED_BY_1000_HERE );
                __delay_cycles( CLOCKSPEED_MS );
            }
     }// -----------------------------------------------
    
    // #############################################################
    // @@@@@@@  Subroutines for the control of the RG-LED  #########
    // #############################################################
    
    void blinkGreen( int count )
    {
    	int j=999;
    	
    	if( (count > 0) && (count < 44) )
    	{
    		for( j=0; j< count; j++ )
    		{
    			GREENLED_ON	;						
    			delay_ms( BLINKDELAY );
    			GREENLED_OFF ;							
    			delay_ms( BLINKDELAY );
    		}//for j
    	}// if
    }// ---------------- blinkGreen ----------------------
    
    
    void blinkRed( int count )
    {
    	int j=999;
    	
    	if( (count > 0) && (count < 44) )
    	{
    		for( j=0; j< count; j++ )
    		{
    			REDLED_ON ;	
    			delay_ms( BLINKDELAY );
    			REDLED_OFF ;		
    			delay_ms( BLINKDELAY );
    		}//for j
    	}// if
    }// ---------------- blinkRed ----------------------
    
    
    
    
    // #############################################################
    // ####### Subroutines for the measurements with ADC10 #########
    // #############################################################
    
    // measure the  voltage at input A3  ( pin 1.3 )
    void measureVbat()
    {
    	long int value;
    	
    	ADC10CTL0 &= ~ENC;						// ADC10 disabled
    	
      	ADC10CTL1 = INCH_3;                     // input A3
      	ADC10AE0 = BIT3;                        // PA.3 ADC option select
    	ADC10CTL0 |=  ADC10IE;     				// interrupt enabled	
    
    
    	GREENLED_ON;							// <=== monitoring !
    
    	//__bis_SR_register(GIE); 				// Enable global interrupts
    	__bic_SR_register(GIE); 				// Disable interrupts to close the window; 
    											// 20201218 ;  Bruce McKenney47378
    	
    	ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit from LPM0,  
        										// General Interrupt Enabled !   (open the window)
        										
    	GREENLED_OFF;							// <=== monitoring !
    
    
    	value = (long int)ADC10MEM;
    	
    	value = value * VBAT_FACTOR / 1000;
    	
    	Vbat = (unsigned int)value;
    	
      	ADC10CTL1 &= ~INCH_3;                   // input A3   off
      	ADC10AE0 &= ~BIT3;                      // PA.3 ADC option select  off
    	
    	ADC10CTL0 &= ~ENC;						// ADC10 disabled !!!!!!!!!!
    } // ------------------------- measureVbat ------------------------------------------
    
     
    

     

  • Your timer ISR is dominated by that modulo operation. I wrote a little program to time unsigned-long modulo and I measured between 408 and 424 clocks (there's some data-dependence in the arithmetic). I usually estimate around 20 clocks to get into/out of an ISR, so your timer ISR takes around 428-444 clocks, which is (ID=3) 53-56 timer ticks.

    Result: At something like CCR0=55 the timer ISR completely takes over the CPU and main doesn't make any progress. When I paused the program while the P1.6 LED was on solid, I saw main in LPM0 (CPUOFF) with GIE=1, and ADC10IFG=1. It was not stuck, it was just extremely slow. I set a breakpoint at the ADC ISR and didn't get there for maybe a minute.

    Even when the P1.6 LED is on solid, it doesn't really stay hung. After a minute or two the LED starts blinking again -- for a while anyway. I watched 7-8 cycles like this. I imagine this is due to the data dependence in the division operation.

    Summary: I'm not sure this is the test you intended, but even under these unusual circumstances the guard still worked.

    [Edit small re-wording for clarity]

  • Hello Bruce,

    I'm very impressed by your thorough analysis and results !  Thanks a lot !

    I was not aware, that the modulo operation on long integers takes so many clocks.  :-o

    Since it is only for observation of the Timer1 activity,  it could also be changed to

    right shift by 7  (i.e. %128) , left shift by 7, and subtraction...

    In some paper I read  that it is best style to keep code in ISRs  very small in order

    to get a fast reaction - this was unfortunatelly not the case here ...

    Yes, I could successfully reproduce ,  that the system was running ok with P1.0(red) blinking always

    and P1.6(green)  leaving the continuous state and blinking further.  It is now clear to me that the slow

    blinking P1.0(red) is a proof for CPU_ON, because the Timer-ISR is always running .

    However, I think   it is a contradiction  that you could see LPM0 (CPUOFF)

    during  Pause  (  P1.6(green) ON )   with modulo calculation running ok all the time...

    How does this all  fit  together ?   Is it  possible that the ADC  finished operation,

    made the CPU ON again   but  the ADC interrupt was waiting because of lower priority ?

    Summary:

    - yes, the test is as i intended.

    - yes, the "guard statement" is absolutely neccessary !

    Thanks again & 1024 greetings, Uli

  • I saw CPUOFF=1 and GIE=1 in the stacked SR (actual value 0x001C), while the timer ISR was running. (I also checked the stacked PC, which pointed just past the CPUOFF-setting instruction.)

    Seeing (stacked) GIE=1 with ADC10IFG=1 said that the ADC had finished but the (ADC) interrupt hadn't been presented. CPUOFF=1 told me that main had been able to complete the CPUOFF-setting instruction, which is the end of the critical sequence. Supporting evidence was that when I then set a breakpoint in the ADC ISR, it took a very long time to reach there, meaning that lower-priority interrupts were being only rarely serviced. (I actually did this twice, to be sure I wasn't hallucinating.)

    One subtlety I should probably point out: If you look at the code generated for __disable_interrupt(), you'll see it sets GIE=0 and then does a NOP. In the general case, you want the NOP since interrupts aren't completely disabled until some time during the Next instruction. In this case, omitting the NOP was OK since the critical sequence didn't really start until the SC was set during the next instruction (it also helped make the exposition a little more clear). I imagine there are alternate sequences where this might matter.

    [Edit: Minor clarification.]

  • Hello Bruce,  thank you for your patience regarding this complex device behavior !

    In the third paragraph above is written "SC" ... Do you mean SR, PC or SP   there ?

    Regarding the modulo operation I tried  %32768  (2^15)  instead of  % 34483 ... and the program length is reduced from 900 to 862  and runs very fast now !  Observing the LEDs (red P10,  green P16)   we can see  that the Timer1 ISR is not dominating any longer.  Now the ADC10 ISR is very active.

    Looping the TA1CCR0 from 64 downto 10   is running much faster than before. The modulo operation is changed by the compiler to shifting and subtraction,  I suppose.  There is no longer any continuous P16 signal  compared to the previous demo programs.

    Thanks again & best regards, Uli

  • > In the third paragraph above is written "SC" ... Do you mean SR, PC or SP   there ?

    I was referring to ADC10SC. Interrupting before ADC10SC is set is OK since it's outside the critical sequence.

  • Thank you !  That clear now.

    Meanwhile I updated the ISR  with modulo  %128    and set the TA1CCR0 = 15625 ;

    this works fine and the ADC10 runs quite fast !

    Best regards, Uli

**Attention** This is a public forum