summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorrobert-hh <robert@hammelrath.com>2022-12-02 21:08:46 +0100
committerDamien George <damien@micropython.org>2022-12-14 12:48:24 +1100
commite69313f89c607976da2a7e06ad49b8db9fe1caf9 (patch)
treeeb2fec3ad51be6ed4c7bf323cff760a7edc3a08f
parenta73dcb3d228008a6225fd80627d3b9566470c687 (diff)
downloadmicropython-e69313f89c607976da2a7e06ad49b8db9fe1caf9.tar.gz
micropython-e69313f89c607976da2a7e06ad49b8db9fe1caf9.zip
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).
-rw-r--r--docs/samd/quickref.rst58
-rw-r--r--ports/samd/boards/SEEED_XIAO/mpconfigboard.h1
-rw-r--r--ports/samd/machine_adc.c50
-rw-r--r--ports/samd/machine_dac.c69
4 files changed, 144 insertions, 34 deletions
diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst
index 5e8298d1b3..aa3d563f7c 100644
--- a/docs/samd/quickref.rst
+++ b/docs/samd/quickref.rst
@@ -254,16 +254,38 @@ an external ADC.
ADC Constructor
```````````````
-.. class:: ADC(dest, *, average=16)
+.. class:: ADC(dest, *, average=16, vref=n)
:noindex:
- Construct and return a new ADC object using the following parameters:
+Construct and return a new ADC object using the following parameters:
- - *dest* is the Pin object on which the ADC is output.
+ - *dest* is the Pin object on which the ADC is output.
- Keyword arguments:
+Keyword arguments:
+
+ - *average* is used to reduce the noise. With a value of 16 the LSB noise is about 1 digit.
+ - *vref* sets the reference voltage for the ADC.
+
+ The default setting is for 3.3V. Other values are:
- - *average* is used to reduce the noise. With a value of 16 the LSB noise is about 1 digit.
+ ==== ============================== ===============================
+ vref SAMD21 SAMD51
+ ==== ============================== ===============================
+ 0 1.0V voltage reference internal bandgap reference (1V)
+ 1 1/1.48 Analogue voltage supply Analogue voltage supply
+ 2 1/2 Analogue voltage supply 1/2 Analogue voltage supply
+ 3 External reference A External reference A
+ 4 External reference B External reference B
+ 5 - External reference C
+ ==== ============================== ===============================
+
+ADC Methods
+```````````
+
+.. method:: read_u16()
+
+Read a single ADC value as unsigned 16 bit quantity. The voltage range is defined
+by the vref option of the constructor, the resolutions by the bits option.
DAC (digital to analog conversion)
----------------------------------
@@ -280,6 +302,32 @@ The DAC class provides a fast digital to analog conversion. Usage example::
The resolution of the DAC is 12 bit for SAMD51 and 10 bit for SAMD21. SAMD21 devices
have 1 DAC channel at GPIO PA02, SAMD51 devices have 2 DAC channels at GPIO PA02 and PA05.
+DAC Constructor
+```````````````
+
+.. class:: DAC(id, *, vref=3)
+ :noindex:
+
+The vref arguments defines the output voltage range, the callback option is used for
+dac_timed(). Suitable values for vref are:
+
+==== ============================ ================================
+vref SAMD21 SAMD51
+==== ============================ ================================
+0 Internal voltage reference Internal bandgap reference (~1V)
+1 Analogue voltage supply Analogue voltage supply
+2 External reference Unbuffered external reference
+3 - Buffered external reference
+==== ============================ ================================
+
+DAC Methods
+```````````
+
+.. method:: write(value)
+
+Write a single value to the selected DAC output. The value range is 0-1023 for
+SAMD21 and 0-4095 for SAMD51. The voltage range depends on the vref setting.
+
Software SPI bus
----------------
diff --git a/ports/samd/boards/SEEED_XIAO/mpconfigboard.h b/ports/samd/boards/SEEED_XIAO/mpconfigboard.h
index 98994a5bc0..7447c5c3ac 100644
--- a/ports/samd/boards/SEEED_XIAO/mpconfigboard.h
+++ b/ports/samd/boards/SEEED_XIAO/mpconfigboard.h
@@ -2,3 +2,4 @@
#define MICROPY_HW_MCU_NAME "SAMD21G18A"
#define MICROPY_HW_XOSC32K (1)
+#define MICROPY_HW_ADC_VREF (2)
diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c
index 8fd3e3bf4e..d407882529 100644
--- a/ports/samd/machine_adc.c
+++ b/ports/samd/machine_adc.c
@@ -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
diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c
index 53407c0d37..a1da9285e5 100644
--- a/ports/samd/machine_dac.c
+++ b/ports/samd/machine_dac.c
@@ -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;
-
- // 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) {
- }
+ 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_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) {