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.

Problem using ADC with PWM

Other Parts Discussed in Thread: MSP430F5529

Hi, All,

I am trying to use ADC module of the micro-controller to decode the incoming signal. My input signal will be an OOK modulated signal of 125 kHz frequency. I need to band-pass sample  the incoming sequence with 55 kHz.

 I have read through the user guide and the forums and figured out that it would be good if i use a PWM signal with 55 kHz to start the ADC conversion. The idea is to use timer with comparator with interrupt to generate PWM signal and then when the interrupt occurs, the ADC conversion should triger the SHI signal.

I have  written a piece of code which should have work okay but it seems not to be.

What I am doing:

  1. I am using MSP430F5529 mcu and CCS with Version: 5.5.0.00077.
  2. I am using ADC12_A and TImer A0 to do this task.
  3. For the ADC :
    1. I am using channel A0 of the ADC with 5 MHz clock provided by ADC12OSC.
    2. I am using the internal voltage reference generator with Vr+ =2.5V and Vr-=0;
    3. I want the ADC to run in single channel single conversion mode.
    4. Also I am using the ADC in pulse sample mode.
    5. code to configure the ADC:
    6. void configureAdc12A(void)
      {
      volatile unsigned int i;
      P6SEL |= 0x01; // Enable A/D channel A0
      REFCTL0 &= ~REFMSTR; // Reset REFMSTR to hand over control to  ADC12_A ref control egisters
      ADC12CTL0 = ADC12ON+ADC12SHT00+ADC12REFON+ADC12REF2_5V;// Turn on ADC12, Sampling tim (0001b = 8 cycles)
      // On Reference Generator and set to 2.5V

      ADC12CTL1 = ADC12SHP; // Use sampling timer and clock frequency set to 5MHz
      ADC12MCTL0 = ADC12SREF_1; // Vr+=Vref+ and Vr-=AVss

      for ( i=0; i<0x30; i++); // Delay for reference start-up

      ADC12CTL0 |= ADC12ENC; // Enable conversions

      }

  1. For the PWM:
I am using timer A0 and comparator to generate 55 kHz PWM signal. I have tested the output pin and it seems working perfectly fine. Following is the code I am using.
  1. void initTimers(void) {

    // Frequency of timer_A is 1MHz. // Generate PWM waveform with frequency 1MHz/18=55kHz and duty ratio 9/19 ~> 50% // TIMER_PERIOD = 18 // DUTYCYCLE = 9

    TIMER_A_clearTimerInterruptFlag(TIMER_A0_BASE);

    TIMER_A_configureUpMode( TIMER_A0_BASE, TIMER_A_CLOCKSOURCE_SMCLK, TIMER_A_CLOCKSOURCE_DIVIDER_1, TIMER_PERIOD, TIMER_A_TAIE_INTERRUPT_ENABLE, TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE, TIMER_A_DO_CLEAR );

    TIMER_A_startCounter( TIMER_A0_BASE, TIMER_A_UP_MODE );

    TIMER_A_setOutputForOutputModeOutBitValue(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, TIMER_A_OUTPUTMODE_OUTBITVALUE_LOW );

    TIMER_A_initCompare(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE, TIMER_A_OUTPUTMODE_RESET_SET, DUTYCYCLE);

  • Timer ISR code here:
    1. #pragma vector=TIMER0_A1_VECTOR
      __interrupt void TIMER0_A1_ISR(void)
      {
      //Any access, read or write, of the TAIV register automatically resets the
      //highest "pending" interrupt flag
      switch ( __even_in_range(TA0IV, 14) ) {
      case 0: break; //No interrupt
      case 2: break; //CCR1 not used
      case 4: //CCR2
      ADC12CTL0 |= ADC12SC; // Start conversion
      TIMER_A_clearTimerInterruptFlag(TIMER_A0_BASE);
      __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
      break;
      default: break;
      }
      }

My main file
void main (void)
{
    WDT_A_hold(WDT_A_BASE);						//Stop watchdog timer

	GPIO_setAsPeripheralModuleFunctionOutputPin( GPIO_PORT_P1, GPIO_PIN3 );
	GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN3 + GPIO_PIN2 );

    //Initialization
    configureAdc12A();
    initTimers();
while (1) { ADC12IFG &=~ADC12IFG0; // Clear Flag bits __bis_SR_register(LPM0_bits + GIE); // LPM0, Comparator_ISR will force exit
__no_operation(); while (!(ADC12IFG & BIT0)); SampledData[index] = ADC12MEM0; index++; } }

Issues I am facing:

 

  1. The first problem with the code is that after CPU enters the LPM0 mode the __bic_SR_register_on_exit(LPM0_bits) line does not force the CPU to exist. I have one breakpoint on line while (!(ADC12IFG & BIT0)) in the main  and one at ADC12CTL0 |= ADC12SC; in ISR. It always stop at the one in ISR and does not come back to the main loop. 

  2. Since the code was always in the ISR I tried to use the following lines of code inside the ISR and when I use the debugger to watch the SampledData[] values, It always show zero.
       while (!(ADC12IFG & BIT0));
       SampledData[index] = ADC12MEM0;
       index++;

Your Suggestion :

  • Break it up into pieces. I would start by toggling a I/o pin whenever the timer timer times out. Also, make sure that you don't mix up the adc conversion of the timer.

  • daniel johnson2 said:

    Break it up into pieces. I would start by toggling a I/o pin whenever the timer timer times out. Also, make sure that you don't mix up the adc conversion of the timer.

    Well, I did tried it in pieces first. I can see the PWM signal okay on the output pin, but the issue is that the timer ISR always active and the program does not return to main loop once it enters the timer ISR. 

    Some Success:


    Well Issue no 1 remain the same but there is some progress with issue 2. I figured out in the debugger mode that the variable "index" was not behaving the way it should be. I had declared it globally in the main as volatile unsigned int and then externed it to the timer_functions file. I tried to declare a different global variable in the timer_funciton and used it instead of index and it seem like the buffer is getting some values now. 

    Since the ADC is suppose to work at 55 KHz , if there is an input signal of 5 KHz, I should have 11 points for each cycle to verify the ADC is working perfectly fine. But it seems that ADC is converting some signal but not at the required sampling rate.

    The ISR code now looks like

    #pragma vector=TIMER0_A1_VECTOR
    __interrupt void TIMER0_A1_ISR(void)
    {
            //Any access, read or write, of the TAIV register automatically resets the
            //highest "pending" interrupt flag
            switch ( __even_in_range(TA0IV, 14) ) {
            case  0: break;                                 //No interrupt
            case  2: break;                                 //CCR1 not used
            case  4:										//CCR2
            	ADC12CTL0 |= ADC12SC;                     // Start conversion
            	TIMER_A_clearTimerInterruptFlag(TIMER_A0_BASE);
                while (!(ADC12IFG & BIT0));
                SampledData1[i] = ADC12MEM0;
                i++;
    
                if (i>100)
                {
                	__no_operation();     // Break point here... Just want to stop and see the samples
                	i = 0;
                }
            	__bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU  // still not working
            	break;
            case  6: break;                                 //CCR3 not used
            case  8: break;                                 //CCR4 not used
            case 10: break;                                 //CCR5 not used
            case 12: break;                                 //CCR6 not used
            case 14:
                    break;
            default: break;
            }
    }

  • It seems I could not explain the problem really well. I will split the issues and create new threads discussing them separately and hope get some attention.

  • For OOK, you don’t need to use the ADC at all.

    All you need is either a ‘carrier there/carrier not there’ detection, e.g. by a PLL, or an amplitude amplifier that puts the carrier, if there, into a TTL square signal.
    With an amplitude amplifier, you can use a timer capture interrupt to measure the duration of each signal period. A short period means ON, a long one means ‘was OFF and now is ON again’. How many ONs mean a ‘1’ bit and how long an OFF is per ‘0’ bit, depends on your application.

    Using a PLL, you only need to capture the time between PLL ‘locked’ signal transitions and divide it by the bit length to get the number of bits the PLL detect ON or OF state.

     If you still want to use the ADC and start it with the timer, then yes, configure the AC to run in repeated single conversion mode, pick the timer as trigger source, Set the SHP bit if you want to use the sampling timer (SHTx), or do not set it and control the sampling time with the PWM duty cycle. You can use DMA to copy the incoming data to RAM, or call the ISR for every ADC result.

    on more thing, I noticed you enable TAIE, bu tdo not do anything when it triggers the overflow interrupt. (default case)

**Attention** This is a public forum