SAMD21g18: ADC with interrupts only reads once - Page 1
Forum section: electronics / general

author: reneman
author_rank: Contributor
timestamp: July 23, 2021, 09:38:36 am
content: 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 RESULTwhile(ADC->STATUS.bit.SYNCBUSY == 1);uint32_t result = ADC->RESULT.reg; /* Read the value. */char message;sprintf(message,"ADC: %d\r\n", result);GHal.uart_puts(message);}I believe the problem is in the flag clearing (interrupt acknowledging?). I've seensome people use ADC_INTENSET_RESRDY, whileothersuse 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 multiplesamples for better accuracy, but there's no need to do that for thisexample.*/ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1;/* Set the clock prescaler to 512, which will run the ADC at8 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 importantbecause the voltage reference is 1/2 of VCCA. So if you want tomeasure 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.DIRCLR.reg = PORT_PB09;// Enable the peripheral multiplexer for PB09.PORT->Group.PINCFG.reg |= PORT_PINCFG_PMUXEN;// Set PB09 to function B which is analog input.PORT->Group.PMUX.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 modeADC->SWTRIG.reg |= ADC_SWTRIG_START;while(ADC->STATUS.bit.SYNCBUSY); //wait for syncNVIC_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?

author: ataradov
author_rank: Super Contributor
timestamp: July 23, 2021, 04:14:23 pm
content: 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.

author: reneman
author_rank: Contributor
timestamp: July 26, 2021, 12:40:11 pm
content: That worked.Thank you