Electronics > Microcontrollers

SAMD21g18: ADC with interrupts only reads once

(1/1)

reneman:
Hello,

I'm trying to implement ADC With interrupts in my samd21g18 so an interrupt is triggered everytime a new result is ready to be read. I've got the interrupt handler configured correctly, and If I put something like an uart_puts inside the handler, it works, and prints the given text indefinitely. However, if I try to read the value of the ADC inside the ISR, it will only work once or sometimes twice.


 Here's the ISR:

--- Code: ---void irq_handler_adc(void)
  {

    ADC->INTFLAG.reg = ADC_INTENSET_RESRDY; /* Clear the data ready flag. */
   
   
    // Wait for synchronization before reading RESULT

    while(ADC->STATUS.bit.SYNCBUSY == 1);
    uint32_t result = ADC->RESULT.reg; /* Read the value. */


    char message[32];
    sprintf(message,"ADC: %d\r\n", result);
    GHal.uart_puts(message);
   
  }

--- End code ---

I believe the problem is in the flag clearing (interrupt acknowledging?). I've seen some people use ADC_INTENSET_RESRDY, while others use ADC_INTFLAG_RESRDY;

UPDATE: I've run some tests modifying the ISR. Commenting the DC->INTFLAG.reg = ADC_INTENSET_RESRDY; line, and leaving the uint32_t result = ADC->RESULT.reg; produce the same issue. Similarly, commenting the uint32_t result = ADC->RESULT.reg; line, and uncommenting the flag clear, produces the issue. So both reading the result of the ADC, and clearing the interrupt flag, produce the issue on their own.

And here's my ADC Set-up code:


--- Code: ---void ADCIntInit()
{

      //1.Enable the bus clock (APB) to the ADC.
      PM->APBCMASK.reg |= PM_APBCMASK_ADC;

      //2.Connect a peripheral clock to the ADC.
      /* Enable GCLK1 for the ADC */
      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_ADC;
      /* Wait for bus synchronization. */
      while (GCLK->STATUS.bit.SYNCBUSY) {};
      //Ya podemos hablar con el ADC


      //3.Load the ADC's calibration values.
      uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
      uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
      linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
      while (ADC->STATUS.bit.SYNCBUSY) {};
      /* Write the calibration data. */
      ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);


      //4.Configure the measurement parameters.
      while (ADC->STATUS.bit.SYNCBUSY) {};

      /* Use the internal VCC reference. This is 1/2 of what's on VCCA.
        since VCCA is typically 3.3v, this is 1.65v.
      */
      ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;

      /* Only capture one sample. The ADC can actually capture and average multiple
        samples for better accuracy, but there's no need to do that for this
        example.
      */
      ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1;

      /* Set the clock prescaler to 512, which will run the ADC at
        8 Mhz / 512 = 31.25 kHz.
        Set the resolution to 12bit.
      */
      ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV4 |
                      ADC_CTRLB_RESSEL_12BIT;

      /* Configure the input parameters.

        - GAIN_DIV2 means that the input voltage is halved. This is important
          because the voltage reference is 1/2 of VCCA. So if you want to
          measure 0-3.3v, you need to halve the input as well.

        - MUXNEG_GND means that the ADC should compare the input value to GND.

        - MUXPOST_PIN3 means that the ADC should read from AIN3, or PB09.
          This is A2 on the Feather M0 board.
      */
      ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN_DIV2 |
                          ADC_INPUTCTRL_MUXNEG_GND |
                          ADC_INPUTCTRL_MUXPOS_PIN3;


      //5. Configuramos el pin que usaremos en el ADC
      // Set PB09 as an input pin.
      PORT->Group[1].DIRCLR.reg = PORT_PB09;

      // Enable the peripheral multiplexer for PB09.
      PORT->Group[1].PINCFG[9].reg |= PORT_PINCFG_PMUXEN;

      // Set PB09 to function B which is analog input.
      PORT->Group[1].PMUX[4].reg = PORT_PMUX_PMUXO_B;



      // Wait for bus synchronization.
      while (ADC->STATUS.bit.SYNCBUSY) {};


      //6. Enable interrupts

      //ADC->INTENCLR.reg = ADC_INTENCLR_RESRDY;
      ADC->INTENSET.reg |= ADC_INTENSET_RESRDY;
      while(ADC->STATUS.bit.SYNCBUSY); //wait for sync

      //free run mode
      ADC->SWTRIG.reg |= ADC_SWTRIG_START;
      while(ADC->STATUS.bit.SYNCBUSY); //wait for sync

      NVIC_EnableIRQ(ADC_IRQn);
      NVIC_SetPriority(ADC_IRQn, 3);

     
     

      //7. Activamos el ADC
 
      // Enable the ADC.
      ADC->CTRLA.bit.ENABLE = true;
     
}

--- End code ---

I'm setting up the ADC in free running mode as well, but I'm not sure if that's required.

Here are some 2 example reads like I mention at the beggining:



Thank you.

PS: Additional question. Is there anything like AVR's ADC Autotrigger for the samd21, or is that not needed as the sam's ADC is much faster?

ataradov:
You are not configuring free-running mode, you are just starting a single conversion. To configure free-running mode you need to set CTRLB.FREERUN bit.

Also, you can't print things from the interrupt handler, printing takes a lot of time, all your interrupts will just overrun each other.

reneman:
That worked.
Thank you

Navigation

[0] Message Index

There was an error while thanking
Thanking...
Go to full version