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.

ADC12_ISR not triggered on F5529

Expert 1140 points

Guys,

I've spent literally 5 hours trying to figure out why my code doesn't work on F5529. It seems that even though I start the ADC12 conversion the ISR isn't triggered and the CPU won't wake up from the LPM. Code snippets are below:

[here set the timer to 0 and call the get_adc_data(), ADC reading to be done on A0, P6.0, ref to AVcc, single samples in the software loop]

int get_adc_data()
{
	int adcVal[AS_NUM_OF_ADC_READS];
	int i, adcValAvgPrev, adcValAvg;

	P6SEL |= BIT0;
	ADC12CTL0 = ADC12ON + ADC12SHT02;
	ADC12IE = 0x01;
	ADC12CTL1 = ADC12CONSEQ_0;
	ADC12CTL2 |= ADC12RES_1;					// 10-bit resolution
	ADC12MCTL0 |= ADC12INCH_0 + ADC12SREF_0;			// V(R+) = AVCC and V(R-) = AVSS
	ADC12CTL0 |= ADC12ENC;

	for (i = 0; i < AS_NUM_OF_ADC_READS; i++)
	{
		uart_puts("ADC before LPM\n\r");
		ADC12IE |= 0x01;

		__bis_status_register(LPM0_bits + GIE);

		uart_puts("ADC after LPM\n\r");

		adcVal[i] = ADC12MEM0;

		adcValAvgPrev = !i ? adcVal[i] : adcValAvg;	// if it's the first iteration then adcValAvgPrev = adcVal[0]

		adcValAvg = adcValAvgPrev + ((adcVal[i] - adcValAvgPrev) / i);
	}
	ADC12CTL0 &= ~ADC12ENC;

	return adcValAvg;
}
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
{
       __bic_status_register_on_exit(CPUOFF);
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0(void)
{
      if (!(++timer_ms % 5000))
      {
            am_probe_finished_flag = 1;
            __bic_status_register_on_exit(CPUOFF);
       }
}

Ok, so the TIMER0_A0_VECTOR wakes up the CPU every 5 seconds and it indeed does. But before this time I expect the ADC12_ISR to wake up the CPU. In fact what I see is "ADC before LPM" then 5 seconds of break and finally "ADC after LPM". It should do 10 readings one after another within milliseconds.
The code was ported from G2553 where it worked perfectly, although I can see that ADC12 is way different than ADC10.

I know that multiple readings can be done better with ADC12MEMx, but this is not a problem here, I will fix it once I have ISR being triggered. Can you please take a short look to see what may be wrong? I have no idea...

 

Best Regards,

tml


  • I can't find start conversion in your code. Try this:

    uart_puts("ADC before LPM\n\r");
    ADC12CTL0 |= ADC12ENC | ADC12SC; /// ADC12IE |= 0x01;

  • Thank you for your response!

    Unfortunately it didn't help. I indeed lost this in my code but in the meantime I hade the ADC12SC in place and it still didn't work. I added it again and still the ADC12_ISR does not seem to be triggered :(

  • I wonder why you see the 'ADC after LPM' message at all.
    If the ADC triggers an interrupt ad your ADC ISR is called, it will exit LPM. However, it foes not clear the interrutp (you don't read ADC10MEM in your ISR or clear the ADC10IFG0 bit). So the ISR will be called again immediately after exiting, and main() cannto continue (only the timer ISR will interrupt this eternal loop).

    It is possible too (I never tested or checked) that the CPU will execute one single instruction between each ISR call. In this case, main would very slooowly continue to execute to the point where the 'ADC after LPM' message output is started (which then will happen fast, if it is interrupt driven with higher priority than ADC interrupt). And the 5s delay until then is pure coincidence.

    Clear the ADC10IFG0 bit or the ADC10IE0 bit inside your ISR, or do an __bic_SR_register_on_exit(CPUOFF|GIE); to prevent further interrupts until you have read ADC12MEM0.

    Also, when you got a result, you should deactivate the timer timeout somehow. Else sooner or later the timer will exit LPM (if you're in it at all at this moment) and flag a timeout, even if you are waiting for something else in LPM somewhere else in your code.

  • Hi,

    I finally found the issue. The key to the success is:

     28.2.10.1 ADC12IV, Interrupt Vector Generator of SLAU208M:

    "ADC12IFGx bits are reset automatically by accessing their associated ADC12MEMx register or may be reset with software." - I somehow missed this important thing when reading about ADC12.
     
    To fix the code I can either:
    - disable the interrupts on ISR exit in the ADC12 ISR (__bic_status_register_on_exit(CPUOFF + GIE)) while not accessing ADC12MEMx inside ISR
    or
    - access the ADC12MEMx inside the ISR which will clear the ADC12IFGx flag and will not cause the program to go into ISR again.
    I guess that the CPU stuck in the ISR. Why the TimerA0 ISR caused the program to continue just before  uart_puts("ADC after LPM\n\r") I don't know (yet).
    I hope that will help others to save some time (and get some sleep instead of finding the issue).
    Best Regards,
    tml
  • tml said:
    "ADC12IFGx bits are reset automatically by accessing their associated ADC12MEMx register or may be reset with software."

    This is common to all MSP interrupts. IIRC, there are only two exceptions: the timer CCR0 interrupts (which have only one interrupt source and the CCR0.CCIFG bit is auto-cleared when the ISR is started) and the ADC10 interrupt (same reason).
    All others require either manually resetting the bit or (whre it applies) handling the interrupt cause (like reading a result register etc.)
    On newer MSPs, there's a third option: the IV register. Reading it will return the enumerated number of the highest-priority pending interrupt, while at the same time clearing the associated IFG bit.

**Attention** This is a public forum