Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 53 additions & 94 deletions drivers/adc/adc_sam0.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,8 @@ struct adc_sam0_cfg {

static void wait_synchronization(Adc *const adc)
{
#if defined(ADC_SYNCBUSY_MASK)
while ((adc->SYNCBUSY.reg & ADC_SYNCBUSY_MASK) != 0) {
while ((ADC_SYNC(adc) & ADC_SYNC_MASK) != 0) {
}
#else
while ((adc->STATUS.reg & ADC_STATUS_SYNCBUSY) != 0) {
}
#endif
}

static int adc_sam0_acquisition_to_clocks(const struct device *dev,
Expand Down Expand Up @@ -120,7 +115,7 @@ static int adc_sam0_channel_setup(const struct device *dev,
const struct adc_sam0_cfg *const cfg = dev->config;
Adc *const adc = cfg->regs;
int retval;
uint8_t SAMPCTRL = 0;
uint8_t sampctrl = 0;

if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
retval = adc_sam0_acquisition_to_clocks(dev,
Expand All @@ -130,51 +125,41 @@ static int adc_sam0_channel_setup(const struct device *dev,
return retval;
}

SAMPCTRL |= ADC_SAMPCTRL_SAMPLEN(retval);
sampctrl |= ADC_SAMPCTRL_SAMPLEN(retval);
}

adc->SAMPCTRL.reg = SAMPCTRL;
adc->SAMPCTRL.reg = sampctrl;
wait_synchronization(adc);


uint8_t REFCTRL;
uint8_t refctrl;

switch (channel_cfg->reference) {
case ADC_REF_INTERNAL:
#ifdef ADC_REFCTRL_REFSEL_INTREF
REFCTRL = ADC_REFCTRL_REFSEL_INTREF | ADC_REFCTRL_REFCOMP;
/* Enable the internal reference, defaulting to 1V */
SUPC->VREF.bit.VREFOE = 1;
#else
REFCTRL = ADC_REFCTRL_REFSEL_INT1V | ADC_REFCTRL_REFCOMP;
refctrl = ADC_REFCTRL_REFSEL_INTERNAL | ADC_REFCTRL_REFCOMP;
/* Enable the internal bandgap reference */
SYSCTRL->VREF.bit.BGOUTEN = 1;
#endif
ADC_BGEN = 1;
break;
case ADC_REF_VDD_1_2:
#ifdef ADC_REFCTRL_REFSEL_INTVCC0
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC0 | ADC_REFCTRL_REFCOMP;
#else
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC1 | ADC_REFCTRL_REFCOMP;
#endif
refctrl = ADC_REFCTRL_REFSEL_VDD_1_2 | ADC_REFCTRL_REFCOMP;
break;
#ifdef ADC_REFCTRL_REFSEL_INTVCC1
#ifdef ADC_REFCTRL_REFSEL_VDD_1
case ADC_REF_VDD_1:
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC1 | ADC_REFCTRL_REFCOMP;
refctrl = ADC_REFCTRL_REFSEL_VDD_1 | ADC_REFCTRL_REFCOMP;
break;
#endif
case ADC_REF_EXTERNAL0:
REFCTRL = ADC_REFCTRL_REFSEL_AREFA;
refctrl = ADC_REFCTRL_REFSEL_AREFA;
break;
case ADC_REF_EXTERNAL1:
REFCTRL = ADC_REFCTRL_REFSEL_AREFB;
refctrl = ADC_REFCTRL_REFSEL_AREFB;
break;
default:
LOG_ERR("Selected reference is not valid");
return -EINVAL;
}
if (adc->REFCTRL.reg != REFCTRL) {
adc->REFCTRL.reg = REFCTRL;
if (adc->REFCTRL.reg != refctrl) {
adc->REFCTRL.reg = refctrl;
wait_synchronization(adc);
#ifdef ADC_SAM0_REFERENCE_GLITCH
struct adc_sam0_data *data = dev->data;
Expand All @@ -184,89 +169,78 @@ static int adc_sam0_channel_setup(const struct device *dev,
}


uint32_t INPUTCTRL = 0;
uint32_t inputctrl = 0;

switch (channel_cfg->gain) {
case ADC_GAIN_1:
#ifdef ADC_INPUTCTRL_GAIN_1X
INPUTCTRL = ADC_INPUTCTRL_GAIN_1X;
inputctrl = ADC_INPUTCTRL_GAIN_1X;
#endif
break;
#ifdef ADC_INPUTCTRL_GAIN_DIV2
case ADC_GAIN_1_2:
INPUTCTRL = ADC_INPUTCTRL_GAIN_DIV2;
inputctrl = ADC_INPUTCTRL_GAIN_DIV2;
break;
#endif
#ifdef ADC_INPUTCTRL_GAIN_2X
case ADC_GAIN_2:
INPUTCTRL = ADC_INPUTCTRL_GAIN_2X;
inputctrl = ADC_INPUTCTRL_GAIN_2X;
break;
#endif
#ifdef ADC_INPUTCTRL_GAIN_4X
case ADC_GAIN_4:
INPUTCTRL = ADC_INPUTCTRL_GAIN_4X;
inputctrl = ADC_INPUTCTRL_GAIN_4X;
break;
#endif
#ifdef ADC_INPUTCTRL_GAIN_8X
case ADC_GAIN_8:
INPUTCTRL = ADC_INPUTCTRL_GAIN_8X;
inputctrl = ADC_INPUTCTRL_GAIN_8X;
break;
#endif
#ifdef ADC_INPUTCTRL_GAIN_16X
case ADC_GAIN_16:
INPUTCTRL = ADC_INPUTCTRL_GAIN_16X;
inputctrl = ADC_INPUTCTRL_GAIN_16X;
break;
#endif
default:
LOG_ERR("Selected ADC gain is not valid");
return -EINVAL;
}

INPUTCTRL |= ADC_INPUTCTRL_MUXPOS(channel_cfg->input_positive);
inputctrl |= ADC_INPUTCTRL_MUXPOS(channel_cfg->input_positive);
if (channel_cfg->differential) {
INPUTCTRL |= ADC_INPUTCTRL_MUXNEG(channel_cfg->input_negative);
inputctrl |= ADC_INPUTCTRL_MUXNEG(channel_cfg->input_negative);

#ifdef ADC_INPUTCTRL_DIFFMODE
INPUTCTRL |= ADC_INPUTCTRL_DIFFMODE;
#else
adc->CTRLB.bit.DIFFMODE = 1;
wait_synchronization(adc);
#endif
ADC_DIFF(adc) |= ADC_DIFF_MASK;
} else {
INPUTCTRL |= ADC_INPUTCTRL_MUXNEG_GND;
inputctrl |= ADC_INPUTCTRL_MUXNEG_GND;

#ifndef ADC_INPUTCTRL_DIFFMODE
adc->CTRLB.bit.DIFFMODE = 0;
wait_synchronization(adc);
#endif
ADC_DIFF(adc) &= ~ADC_DIFF_MASK;
}
wait_synchronization(adc);

adc->INPUTCTRL.reg = INPUTCTRL;
adc->INPUTCTRL.reg = inputctrl;
wait_synchronization(adc);

/* Enable references if they're selected */
switch (channel_cfg->input_positive) {
#ifdef ADC_INPUTCTRL_MUXPOS_TEMP_Val
case ADC_INPUTCTRL_MUXPOS_TEMP_Val:
SYSCTRL->VREF.bit.TSEN = 1;
ADC_TSEN = 1;
break;
#endif
#ifdef ADC_INPUTCTRL_MUXPOS_PTAT_Val
case ADC_INPUTCTRL_MUXPOS_PTAT_Val:
SUPC->VREF.bit.TSEN = 1;
ADC_TSEN = 1;
break;
#endif
#ifdef ADC_INPUTCTRL_MUXPOS_CTAT_Val
case ADC_INPUTCTRL_MUXPOS_CTAT_Val:
SUPC->VREF.bit.TSEN = 1;
ADC_TSEN = 1;
break;
#endif
case ADC_INPUTCTRL_MUXPOS_BANDGAP_Val:
#ifdef ADC_REFCTRL_REFSEL_INTREF
SUPC->VREF.bit.VREFOE = 1;
#else
SYSCTRL->VREF.bit.BGOUTEN = 1;
#endif
ADC_BGEN = 1;
break;
default:
break;
Expand Down Expand Up @@ -361,23 +335,22 @@ static int start_read(const struct device *dev,
return -EINVAL;
}

adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val;
ADC_RESSEL(adc) = ADC_RESSEL_8BIT;
break;
case 10:
if (sequence->oversampling) {
LOG_ERR("Oversampling requires 12 bit resolution");
return -EINVAL;
}

adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
ADC_RESSEL(adc) = ADC_RESSEL_10BIT;
break;
case 12:
if (sequence->oversampling) {
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
ADC_RESSEL(adc) = ADC_RESSEL_16BIT;
} else {
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val;
ADC_RESSEL(adc) = ADC_RESSEL_12BIT;
}

break;
default:
LOG_ERR("ADC resolution value %d is not valid",
Expand All @@ -387,7 +360,15 @@ static int start_read(const struct device *dev,

wait_synchronization(adc);

if (sequence->channels != 1U) {
if ((sequence->channels == 0)
|| ((sequence->channels & (sequence->channels - 1)) != 0)) {
/* The caller is expected to identify a single input channel, which will
* typically be the positive input, though no check is made for this...
*
* While ensuring that the channels bitfield matches the positive input
* might be sensible, this will likely break users before this revision
* was put in place.
*/
LOG_ERR("Channel scanning is not supported");
return -ENOTSUP;
}
Expand Down Expand Up @@ -465,11 +446,7 @@ static int adc_sam0_init(const struct device *dev)
GCLK->CLKCTRL.reg = cfg->gclk | GCLK_CLKCTRL_CLKEN;
#endif

#ifdef ADC_CTRLA_PRESCALER_Pos
adc->CTRLA.reg = cfg->prescaler;
#else
adc->CTRLB.reg = cfg->prescaler;
#endif
ADC_PRESCALER(adc) = cfg->prescaler;
wait_synchronization(adc);

adc->INTENCLR.reg = ADC_INTENCLR_MASK;
Expand Down Expand Up @@ -524,43 +501,25 @@ static const struct adc_driver_api adc_sam0_api = {
.gclk_mask = UTIL_CAT(GCLK_PCHCTRL_GEN_GCLK, \
DT_INST_PROP(n, gclk)), \
.gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, periph_ch), \
.prescaler = UTIL_CAT(ADC_CTRLA_PRESCALER_DIV, \
DT_INST_PROP(n, prescaler)),

#define ADC_SAM0_BIASCOMP_SHIFT(n) \
(ADC0_FUSES_BIASCOMP_Pos + DT_INST_PROP(n, calib_offset))
#define ADC_SAM0_BIASCOMP(n) \
(((*(uint32_t *)NVMCTRL_SW0) >> ADC_SAM0_BIASCOMP_SHIFT(n)) & 0x7)

#define ADC_SAM0_BIASR2R_SHIFT(n) \
(ADC0_FUSES_BIASR2R_Pos + DT_INST_PROP(n, calib_offset))
#define ADC_SAM0_BIASR2R(n) \
(((*(uint32_t *)NVMCTRL_SW0) >> ADC_SAM0_BIASR2R_SHIFT(n)) & 0x7)

#define ADC_SAM0_BIASREFBUF_SHIFT(n) \
(ADC0_FUSES_BIASREFBUF_Pos + DT_INST_PROP(n, calib_offset))
#define ADC_SAM0_BIASREFBUF(n) \
(((*(uint32_t *)NVMCTRL_SW0) >> ADC_SAM0_BIASREFBUF_SHIFT(n)) & 0x7)
.prescaler = UTIL_CAT(ADC_CTRLx_PRESCALER_DIV, \
UTIL_CAT(DT_INST_PROP(n, prescaler), _Val)),

#define ADC_SAM0_CONFIGURE(n) \
do { \
const struct adc_sam0_cfg *const cfg = dev->config; \
Adc * const adc = cfg->regs; \
uint32_t comp = ADC_SAM0_BIASCOMP(n); \
uint32_t r2r = ADC_SAM0_BIASR2R(n); \
uint32_t rbuf = ADC_SAM0_BIASREFBUF(n); \
adc->CALIB.reg = ADC_CALIB_BIASCOMP(comp) | \
ADC_CALIB_BIASR2R(r2r) | \
ADC_CALIB_BIASREFBUF(rbuf); \
adc->CALIB.reg = ADC_SAM0_BIASCOMP(n) \
| ADC_SAM0_BIASR2R(n) \
| ADC_SAM0_BIASREFBUF(n); \
} while (0)

#else

#define ADC_SAM0_CLOCK_CONTROL(n) \
.gclk = UTIL_CAT(GCLK_CLKCTRL_GEN_GCLK, DT_INST_PROP(n, gclk)) |\
GCLK_CLKCTRL_ID_ADC, \
.prescaler = UTIL_CAT(ADC_CTRLB_PRESCALER_DIV, \
DT_INST_PROP(n, prescaler)), \
.prescaler = UTIL_CAT(ADC_CTRLx_PRESCALER_DIV, \
UTIL_CAT(DT_INST_PROP(n, prescaler), _Val)),

#define ADC_SAM0_CONFIGURE(n) \
do { \
Expand Down
12 changes: 12 additions & 0 deletions samples/drivers/adc/boards/atsamd21_xpro.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2021 Argentum Systems Ltd.
*/

/ {
zephyr,user {
/* EXT-1, pin 3 ADC(+) */
io-channels = <&adc 8>;
};
};
11 changes: 8 additions & 3 deletions samples/drivers/adc/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
#define ADC_REFERENCE ADC_REF_INTERNAL
#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT

#ifdef CONFIG_ADC_NRFX_SAADC
#define ADC_INPUT_POS_OFFSET SAADC_CH_PSELP_PSELP_AnalogInput0
#else
#define ADC_INPUT_POS_OFFSET 0
#endif

/* Get the numbers of up to two channels */
static uint8_t channel_ids[ADC_NUM_CHANNELS] = {
DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 0),
Expand Down Expand Up @@ -76,9 +82,8 @@ void main(void)
*/
for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) {
channel_cfg.channel_id = channel_ids[i];
#ifdef CONFIG_ADC_NRFX_SAADC
channel_cfg.input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0
+ channel_ids[i];
#ifdef CONFIG_ADC_CONFIGURABLE_INPUTS
channel_cfg.input_positive = ADC_INPUT_POS_OFFSET + channel_ids[i];
#endif

adc_channel_setup(dev_adc, &channel_cfg);
Expand Down
Loading