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:
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);
}
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:
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;
}
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?