samd: Add a vref=num option to the ADC and DAC constructor.

ADC: The argument of vref=num is an integer. Values for num are:

    SAMD21:
    0  INT1V   1.0V voltage reference
    1  INTVCC0 1/1.48 Analog voltage supply
    2  INTVCC1 1/2 Analog voltage supply (only for VDDANA > 2.0V)
    3  VREFA   External reference
    4  VREFB   External reference

    SAMD51:
    0  INTREF  internal bandgap reference
    1  INTVCC1 Analog voltage supply
    2  INTVCC0 1/2 Analog voltage supply (only for VDDANA > 2.0v)
    3  AREFA   External reference A
    4  AREFB   External reference B
    5  AREFC   External reference C (ADC1 only)

DAC: The argument of vref=num is an integer. Suitable values:

    SAMD21:
    0  INT1V   Internal voltage reference
    1  VDDANA  Analog voltage supply
    2  VREFA   External reference

    SAMD51:
    0  INTREF Internal bandgap reference
    1  VDDANA Analog voltage supply
    2  VREFAU Unbuffered external voltage reference (not buffered in DAC)
    4  VREFAB Buffered external voltage reference (buffered in DAC).
This commit is contained in:
robert-hh
2022-12-02 21:08:46 +01:00
committed by Damien George
parent a73dcb3d22
commit e69313f89c
4 changed files with 143 additions and 33 deletions

View File

@@ -2,3 +2,4 @@
#define MICROPY_HW_MCU_NAME "SAMD21G18A"
#define MICROPY_HW_XOSC32K (1)
#define MICROPY_HW_ADC_VREF (2)

View File

@@ -40,11 +40,40 @@ typedef struct _machine_adc_obj_t {
uint8_t id;
uint8_t avg;
uint8_t bits;
uint8_t vref;
} machine_adc_obj_t;
#define DEFAULT_ADC_BITS 12
#define DEFAULT_ADC_AVG 16
#if defined(MCU_SAMD21)
static uint8_t adc_vref_table[] = {
ADC_REFCTRL_REFSEL_INT1V_Val, ADC_REFCTRL_REFSEL_INTVCC0_Val,
ADC_REFCTRL_REFSEL_INTVCC1_Val, ADC_REFCTRL_REFSEL_AREFA_Val, ADC_REFCTRL_REFSEL_AREFB_Val
};
#if MICROPY_HW_ADC_VREF
#define DEFAULT_ADC_VREF MICROPY_HW_ADC_VREF
#else
#define DEFAULT_ADC_VREF (3)
#endif
#define ADC_EVSYS_CHANNEL 0
#elif defined(MCU_SAMD51)
static uint8_t adc_vref_table[] = {
ADC_REFCTRL_REFSEL_INTREF_Val, ADC_REFCTRL_REFSEL_INTVCC1_Val,
ADC_REFCTRL_REFSEL_INTVCC0_Val, ADC_REFCTRL_REFSEL_AREFA_Val,
ADC_REFCTRL_REFSEL_AREFB_Val, ADC_REFCTRL_REFSEL_AREFC_Val
};
#if MICROPY_HW_ADC_VREF
#define DEFAULT_ADC_VREF MICROPY_HW_ADC_VREF
#else
#define DEFAULT_ADC_VREF (3)
#endif
#endif // defined(MCU_SAMD21)
Adc *const adc_bases[] = ADC_INSTS;
uint32_t busy_flags = 0;
bool init_flags[2] = {false, false};
@@ -66,17 +95,18 @@ STATIC void adc_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t k
(void)kind;
machine_adc_obj_t *self = MP_OBJ_TO_PTR(o);
mp_printf(print, "ADC(%s, device=%u, channel=%u, bits=%u, average=%u)",
mp_printf(print, "ADC(%s, device=%u, channel=%u, bits=%u, average=%u, vref=%d)",
pin_name(self->id), self->adc_config.device,
self->adc_config.channel, self->bits, 1 << self->avg);
self->adc_config.channel, self->bits, 1 << self->avg, self->vref);
}
STATIC mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_id, ARG_bits, ARG_average };
enum { ARG_id, ARG_bits, ARG_average, ARG_vref };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_bits, MP_ARG_INT, {.u_int = DEFAULT_ADC_BITS} },
{ MP_QSTR_average, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_ADC_AVG} },
{ MP_QSTR_vref, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_ADC_VREF} },
};
// Parse the arguments.
@@ -99,8 +129,14 @@ STATIC mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_
uint32_t avg = log2i(args[ARG_average].u_int);
self->avg = (avg <= 10 ? avg : 10);
uint8_t vref = args[ARG_vref].u_int;
if (0 <= vref && vref < sizeof(adc_vref_table)) {
self->vref = vref;
}
// flag the device/channel as being in use.
busy_flags |= (1 << (self->adc_config.device * 16 + self->adc_config.channel));
init_flags[self->adc_config.device] = false;
adc_init(self);
@@ -111,6 +147,8 @@ STATIC mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_
STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) {
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
Adc *adc = adc_bases[self->adc_config.device];
// Set the reference voltage. Default: external AREFA.
adc->REFCTRL.reg = adc_vref_table[self->vref];
// Set Input channel and resolution
// Select the pin as positive input and gnd as negative input reference, non-diff mode by default
adc->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND | self->adc_config.channel;
@@ -183,7 +221,7 @@ static void adc_init(machine_adc_obj_t *self) {
// Divide 48MHz clock by 32 to obtain 1.5 MHz clock to adc
adc->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV32;
// Select external AREFA as reference voltage.
adc->REFCTRL.reg = ADC_REFCTRL_REFSEL_AREFA;
adc->REFCTRL.reg = adc_vref_table[self->vref];
// Average: Accumulate samples and scale them down accordingly
adc->AVGCTRL.reg = self->avg | ADC_AVGCTRL_ADJRES(self->avg);
// Enable ADC and wait to be ready
@@ -222,8 +260,8 @@ static void adc_init(machine_adc_obj_t *self) {
adc->CALIB.reg = ADC_CALIB_BIASCOMP(biascomp) | ADC_CALIB_BIASR2R(biasr2r) | ADC_CALIB_BIASREFBUF(biasrefbuf);
// Divide 48MHz clock by 32 to obtain 1.5 MHz clock to adc
adc->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV32;
// Select external AREFA as reference voltage.
adc->REFCTRL.reg = ADC_REFCTRL_REFSEL_AREFA;
// Set the reference voltage. Default: external AREFA.
adc->REFCTRL.reg = adc_vref_table[self->vref];
// Average: Accumulate samples and scale them down accordingly
adc->AVGCTRL.reg = self->avg | ADC_AVGCTRL_ADJRES(self->avg);
// Enable ADC and wait to be ready

View File

@@ -38,9 +38,10 @@ typedef struct _dac_obj_t {
mp_obj_base_t base;
uint8_t id;
mp_hal_pin_obj_t gpio_id;
uint8_t vref;
} dac_obj_t;
STATIC const dac_obj_t dac_obj[] = {
STATIC dac_obj_t dac_obj[] = {
#if defined(MCU_SAMD21)
{{&machine_dac_type}, 0, PIN_PA02},
#elif defined(MCU_SAMD51)
@@ -51,25 +52,53 @@ STATIC const dac_obj_t dac_obj[] = {
Dac *const dac_bases[] = DAC_INSTS;
#if defined(MCU_SAMD21)
#define MAX_DAC_VALUE (1023)
#define MAX_DAC_VALUE (1023)
#define DEFAULT_DAC_VREF (1)
#define MAX_DAC_VREF (2)
#elif defined(MCU_SAMD51)
#define MAX_DAC_VALUE (4095)
// According to Errata 2.9.2, VDDANA as ref value is not available. However it worked
// in tests. So I keep the selection here but set the default to Aref, which is usually
// connected at the Board to VDDANA
static uint8_t dac_vref_table[] = {
DAC_CTRLB_REFSEL_INTREF_Val, DAC_CTRLB_REFSEL_VDDANA_Val,
DAC_CTRLB_REFSEL_VREFPU_Val, DAC_CTRLB_REFSEL_VREFPB_Val
};
#define MAX_DAC_VALUE (4095)
#define DEFAULT_DAC_VREF (2)
#define MAX_DAC_VREF (3)
static bool dac_init = false;
#endif
STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
const mp_obj_t *args) {
const mp_obj_t *all_args) {
mp_arg_check_num(n_args, n_kw, 1, 1, true);
uint8_t id = mp_obj_get_int(args[0]);
const dac_obj_t *self = NULL;
enum { ARG_id, ARG_vref };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_vref, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_DAC_VREF} },
};
// Parse the arguments.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
uint8_t id = args[ARG_id].u_int;
dac_obj_t *self = NULL;
if (0 <= id && id <= MP_ARRAY_SIZE(dac_obj)) {
self = &dac_obj[id];
} else {
mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for DAC"));
}
uint8_t vref = args[ARG_vref].u_int;
if (0 <= vref && vref <= MAX_DAC_VREF) {
self->vref = vref;
}
Dac *dac = dac_bases[0]; // Just one DAC
// Init DAC
@@ -85,7 +114,7 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
dac->CTRLA.bit.SWRST = 1;
while (dac->CTRLA.bit.SWRST) {
}
dac->CTRLB.reg = DAC_CTRLB_EOEN | DAC_CTRLB_REFSEL(DAC_CTRLB_REFSEL_AVCC_Val);
dac->CTRLB.reg = DAC_CTRLB_EOEN | DAC_CTRLB_REFSEL(self->vref);
// Enable DAC and wait to be ready
dac->CTRLA.bit.ENABLE = 1;
while (dac->STATUS.bit.SYNCBUSY) {
@@ -95,21 +124,15 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
// Configuration SAMD51
// Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz
if (!dac_init) {
dac_init = true;
MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC;
GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN;
dac_init = true;
MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC;
GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN;
// Reset DAC registers
dac->CTRLA.bit.SWRST = 1;
while (dac->CTRLA.bit.SWRST) {
}
dac->CTRLB.reg = DAC_CTRLB_REFSEL(DAC_CTRLB_REFSEL_VDDANA_Val);
} else {
dac->CTRLA.bit.ENABLE = 0;
while (dac->SYNCBUSY.bit.ENABLE) {
}
// Reset DAC registers
dac->CTRLA.bit.SWRST = 1;
while (dac->CTRLA.bit.SWRST) {
}
dac->CTRLB.reg = DAC_CTRLB_REFSEL(dac_vref_table[self->vref]);
dac->DACCTRL[self->id].reg = DAC_DACCTRL_ENABLE | DAC_DACCTRL_REFRESH(2) | DAC_DACCTRL_CCTRL_CC12M;
// Enable DAC and wait to be ready
@@ -126,7 +149,7 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
STATIC void dac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
dac_obj_t *self = self_in;
mp_printf(print, "DAC(%u, Pin=%s)", self->id, pin_name(self->gpio_id));
mp_printf(print, "DAC(%u, Pin=%s, vref=%d)", self->id, pin_name(self->gpio_id), self->vref);
}
STATIC mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) {