I started experimenting with the ADC on a SAMD21, and strangely, the result I get is always 0
.
I initialized it like this (using the temperature sensor to get some values without external hardware):
SYSCTRL->VREF.bit.TSEN = 1; // enable temperature sensor
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | GCLK_CLKCTRL_ID_ADC; // base clock GCLK2 = 1MHz
PM->APBCMASK.bit.ADC_ = 1; // Enable bus clock
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val; // internal 1V reference
ADC->AVGCTRL.bit.SAMPLENUM = ADC_AVGCTRL_SAMPLENUM_4_Val; // average from 4 samples
ADC->SAMPCTRL.bit.SAMPLEN = 49; // for approx. 100 us/sample with a 4x prescaler on the 1MHz clock
ADC->CTRLB.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV4_Val;
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val; // to support averaging
ADC->CTRLB.bit.FREERUN = 1;
ADC->INPUTCTRL.bit.MUXPOS = 0x18; // temperature sensor
ADC->INTENSET.bit.RESRDY = 1; // result ready interrupt
ADC->CTRLA.bit.ENABLE = 1;
NVIC_EnableIRQ( ADC_IRQn );
I checked that the clocks are working as they should, and made sure the ADC interrupts get regularly triggered, by toggling a portpin in the ADC interrupt.
After that, I tried reading some values.
volatile int debug_adc[201];
int debug_adc_c = 0;
void ADC_Handler()
{
volatile int result = ADC->RESULT.bit.RESULT;
debug_adc[debug_adc_c++] = result;
if (debug_adc_c >= 200)
{
debug_adc_c = 0;
}
}
I let it run for a while, and when I stop it, the array is full of zeroes, and the RESULT register in the I/O view is also zero.
However, if I place a breakpoint inside the interrupt handler, I see non-zero values, both in the array in the Watch screen and in the I/O view.
I don't touch the ADC anywhere else in my code. I also tried it without averaging (ADC->AVGCTRL.bit.SAMPLENUM = 0
), nothing changed.
The datasheet recommends performing one manual conversion at the beginning, even if I use the freerunnig mode: ADC->SWTRIG.bit.START = 1;
. I did it, but it didn't change anything.
Did I miss something obvious?
Best Answer
The ADC has both negative and positive inputs, but it seems it is not just merely an optional extra feature. I have to specify a negative input, even if I only use the positive one. (the datasheet is a little unclear here, it only mentions both inputs being used in the calculation if I choose differential input in CTRLB.DIFFMODE, which wasn't the case here)
After setting the negative input to the internal ground,
everything works as expected.
I still don't understand why it was working when a breakpoint was set, but at least the code works as it should.