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:
parent
a73dcb3d22
commit
e69313f89c
|
@ -254,7 +254,7 @@ 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:
|
||||
|
@ -264,6 +264,28 @@ ADC Constructor
|
|||
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:
|
||||
|
||||
==== ============================== ===============================
|
||||
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
|
||||
----------------
|
||||
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
#define MICROPY_HW_MCU_NAME "SAMD21G18A"
|
||||
|
||||
#define MICROPY_HW_XOSC32K (1)
|
||||
#define MICROPY_HW_ADC_VREF (2)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 DEFAULT_DAC_VREF (1)
|
||||
#define MAX_DAC_VREF (2)
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
// 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,7 +124,6 @@ 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;
|
||||
|
@ -104,12 +132,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_REFSEL(DAC_CTRLB_REFSEL_VDDANA_Val);
|
||||
} else {
|
||||
dac->CTRLA.bit.ENABLE = 0;
|
||||
while (dac->SYNCBUSY.bit.ENABLE) {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue