kopia lustrzana https://github.com/micropython/micropython
Merge a7edf0107c
into 49ce7a6075
commit
e29736d6f4
|
@ -254,30 +254,39 @@ an external ADC.
|
|||
ADC Constructor
|
||||
```````````````
|
||||
|
||||
.. class:: ADC(dest, *, average=16, vref=n)
|
||||
.. class:: ADC(dest, *, average=16, bits=12, vref=3, callback=None)
|
||||
:noindex:
|
||||
|
||||
Construct and return a new ADC object using the following parameters:
|
||||
On the SAMD21/SAMD51 ADC functionality is available on Pins labelled 'Ann'.
|
||||
|
||||
- *dest* is the Pin object on which the ADC is output.
|
||||
Use the :ref:`machine.ADC <machine.ADC>` class::
|
||||
|
||||
Keyword arguments:
|
||||
from machine import ADC
|
||||
|
||||
- *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.
|
||||
adc0 = ADC(Pin("A0")) # create ADC object on ADC pin, average=16
|
||||
adc0.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v
|
||||
adc1 = ADC(Pin("A1"), average=1) # create ADC object on ADC pin, average=1
|
||||
|
||||
The default setting is for 3.3V. Other values are:
|
||||
The resolution of the ADC is set by the bits keyword option. The default is 12.
|
||||
Suitable values are 8, 10 and 12. If you need a higher resolution or better
|
||||
accuracy, use an external ADC. The default value of average is 16.
|
||||
Averaging is used to reduce the noise. With a value of 16 the LSB noise is
|
||||
about 1 digit. The vref=n option 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
|
||||
==== ============================== ===============================
|
||||
==== ============================== ===============================
|
||||
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
|
||||
==== ============================== ===============================
|
||||
|
||||
The callback keyword option is used for timed ADC sampling. The callback is executed
|
||||
when all data has been sampled.
|
||||
|
||||
ADC Methods
|
||||
```````````
|
||||
|
@ -287,27 +296,66 @@ ADC Methods
|
|||
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)
|
||||
----------------------------------
|
||||
.. method:: read_timed(data, freq)
|
||||
|
||||
The DAC class provides a fast digital to analog conversion. Usage example::
|
||||
Read adc values into the data buffer at a supplied frequency. The buffer
|
||||
must be preallocated. Values are stored as 16 bit quantities in the binary
|
||||
range given by the bits option. If bits=12, the value range is 0-4095.
|
||||
The voltage range is defined by the vref option.
|
||||
The sampling frequency range depends on the bits and average setting. At bits=8
|
||||
and average=1, the largest rate is >1 MHz for SAMD21 and 350kHz for SAMD21.
|
||||
the lowest sampling rate is 1 Hz. The call to the method returns immediately,
|
||||
The data transfer is done by DMA in the background, controlled by a hardware timer.
|
||||
If in the constructor a callback was defined, it will be called after all data has been
|
||||
read. Alternatively, the method busy() can be used to tell, if the capture has finished.
|
||||
|
||||
from machine import DAC
|
||||
Example for a call to adc.read_timed() and a callback::
|
||||
|
||||
dac0 = DAC(0) # create DAC object on DAC pin A0
|
||||
dac0.write(1023) # write value, 0-4095 across voltage range 0.0v - 3.3v
|
||||
dac1 = DAC(1) # create DAC object on DAC pin A1
|
||||
dac1.write(2000) # write value, 0-4095 across voltage range 0.0v - 3.3v
|
||||
from machine import ADC
|
||||
from array import array
|
||||
|
||||
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.
|
||||
def finished(adc_o):
|
||||
print("Sampling finished on ADC", adc_o)
|
||||
|
||||
# create ADC object on ADC pin A0, average=1
|
||||
adc = ADC(Pin("A0"), average=1, callback=finished)
|
||||
buffer = array("H", bytearray(512)) # create an array for 256 ADC values
|
||||
adc.read_timed(buffer, 10000) # read 256 12 bit values at a frequency of
|
||||
# 10 kHz and call finished() when done.
|
||||
|
||||
.. method:: busy()
|
||||
|
||||
busy() returns `True` while the data acquisition using read_timed() is ongoing, `False`
|
||||
otherwise.
|
||||
|
||||
.. method deinit()
|
||||
|
||||
Deinitialize as ADC object and release the resources used by it, especially the ADC
|
||||
channel and the timer used for read_timed().
|
||||
|
||||
|
||||
DAC (digital to analogue conversion)
|
||||
------------------------------------
|
||||
|
||||
DAC Constructor
|
||||
```````````````
|
||||
|
||||
.. class:: DAC(id, *, vref=3)
|
||||
.. class:: DAC(id, *, vref=3, callback=None)
|
||||
:noindex:
|
||||
|
||||
|
||||
The DAC class provides a fast digital to analogue conversion. Usage example::
|
||||
|
||||
from machine import DAC
|
||||
|
||||
dac0 = DAC(0) # create DAC object on DAC pin A0
|
||||
dac0.write(1023) # write value, 0-4095 across voltage range 0.0V - 3.3V
|
||||
dac1 = DAC(1) # create DAC object on DAC pin A1
|
||||
dac1.write(2000) # write value, 0-4095 across voltage range 0.0V - 3.3V
|
||||
|
||||
The resolution of the DAC is 12 bit for SAMD51 and 10 bit for SAMD21. SAMD21 devices
|
||||
have 1 DAC channel at GPIO PA02, accepting only 0 as id. SAMD51 devices have
|
||||
2 DAC channels at GPIO PA02 and PA05 with values 0 and 1 for the id.
|
||||
The vref arguments defines the output voltage range, the callback option is used for
|
||||
dac_timed(). Suitable values for vref are:
|
||||
|
||||
|
@ -320,6 +368,7 @@ vref SAMD21 SAMD51
|
|||
3 - Buffered external reference
|
||||
==== ============================ ================================
|
||||
|
||||
|
||||
DAC Methods
|
||||
```````````
|
||||
|
||||
|
@ -328,6 +377,52 @@ DAC Methods
|
|||
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.
|
||||
|
||||
.. method:: write_timed(data, freq [, count=1])
|
||||
|
||||
The call to dac_timed() allows to output a series of analogue values at a given rate.
|
||||
data must be a buffer with 16 bit values in the range of the DAC (10 bit of 12 bit).
|
||||
freq may have a range of 1Hz to ~200kHz for SAMD21 and 1 Hz to ~500kHz for SAMD51.
|
||||
The optional argument count specifies, how often data output will be repeated. The
|
||||
range is 1 - 2**32. If count == 0, the data output will be repeated until stopped
|
||||
by a call to deinit(). If the data has been output count times, a callback will
|
||||
be called, if given.
|
||||
|
||||
Example::
|
||||
|
||||
from machine import DAC
|
||||
from array import array
|
||||
|
||||
data = array("H", [i for i in range(0, 4096, 256)]) # create a step sequence
|
||||
|
||||
def done(dac_o):
|
||||
print("Sequence done at", dac_o)
|
||||
|
||||
dac = DAC(0, callback=done)
|
||||
dac.write_timed(data, 1000, 10) # output data 10 times at a rate of 1000 values/s
|
||||
# and call done() when finished.
|
||||
|
||||
The data transfer is done by DMA and not affected by python code execution.
|
||||
It is possible to restart dac.write_timed() in the callback function with changed
|
||||
parameters.
|
||||
|
||||
|
||||
.. method:: busy()
|
||||
:noindex:
|
||||
|
||||
Tell, whether a write_timed() activity is ongoing. It returns `True` if yes, `False`
|
||||
otherwise.
|
||||
|
||||
|
||||
.. method:: deinit()
|
||||
|
||||
Deinitialize the DAC and release all resources used by it, especially the DMA channels
|
||||
and the Timers. On most SAMD21 boards, there is just one timer available for
|
||||
dac.write_timed() and adc.read_timed_into(). So they cannot run both at the same time,
|
||||
and releasing the timer may be important. The DAC driver consumes a substantial amount
|
||||
of current. deinit() will reduce that as well. After calling deinit(), the
|
||||
DAC objects cannot be used any more and must be recreated.
|
||||
|
||||
|
||||
Software SPI bus
|
||||
----------------
|
||||
|
||||
|
|
|
@ -140,6 +140,24 @@ static mp_obj_t machine_adc_read(mp_obj_t self_in) {
|
|||
static MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_obj, machine_adc_read);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
// ADC.atten(value) -- this is a legacy method.
|
||||
static mp_obj_t machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t freq_in) {
|
||||
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_int_t freq = mp_obj_get_int(freq_in);
|
||||
mp_machine_adc_read_timed(self, values, freq);
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(machine_adc_read_timed_obj, machine_adc_read_timed);
|
||||
|
||||
// ADC.busy())
|
||||
static mp_obj_t machine_adc_busy(mp_obj_t self_in) {
|
||||
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_machine_adc_busy(self);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_busy_obj, machine_adc_busy);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
|
||||
#if MICROPY_PY_MACHINE_ADC_INIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_adc_init_obj) },
|
||||
|
@ -164,6 +182,10 @@ static const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
|
|||
#if MICROPY_PY_MACHINE_ADC_READ
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_adc_read_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
{ MP_ROM_QSTR(MP_QSTR_read_timed), MP_ROM_PTR(&machine_adc_read_timed_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_busy), MP_ROM_PTR(&machine_adc_busy_obj) },
|
||||
#endif
|
||||
|
||||
// A port must add ADC class constants defining the following macro.
|
||||
// It can be defined to nothing if there are no constants.
|
||||
|
|
|
@ -101,6 +101,7 @@ MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH)
|
|||
|
||||
SRC_C += \
|
||||
mcu/$(MCU_SERIES_LOWER)/clock_config.c \
|
||||
dma_manager.c \
|
||||
help.c \
|
||||
machine_bitstream.c \
|
||||
machine_dac.c \
|
||||
|
@ -119,6 +120,7 @@ SRC_C += \
|
|||
samd_soc.c \
|
||||
samd_spiflash.c \
|
||||
usbd.c \
|
||||
tc_manager.c \
|
||||
|
||||
SHARED_SRC_C += \
|
||||
drivers/dht/dht.c \
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Robert Hammelrath
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "py/mpconfig.h"
|
||||
#include "sam.h"
|
||||
#include "dma_manager.h"
|
||||
#include "samd_soc.h"
|
||||
|
||||
#if MICROPY_HW_DMA_MANAGER
|
||||
|
||||
// Set a number of dma channels managed here. samd21 has 21 dma channels, samd51
|
||||
// has 32 channels, as defined by the lib macro DMAC_CH_NUM.
|
||||
// At first, we use a smaller number here to save RAM. May be increased as needed.
|
||||
|
||||
#ifndef MICROPY_HW_DMA_CHANNELS
|
||||
#if defined(MCU_SAMD21)
|
||||
#define MICROPY_HW_DMA_CHANNELS 2
|
||||
#elif defined(MCU_SAMD51)
|
||||
#define MICROPY_HW_DMA_CHANNELS 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MICROPY_HW_DMA_CHANNELS > DMAC_CH_NUM
|
||||
#error Number of DMA channels too large
|
||||
#endif
|
||||
|
||||
volatile DmacDescriptor dma_desc[MICROPY_HW_DMA_CHANNELS] __attribute__ ((aligned(16)));
|
||||
static volatile DmacDescriptor dma_write_back[MICROPY_HW_DMA_CHANNELS] __attribute__ ((aligned(16)));
|
||||
|
||||
// List of channel flags: true: channel used, false: channel available
|
||||
static bool channel_list[MICROPY_HW_DMA_CHANNELS];
|
||||
|
||||
static bool dma_initialized = false;
|
||||
|
||||
// allocate_channel(): retrieve an available channel. Return the number or -1
|
||||
int allocate_dma_channel(void) {
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(channel_list); i++) {
|
||||
if (channel_list[i] == false) { // Channel available
|
||||
channel_list[i] = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("no dma channel available"));
|
||||
}
|
||||
|
||||
// free_channel(n): Declare channel as free
|
||||
void free_dma_channel(int n) {
|
||||
if (n >= 0 && n < MP_ARRAY_SIZE(channel_list)) {
|
||||
channel_list[n] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void dma_init(void) {
|
||||
if (!dma_initialized) {
|
||||
|
||||
// Enable the DMA clock
|
||||
#if defined(MCU_SAMD21)
|
||||
PM->AHBMASK.reg |= PM_AHBMASK_DMAC;
|
||||
PM->APBBMASK.reg |= PM_APBBMASK_DMAC;
|
||||
#elif defined(MCU_SAMD51)
|
||||
MCLK->AHBMASK.reg |= MCLK_AHBMASK_DMAC;
|
||||
#endif
|
||||
// Setup the initial DMA configuration
|
||||
DMAC->CTRL.reg = DMAC_CTRL_SWRST;
|
||||
while (DMAC->CTRL.reg & DMAC_CTRL_SWRST) {
|
||||
}
|
||||
// Set the DMA descriptor pointers
|
||||
DMAC->BASEADDR.reg = (uint32_t)dma_desc;
|
||||
DMAC->WRBADDR.reg = (uint32_t)dma_write_back;
|
||||
// Enable the DMA
|
||||
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
|
||||
|
||||
dma_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void dma_deinit(void) {
|
||||
memset((uint8_t *)dma_desc, 0, sizeof(dma_desc));
|
||||
memset((uint8_t *)dma_write_back, 0, sizeof(dma_write_back));
|
||||
memset((uint8_t *)channel_list, 0, sizeof(channel_list));
|
||||
dma_initialized = false;
|
||||
// Disable DMA
|
||||
DMAC->CTRL.reg = 0;
|
||||
for (int ch = 0; ch < DMAC_CH_NUM; ch++) {
|
||||
dma_register_irq(ch, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void dac_stop_dma(int dma_channel, bool wait) {
|
||||
#if defined(MCU_SAMD21)
|
||||
NVIC_DisableIRQ(DMAC_IRQn);
|
||||
DMAC->CHID.reg = dma_channel;
|
||||
DMAC->CHINTENCLR.reg = DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR | DMAC_CHINTENSET_SUSP;
|
||||
DMAC->CHCTRLA.reg = 0;
|
||||
while (wait && DMAC->CHCTRLA.bit.ENABLE) {
|
||||
}
|
||||
#elif defined(MCU_SAMD51)
|
||||
if (0 <= dma_channel && dma_channel < 4) {
|
||||
NVIC_DisableIRQ(DMAC_0_IRQn + dma_channel);
|
||||
} else if (dma_channel >= 4) {
|
||||
NVIC_DisableIRQ(DMAC_4_IRQn);
|
||||
}
|
||||
DMAC->Channel[dma_channel].CHINTENCLR.reg =
|
||||
DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR | DMAC_CHINTENSET_SUSP;
|
||||
DMAC->Channel[dma_channel].CHCTRLA.reg = 0;
|
||||
while (wait && DMAC->Channel[dma_channel].CHCTRLA.bit.ENABLE) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Robert Hammelrath
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_SAMD_DMACHANNEL_H
|
||||
#define MICROPY_INCLUDED_SAMD_DMACHANNEL_H
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
int allocate_dma_channel(void);
|
||||
void free_dma_channel(int n);
|
||||
void dma_init(void);
|
||||
void dma_deinit(void);
|
||||
void dac_stop_dma(int dma_channel, bool wait);
|
||||
|
||||
extern volatile DmacDescriptor dma_desc[];
|
||||
|
||||
#endif // MICROPY_INCLUDED_SAMD_DMACHANNEL_H
|
|
@ -28,9 +28,16 @@
|
|||
// This file is never compiled standalone, it's included directly from
|
||||
// extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE.
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include <stdint.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/mperrno.h"
|
||||
|
||||
#include "mphalport.h"
|
||||
#include "sam.h"
|
||||
#include "pin_af.h"
|
||||
#include "samd_soc.h"
|
||||
#include "dma_manager.h"
|
||||
#include "tc_manager.h"
|
||||
|
||||
typedef struct _machine_adc_obj_t {
|
||||
mp_obj_base_t base;
|
||||
|
@ -39,12 +46,17 @@ typedef struct _machine_adc_obj_t {
|
|||
uint8_t avg;
|
||||
uint8_t bits;
|
||||
uint8_t vref;
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
int8_t dma_channel;
|
||||
int8_t tc_index;
|
||||
#endif
|
||||
} 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
|
||||
|
@ -54,9 +66,21 @@ static uint8_t adc_vref_table[] = {
|
|||
#else
|
||||
#define DEFAULT_ADC_VREF (3)
|
||||
#endif
|
||||
#define MAX_ADC_VREF (4)
|
||||
|
||||
#define ADC_EVSYS_CHANNEL 0
|
||||
|
||||
typedef struct _device_mgmt_t {
|
||||
bool init;
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
bool busy;
|
||||
mp_obj_t callback;
|
||||
mp_obj_t self;
|
||||
#endif
|
||||
} device_mgmt_t;
|
||||
|
||||
device_mgmt_t device_mgmt[ADC_INST_NUM];
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
static uint8_t adc_vref_table[] = {
|
||||
|
@ -69,6 +93,17 @@ static uint8_t adc_vref_table[] = {
|
|||
#else
|
||||
#define DEFAULT_ADC_VREF (3)
|
||||
#endif
|
||||
#define MAX_ADC_VREF (5)
|
||||
|
||||
typedef struct _device_mgmt_t {
|
||||
bool init;
|
||||
bool busy;
|
||||
int8_t dma_channel;
|
||||
mp_obj_t callback;
|
||||
mp_obj_t self;
|
||||
} device_mgmt_t;
|
||||
|
||||
device_mgmt_t device_mgmt[ADC_INST_NUM];
|
||||
|
||||
#endif // defined(MCU_SAMD21)
|
||||
|
||||
|
@ -76,15 +111,48 @@ static uint8_t adc_vref_table[] = {
|
|||
#define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS
|
||||
|
||||
Adc *const adc_bases[] = ADC_INSTS;
|
||||
uint32_t busy_flags = 0;
|
||||
bool init_flags[2] = {false, false};
|
||||
static void adc_init(machine_adc_obj_t *self);
|
||||
uint32_t ch_busy_flags = 0;
|
||||
|
||||
static uint8_t resolution[] = {
|
||||
ADC_CTRLB_RESSEL_8BIT_Val, ADC_CTRLB_RESSEL_10BIT_Val, ADC_CTRLB_RESSEL_12BIT_Val
|
||||
};
|
||||
static void adc_init(machine_adc_obj_t *self);
|
||||
|
||||
extern mp_int_t log2i(mp_int_t num);
|
||||
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
|
||||
// Active just for SAMD21, stops the freerun mode
|
||||
// For SAMD51, just the INT flag is reset.
|
||||
void adc_irq_handler(int dma_channel) {
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
DMAC->CHID.reg = dma_channel;
|
||||
DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL | DMAC_CHINTFLAG_TERR | DMAC_CHINTFLAG_SUSP;
|
||||
ADC->EVCTRL.bit.STARTEI = 0;
|
||||
device_mgmt[0].busy = 0;
|
||||
if (device_mgmt[0].callback != MP_OBJ_NULL) {
|
||||
mp_sched_schedule(device_mgmt[0].callback, device_mgmt[0].self);
|
||||
}
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
DMAC->Channel[dma_channel].CHINTFLAG.reg =
|
||||
DMAC_CHINTFLAG_TCMPL | DMAC_CHINTFLAG_TERR | DMAC_CHINTFLAG_SUSP;
|
||||
if (device_mgmt[0].dma_channel == dma_channel) {
|
||||
device_mgmt[0].busy = 0;
|
||||
if (device_mgmt[0].callback != MP_OBJ_NULL) {
|
||||
mp_sched_schedule(device_mgmt[0].callback, device_mgmt[0].self);
|
||||
}
|
||||
} else if (device_mgmt[1].dma_channel == dma_channel) {
|
||||
device_mgmt[1].busy = 0;
|
||||
if (device_mgmt[1].callback != MP_OBJ_NULL) {
|
||||
mp_sched_schedule(device_mgmt[1].callback, device_mgmt[1].self);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -95,12 +163,16 @@ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_p
|
|||
}
|
||||
|
||||
static mp_obj_t mp_machine_adc_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, ARG_vref };
|
||||
|
||||
enum { ARG_id, ARG_bits, ARG_average, ARG_vref, ARG_callback };
|
||||
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} },
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
#endif
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
|
@ -109,28 +181,39 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args
|
|||
|
||||
// Unpack and check, whether the pin has ADC capability
|
||||
int id = mp_hal_get_pin_obj(args[ARG_id].u_obj);
|
||||
adc_config_t adc_config = get_adc_config(id, busy_flags);
|
||||
adc_config_t adc_config = get_adc_config(id, ch_busy_flags);
|
||||
|
||||
// Now that we have a valid device and channel, create and populate the ADC instance
|
||||
machine_adc_obj_t *self = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type);
|
||||
self->id = id;
|
||||
self->adc_config = adc_config;
|
||||
self->bits = DEFAULT_ADC_BITS;
|
||||
|
||||
uint16_t bits = args[ARG_bits].u_int;
|
||||
if (bits >= 8 && bits <= 12) {
|
||||
if (8 <= bits && bits <= 12) {
|
||||
self->bits = bits;
|
||||
}
|
||||
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)) {
|
||||
if (0 <= vref && vref <= MAX_ADC_VREF) {
|
||||
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;
|
||||
ch_busy_flags |= (1 << (self->adc_config.device * 16 + self->adc_config.channel));
|
||||
device_mgmt[self->adc_config.device].init = false;
|
||||
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
device_mgmt[adc_config.device].callback = args[ARG_callback].u_obj;
|
||||
if (device_mgmt[adc_config.device].callback == mp_const_none) {
|
||||
device_mgmt[adc_config.device].callback = MP_OBJ_NULL;
|
||||
} else {
|
||||
device_mgmt[adc_config.device].self = self;
|
||||
}
|
||||
self->dma_channel = -1;
|
||||
self->tc_index = -1;
|
||||
#endif
|
||||
|
||||
adc_init(self);
|
||||
|
||||
|
@ -140,6 +223,15 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args
|
|||
// read_u16()
|
||||
static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
|
||||
Adc *adc = adc_bases[self->adc_config.device];
|
||||
// Set the reference voltage. Default: external AREFA.
|
||||
adc->REFCTRL.reg = adc_vref_table[self->vref];
|
||||
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
if (device_mgmt[self->adc_config.device].busy != 0) {
|
||||
mp_raise_OSError(MP_EBUSY);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set the reference voltage. Default: external AREFA.
|
||||
adc->REFCTRL.reg = adc_vref_table[self->vref];
|
||||
// Set Input channel and resolution
|
||||
|
@ -147,6 +239,14 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
|
|||
adc->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND | self->adc_config.channel;
|
||||
// set resolution. Scale 8-16 to 0 - 4 for table access.
|
||||
adc->CTRLB.bit.RESSEL = resolution[(self->bits - 8) / 2];
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
// Stop the ADC sampling by timer
|
||||
adc->EVCTRL.bit.STARTEI = 0;
|
||||
#elif defined(MCU_SAMD51)
|
||||
// Do not restart ADC after data has bee read
|
||||
adc->DSEQCTRL.reg = 0;
|
||||
#endif
|
||||
// Measure input voltage
|
||||
adc->SWTRIG.bit.START = 1;
|
||||
while (adc->INTFLAG.bit.RESRDY == 0) {
|
||||
|
@ -155,29 +255,168 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
|
|||
return adc->RESULT.reg * (65536 / (1 << self->bits));
|
||||
}
|
||||
|
||||
// deinit() : release the ADC channel
|
||||
static void mp_machine_adc_deinit(machine_adc_obj_t *self) {
|
||||
busy_flags &= ~((1 << (self->adc_config.device * 16 + self->adc_config.channel)));
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
|
||||
static void mp_machine_adc_read_timed(machine_adc_obj_t *self, mp_obj_t values, mp_int_t freq) {
|
||||
Adc *adc = adc_bases[self->adc_config.device];
|
||||
mp_buffer_info_t src;
|
||||
mp_get_buffer_raise(values, &src, MP_BUFFER_READ);
|
||||
if (src.len >= 2) {
|
||||
if (self->tc_index == -1) {
|
||||
self->tc_index = allocate_tc_instance();
|
||||
}
|
||||
if (self->dma_channel == -1) {
|
||||
self->dma_channel = allocate_dma_channel();
|
||||
dma_init();
|
||||
dma_register_irq(self->dma_channel, adc_irq_handler);
|
||||
}
|
||||
// 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;
|
||||
// set resolution. Scale 8-16 to 0 - 4 for table access.
|
||||
adc->CTRLB.bit.RESSEL = resolution[(self->bits - 8) / 2];
|
||||
|
||||
// Configure DMA for halfword output to the DAC
|
||||
#if defined(MCU_SAMD21)
|
||||
configure_tc(self->tc_index, freq, TC_EVCTRL_OVFEO);
|
||||
// Enable APBC clock
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;
|
||||
// Set up the EVSYS channel
|
||||
EVSYS->CTRL.bit.SWRST = 1;
|
||||
EVSYS->USER.reg = EVSYS_USER_CHANNEL(ADC_EVSYS_CHANNEL + 1) |
|
||||
EVSYS_USER_USER(EVSYS_ID_USER_ADC_START);
|
||||
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(ADC_EVSYS_CHANNEL) |
|
||||
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TC3_OVF + 3 * self->tc_index) |
|
||||
EVSYS_CHANNEL_PATH_ASYNCHRONOUS;
|
||||
|
||||
dma_desc[self->dma_channel].BTCTRL.reg =
|
||||
DMAC_BTCTRL_VALID | DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_DSTINC | DMAC_BTCTRL_STEPSEL |
|
||||
DMAC_BTCTRL_STEPSIZE(DMAC_BTCTRL_STEPSIZE_X1_Val);
|
||||
dma_desc[self->dma_channel].BTCNT.reg = src.len / 2;
|
||||
dma_desc[self->dma_channel].SRCADDR.reg = (uint32_t)(&adc->RESULT.reg);
|
||||
dma_desc[self->dma_channel].DSTADDR.reg = (uint32_t)(src.buf) + src.len;
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = 0; // ONE_SHOT
|
||||
DMAC->CHID.reg = self->dma_channel;
|
||||
DMAC->CHCTRLA.reg = 0;
|
||||
while (DMAC->CHCTRLA.bit.ENABLE) {
|
||||
}
|
||||
DMAC->CHCTRLB.reg =
|
||||
DMAC_CHCTRLB_LVL(0) |
|
||||
DMAC_CHCTRLB_TRIGACT_BEAT |
|
||||
DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY);
|
||||
|
||||
DMAC->CHINTENSET.reg = DMAC_CHINTFLAG_TCMPL;
|
||||
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
|
||||
NVIC_EnableIRQ(DMAC_IRQn);
|
||||
adc->EVCTRL.bit.STARTEI = 1;
|
||||
device_mgmt[0].busy = 1;
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
configure_tc(self->tc_index, freq, 0);
|
||||
device_mgmt[self->adc_config.device].dma_channel = self->dma_channel;
|
||||
|
||||
// Restart ADC after data has bee read
|
||||
adc->DSEQCTRL.reg = ADC_DSEQCTRL_AUTOSTART;
|
||||
// Start the first sampling to ensure we get a proper first value.
|
||||
adc->SWTRIG.bit.START = 1;
|
||||
while (adc->INTFLAG.bit.RESRDY == 0) {
|
||||
}
|
||||
// Wait a little bit allowing the ADC to settle.
|
||||
mp_hal_delay_us(15);
|
||||
|
||||
dma_desc[self->dma_channel].BTCTRL.reg =
|
||||
DMAC_BTCTRL_VALID | DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_DSTINC | DMAC_BTCTRL_STEPSEL |
|
||||
DMAC_BTCTRL_STEPSIZE(DMAC_BTCTRL_STEPSIZE_X1_Val);
|
||||
dma_desc[self->dma_channel].BTCNT.reg = src.len / 2;
|
||||
dma_desc[self->dma_channel].SRCADDR.reg = (uint32_t)(&adc->RESULT.reg);
|
||||
dma_desc[self->dma_channel].DSTADDR.reg = (uint32_t)(src.buf) + src.len;
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = 0; // ONE_SHOT
|
||||
|
||||
DMAC->Channel[self->dma_channel].CHCTRLA.reg =
|
||||
DMAC_CHCTRLA_BURSTLEN(DMAC_CHCTRLA_BURSTLEN_SINGLE_Val) |
|
||||
DMAC_CHCTRLA_TRIGACT(DMAC_CHCTRLA_TRIGACT_BURST_Val) |
|
||||
DMAC_CHCTRLA_TRIGSRC(TC0_DMAC_ID_OVF + 3 * self->tc_index);
|
||||
DMAC->Channel[self->dma_channel].CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;
|
||||
DMAC->Channel[self->dma_channel].CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
|
||||
if (self->dma_channel < 4) {
|
||||
NVIC_EnableIRQ(DMAC_0_IRQn + self->dma_channel);
|
||||
} else {
|
||||
NVIC_EnableIRQ(DMAC_4_IRQn);
|
||||
}
|
||||
device_mgmt[self->adc_config.device].busy = 1;
|
||||
|
||||
#endif // defined SAMD21 or SAMD51
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void adc_deinit_all(void) {
|
||||
busy_flags = 0;
|
||||
init_flags[0] = 0;
|
||||
init_flags[1] = 0;
|
||||
// busy() : Report, if the ADC device is busy
|
||||
static mp_obj_t mp_machine_adc_busy(machine_adc_obj_t *self) {
|
||||
return device_mgmt[self->adc_config.device].busy ? mp_const_true : mp_const_false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// deinit() : release the ADC channel
|
||||
static void mp_machine_adc_deinit(machine_adc_obj_t *self) {
|
||||
ch_busy_flags &= ~((1 << (self->adc_config.device * 16 + self->adc_config.channel)));
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
if (self->dma_channel >= 0) {
|
||||
#if defined(MCU_SAMD51)
|
||||
if (self->dma_channel == device_mgmt[self->adc_config.device].dma_channel) {
|
||||
device_mgmt[self->adc_config.device].dma_channel = -1;
|
||||
device_mgmt[self->adc_config.device].busy = 0;
|
||||
}
|
||||
#endif
|
||||
dac_stop_dma(self->dma_channel, true);
|
||||
free_dma_channel(self->dma_channel);
|
||||
self->dma_channel = -1;
|
||||
}
|
||||
if (self->tc_index >= 0) {
|
||||
free_tc_instance(self->tc_index);
|
||||
self->tc_index = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
void adc_deinit_all(void) {
|
||||
ch_busy_flags = 0;
|
||||
device_mgmt[0].init = 0;
|
||||
#if defined(MCU_SAMD51)
|
||||
device_mgmt[0].dma_channel = -1;
|
||||
device_mgmt[1].init = 0;
|
||||
device_mgmt[1].dma_channel = -1;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void adc_deinit_all(void) {
|
||||
ch_busy_flags = 0;
|
||||
device_mgmt[0].init = 0;
|
||||
#if defined(MCU_SAMD51)
|
||||
device_mgmt[1].init = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void adc_init(machine_adc_obj_t *self) {
|
||||
// ADC & clock init is done only once per ADC
|
||||
if (init_flags[self->adc_config.device] == false) {
|
||||
if (device_mgmt[self->adc_config.device].init == false) {
|
||||
Adc *adc = adc_bases[self->adc_config.device];
|
||||
|
||||
init_flags[self->adc_config.device] = true;
|
||||
device_mgmt[self->adc_config.device].init = true;
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
// Configuration SAMD21
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK2 at 48 MHz
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK5 at 48 MHz
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_ADC;
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | GCLK_CLKCTRL_ID_ADC;
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_ADC;
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
// Reset ADC registers
|
||||
|
@ -190,7 +429,7 @@ static void adc_init(machine_adc_obj_t *self) {
|
|||
linearity |= ((*((uint32_t *)ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
|
||||
/* Write the calibration data. */
|
||||
ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
|
||||
// Divide 48MHz clock by 32 to obtain 1.5 MHz clock to adc
|
||||
// Divide a 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_vref_table[self->vref];
|
||||
|
@ -203,12 +442,12 @@ static void adc_init(machine_adc_obj_t *self) {
|
|||
|
||||
#elif defined(MCU_SAMD51)
|
||||
// Configuration SAMD51
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK2 at 48 MHz
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK5 at 48 MHz
|
||||
if (self->adc_config.device == 0) {
|
||||
GCLK->PCHCTRL[ADC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN;
|
||||
GCLK->PCHCTRL[ADC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN;
|
||||
MCLK->APBDMASK.bit.ADC0_ = 1;
|
||||
} else {
|
||||
GCLK->PCHCTRL[ADC1_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN;
|
||||
GCLK->PCHCTRL[ADC1_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN;
|
||||
MCLK->APBDMASK.bit.ADC1_ = 1;
|
||||
}
|
||||
// Reset ADC registers
|
||||
|
@ -230,8 +469,10 @@ static void adc_init(machine_adc_obj_t *self) {
|
|||
}
|
||||
/* Write the calibration data. */
|
||||
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;
|
||||
// Divide 48MHz clock by 4 to obtain 12 MHz clock to adc
|
||||
adc->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV4;
|
||||
// Enable the offset compensation
|
||||
adc->SAMPCTRL.reg = ADC_SAMPCTRL_OFFCOMP;
|
||||
// Set the reference voltage. Default: external AREFA.
|
||||
adc->REFCTRL.reg = adc_vref_table[self->vref];
|
||||
// Average: Accumulate samples and scale them down accordingly
|
||||
|
|
|
@ -31,28 +31,33 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
|
||||
#include "sam.h"
|
||||
#include "pin_af.h"
|
||||
#include "modmachine.h"
|
||||
#include "samd_soc.h"
|
||||
#include "dma_manager.h"
|
||||
#include "tc_manager.h"
|
||||
|
||||
typedef struct _dac_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t id;
|
||||
mp_hal_pin_obj_t gpio_id;
|
||||
bool initialized;
|
||||
uint8_t vref;
|
||||
} dac_obj_t;
|
||||
|
||||
static dac_obj_t dac_obj[] = {
|
||||
#if defined(MCU_SAMD21)
|
||||
{{&machine_dac_type}, 0, PIN_PA02},
|
||||
#elif defined(MCU_SAMD51)
|
||||
{{&machine_dac_type}, 0, PIN_PA02},
|
||||
{{&machine_dac_type}, 1, PIN_PA05},
|
||||
mp_hal_pin_obj_t gpio_id;
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
int8_t dma_channel;
|
||||
int8_t tc_index;
|
||||
bool busy;
|
||||
uint32_t count;
|
||||
mp_obj_t callback;
|
||||
#endif
|
||||
};
|
||||
} dac_obj_t;
|
||||
Dac *const dac_bases[] = DAC_INSTS;
|
||||
static void dac_init(dac_obj_t *self);
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
|
@ -60,8 +65,20 @@ Dac *const dac_bases[] = DAC_INSTS;
|
|||
#define DEFAULT_DAC_VREF (1)
|
||||
#define MAX_DAC_VREF (2)
|
||||
|
||||
static dac_obj_t dac_obj[] = {
|
||||
{{&machine_dac_type}, 0, 0, DEFAULT_DAC_VREF, PIN_PA02},
|
||||
};
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
#define MAX_DAC_VALUE (4095)
|
||||
#define DEFAULT_DAC_VREF (2)
|
||||
#define MAX_DAC_VREF (3)
|
||||
|
||||
static dac_obj_t dac_obj[] = {
|
||||
{{&machine_dac_type}, 0, 0, DEFAULT_DAC_VREF, PIN_PA02},
|
||||
{{&machine_dac_type}, 1, 0, DEFAULT_DAC_VREF, PIN_PA05},
|
||||
};
|
||||
// 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
|
||||
|
@ -69,20 +86,61 @@ 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
|
||||
|
||||
#endif // defined SAMD21 or SAMD51
|
||||
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
|
||||
void dac_irq_handler(int dma_channel) {
|
||||
dac_obj_t *self;
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
DMAC->CHID.reg = dma_channel;
|
||||
DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL;
|
||||
self = &dac_obj[0];
|
||||
if (self->count > 1) {
|
||||
self->count -= 1;
|
||||
dma_desc[self->dma_channel].BTCTRL.reg |= DMAC_BTCTRL_VALID;
|
||||
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
} else {
|
||||
self->busy = false;
|
||||
if (self->callback != MP_OBJ_NULL) {
|
||||
mp_sched_schedule(self->callback, self);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
DMAC->Channel[dma_channel].CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL;
|
||||
if (dac_obj[0].dma_channel == dma_channel) {
|
||||
self = &dac_obj[0];
|
||||
} else {
|
||||
self = &dac_obj[1];
|
||||
}
|
||||
if (self->count > 1) {
|
||||
self->count -= 1;
|
||||
dma_desc[self->dma_channel].BTCTRL.reg |= DMAC_BTCTRL_VALID;
|
||||
DMAC->Channel[self->dma_channel].CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
} else {
|
||||
self->busy = false;
|
||||
if (self->callback != MP_OBJ_NULL) {
|
||||
mp_sched_schedule(self->callback, self);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#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 *all_args) {
|
||||
|
||||
enum { ARG_id, ARG_vref };
|
||||
enum { ARG_id, ARG_vref, ARG_callback };
|
||||
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} },
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
#endif
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
|
@ -91,10 +149,10 @@ static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
|
|||
|
||||
uint8_t id = args[ARG_id].u_int;
|
||||
dac_obj_t *self = NULL;
|
||||
if (0 <= id && id <= MP_ARRAY_SIZE(dac_obj)) {
|
||||
if (0 <= id && id < MP_ARRAY_SIZE(dac_obj)) {
|
||||
self = &dac_obj[id];
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for DAC"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid DAC ID"));
|
||||
}
|
||||
|
||||
uint8_t vref = args[ARG_vref].u_int;
|
||||
|
@ -102,54 +160,81 @@ static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
|
|||
self->vref = vref;
|
||||
}
|
||||
|
||||
Dac *dac = dac_bases[0]; // Just one DAC
|
||||
|
||||
// Init DAC
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
// Configuration SAMD21
|
||||
// Enable APBC clocks and PCHCTRL clocks; GCLK3 at 1 MHz
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_DAC;
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK3 | GCLK_CLKCTRL_ID_DAC;
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
// Reset DAC registers
|
||||
dac->CTRLA.bit.SWRST = 1;
|
||||
while (dac->CTRLA.bit.SWRST) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
// Configuration SAMD51
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz
|
||||
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
|
||||
dac->CTRLA.bit.ENABLE = 1;
|
||||
while (dac->SYNCBUSY.bit.ENABLE) {
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
self->callback = args[ARG_callback].u_obj;
|
||||
if (self->callback == mp_const_none) {
|
||||
self->callback = MP_OBJ_NULL;
|
||||
}
|
||||
self->dma_channel = -1;
|
||||
self->tc_index = -1;
|
||||
self->busy = false;
|
||||
#endif
|
||||
|
||||
self->initialized = false;
|
||||
|
||||
dac_init(self);
|
||||
// Set the port as given in self->gpio_id as DAC
|
||||
mp_hal_set_pin_mux(self->gpio_id, ALT_FCT_DAC);
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
static void dac_init(dac_obj_t *self) {
|
||||
// Init DAC
|
||||
if (self->initialized == false) {
|
||||
Dac *dac = dac_bases[0]; // Just one DAC
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
// Configuration SAMD21
|
||||
// Enable APBC clocks and PCHCTRL clocks; GCLK5 at 48 MHz
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_DAC;
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_DAC;
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
// Reset DAC registers
|
||||
dac->CTRLA.bit.SWRST = 1;
|
||||
while (dac->CTRLA.bit.SWRST) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
// Configuration SAMD51
|
||||
|
||||
// If the DAC is enabled it was already reset
|
||||
// In that case just disable it.
|
||||
if (dac->CTRLA.bit.ENABLE) {
|
||||
// Enable DAC and wait to be ready
|
||||
dac->CTRLA.bit.ENABLE = 0;
|
||||
while (dac->SYNCBUSY.bit.ENABLE) {
|
||||
}
|
||||
} else {
|
||||
// Enable APBD clocks and PCHCTRL clocks; GCLK5 at 48 MHz
|
||||
MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC;
|
||||
GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK5 | 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
|
||||
dac->CTRLA.bit.ENABLE = 1;
|
||||
while (dac->SYNCBUSY.bit.ENABLE) {
|
||||
}
|
||||
|
||||
#endif // defined SAMD21 or SAMD51
|
||||
}
|
||||
self->initialized = true;
|
||||
}
|
||||
|
||||
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=%q, vref=%d)", self->id, pin_find_by_id(self->gpio_id)->name, self->vref);
|
||||
|
@ -157,14 +242,25 @@ static void dac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t
|
|||
|
||||
static mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) {
|
||||
Dac *dac = dac_bases[0]; // Just one DAC
|
||||
dac_obj_t *self = self_in;
|
||||
|
||||
if (self->initialized == false) {
|
||||
mp_raise_OSError(MP_ENODEV);
|
||||
}
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
if (self->busy != false) {
|
||||
mp_raise_OSError(MP_EBUSY);
|
||||
}
|
||||
#endif
|
||||
|
||||
int value = mp_obj_get_int(value_in);
|
||||
|
||||
if (value < 0 || value > MAX_DAC_VALUE) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("value out of range"));
|
||||
}
|
||||
#if defined(MCU_SAMD21)
|
||||
dac->DATA.reg = value;
|
||||
#elif defined(MCU_SAMD51)
|
||||
dac_obj_t *self = self_in;
|
||||
dac->DATA[self->id].reg = value;
|
||||
#endif
|
||||
|
||||
|
@ -172,8 +268,157 @@ static mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) {
|
|||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(dac_write_obj, dac_write);
|
||||
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
|
||||
static mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) {
|
||||
Dac *dac = dac_bases[0]; // Just one DAC used
|
||||
dac_obj_t *self = args[0];
|
||||
mp_buffer_info_t src;
|
||||
|
||||
if (self->initialized == false) {
|
||||
mp_raise_OSError(MP_ENODEV);
|
||||
}
|
||||
|
||||
mp_get_buffer_raise(args[1], &src, MP_BUFFER_READ);
|
||||
if (n_args > 3) {
|
||||
self->count = mp_obj_get_int(args[3]);
|
||||
} else {
|
||||
self->count = 1;
|
||||
}
|
||||
if (src.len >= 2) {
|
||||
int freq = mp_obj_get_int(args[2]);
|
||||
if (self->tc_index == -1) {
|
||||
self->tc_index = allocate_tc_instance();
|
||||
}
|
||||
if (self->dma_channel == -1) {
|
||||
self->dma_channel = allocate_dma_channel();
|
||||
dma_init();
|
||||
dma_register_irq(self->dma_channel, dac_irq_handler);
|
||||
}
|
||||
// Configure TC; no need to check the return value
|
||||
configure_tc(self->tc_index, freq, 0);
|
||||
self->busy = true;
|
||||
|
||||
// Configure DMA for halfword output to the DAC
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
dma_desc[self->dma_channel].BTCTRL.reg =
|
||||
DMAC_BTCTRL_VALID | DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_STEPSEL |
|
||||
DMAC_BTCTRL_STEPSIZE(DMAC_BTCTRL_STEPSIZE_X1_Val);
|
||||
dma_desc[self->dma_channel].BTCNT.reg = src.len / 2;
|
||||
dma_desc[self->dma_channel].SRCADDR.reg = (uint32_t)(src.buf) + src.len;
|
||||
dma_desc[self->dma_channel].DSTADDR.reg = (uint32_t)(&dac->DATA.reg);
|
||||
if (self->count >= 1) {
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = 0; // ONE_SHOT
|
||||
} else {
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = (uint32_t)(&dma_desc[self->dma_channel].BTCTRL.reg);
|
||||
}
|
||||
|
||||
DMAC->CHID.reg = self->dma_channel;
|
||||
DMAC->CHCTRLA.reg = 0;
|
||||
while (DMAC->CHCTRLA.bit.ENABLE) {
|
||||
}
|
||||
DMAC->CHCTRLB.reg =
|
||||
DMAC_CHCTRLB_LVL(0) |
|
||||
DMAC_CHCTRLB_TRIGACT_BEAT |
|
||||
DMAC_CHCTRLB_TRIGSRC(TC3_DMAC_ID_OVF + 3 * self->tc_index);
|
||||
DMAC->CHINTENSET.reg = DMAC_CHINTFLAG_TCMPL;
|
||||
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
|
||||
NVIC_EnableIRQ(DMAC_IRQn);
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
dma_desc[self->dma_channel].BTCTRL.reg =
|
||||
DMAC_BTCTRL_VALID | DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_STEPSEL |
|
||||
DMAC_BTCTRL_STEPSIZE(DMAC_BTCTRL_STEPSIZE_X1_Val);
|
||||
dma_desc[self->dma_channel].BTCNT.reg = src.len / 2;
|
||||
dma_desc[self->dma_channel].SRCADDR.reg = (uint32_t)(src.buf) + src.len;
|
||||
dma_desc[self->dma_channel].DSTADDR.reg = (uint32_t)(&dac->DATA[self->id].reg);
|
||||
if (self->count >= 1) {
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = 0; // ONE_SHOT
|
||||
} else {
|
||||
dma_desc[self->dma_channel].DESCADDR.reg = (uint32_t)(&dma_desc[self->dma_channel].BTCTRL.reg);
|
||||
}
|
||||
|
||||
DMAC->Channel[self->dma_channel].CHCTRLA.reg =
|
||||
DMAC_CHCTRLA_BURSTLEN(DMAC_CHCTRLA_BURSTLEN_SINGLE_Val) |
|
||||
DMAC_CHCTRLA_TRIGACT(DMAC_CHCTRLA_TRIGACT_BURST_Val) |
|
||||
DMAC_CHCTRLA_TRIGSRC(TC0_DMAC_ID_OVF + 3 * self->tc_index);
|
||||
DMAC->Channel[self->dma_channel].CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;
|
||||
DMAC->Channel[self->dma_channel].CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
|
||||
|
||||
if (self->dma_channel < 4) {
|
||||
NVIC_EnableIRQ(DMAC_0_IRQn + self->dma_channel);
|
||||
} else {
|
||||
NVIC_EnableIRQ(DMAC_4_IRQn);
|
||||
}
|
||||
|
||||
#endif // defined SAMD21 or SAMD51
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed);
|
||||
|
||||
static void dac_deinit_channel(dac_obj_t *self) {
|
||||
self->initialized = false;
|
||||
if (self->dma_channel >= 0) {
|
||||
dac_stop_dma(self->dma_channel, true);
|
||||
free_dma_channel(self->dma_channel);
|
||||
self->dma_channel = -1;
|
||||
}
|
||||
if (self->tc_index >= 0) {
|
||||
free_tc_instance(self->tc_index);
|
||||
self->tc_index = -1;
|
||||
}
|
||||
self->callback = MP_OBJ_NULL;
|
||||
self->busy = false;
|
||||
}
|
||||
|
||||
// Reset DAC and clear the DMA channel entries in the DAC objects.
|
||||
void dac_deinit_all(void) {
|
||||
// Reset the DAC to lower the current consumption as SAMD21
|
||||
dac_bases[0]->CTRLA.bit.SWRST = 1;
|
||||
dac_deinit_channel(&dac_obj[0]);
|
||||
#if defined(MCU_SAMD51)
|
||||
dac_deinit_channel(&dac_obj[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
static mp_obj_t dac_deinit(mp_obj_t self_in) {
|
||||
dac_deinit_all();
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(dac_deinit_obj, dac_deinit);
|
||||
|
||||
// busy() : Report, if the DAC device is busy
|
||||
static mp_obj_t machine_dac_busy(mp_obj_t self_in) {
|
||||
dac_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return self->busy ? mp_const_true : mp_const_false;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(machine_dac_busy_obj, machine_dac_busy);
|
||||
#else
|
||||
|
||||
void dac_deinit_all(void) {
|
||||
// Reset the DAC to lower the current consumption as SAMD21
|
||||
dac_bases[0]->CTRLA.bit.SWRST = 1;
|
||||
dac_obj[0].initialized = false;
|
||||
#if defined(MCU_SAMD51)
|
||||
dac_obj[1].initialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t dac_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&dac_write_obj) },
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED
|
||||
{ MP_ROM_QSTR(MP_QSTR_busy), MP_ROM_PTR(&machine_dac_busy_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&dac_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table);
|
||||
|
|
|
@ -35,9 +35,12 @@
|
|||
#include "shared/runtime/softtimer.h"
|
||||
#include "shared/tinyusb/mp_usbd.h"
|
||||
#include "clock_config.h"
|
||||
#include "dma_manager.h"
|
||||
#include "tc_manager.h"
|
||||
|
||||
extern uint8_t _sstack, _estack, _sheap, _eheap;
|
||||
extern void adc_deinit_all(void);
|
||||
extern void dac_deinit_all(void);
|
||||
extern void pin_irq_deinit_all(void);
|
||||
extern void pwm_deinit_all(void);
|
||||
extern void sercom_deinit_all(void);
|
||||
|
@ -87,9 +90,18 @@ void samd_main(void) {
|
|||
|
||||
soft_reset_exit:
|
||||
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
|
||||
#if MICROPY_HW_DMA_MANAGER
|
||||
dma_deinit();
|
||||
#endif
|
||||
#if MICROPY_HW_TC_MANAGER
|
||||
tc_deinit();
|
||||
#endif
|
||||
#if MICROPY_PY_MACHINE_ADC
|
||||
adc_deinit_all();
|
||||
#endif
|
||||
#if MICROPY_PY_MACHINE_DAC
|
||||
dac_deinit_all();
|
||||
#endif
|
||||
pin_irq_deinit_all();
|
||||
#if MICROPY_PY_MACHINE_PWM
|
||||
pwm_deinit_all();
|
||||
|
|
|
@ -70,6 +70,13 @@ unsigned long trng_random_u32(int delay);
|
|||
#define MICROPY_PY_DEFLATE (SAMD21_EXTRA_FEATURES)
|
||||
#define MICROPY_PY_ONEWIRE (SAMD21_EXTRA_FEATURES)
|
||||
|
||||
#ifndef MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
#define MICROPY_PY_MACHINE_ADC_READ_TIMED (SAMD21_EXTRA_FEATURES)
|
||||
#endif
|
||||
#ifndef MICROPY_PY_MACHINE_DAC_TIMED
|
||||
#define MICROPY_PY_MACHINE_DAC_TIMED (SAMD21_EXTRA_FEATURES)
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_MACHINE_PIN_BOARD_CPU
|
||||
#define MICROPY_PY_MACHINE_PIN_BOARD_CPU (1)
|
||||
#endif
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
unsigned long trng_random_u32(void);
|
||||
|
||||
// fatfs configuration used in ffconf.h
|
||||
#define MICROPY_FATFS_ENABLE_LFN (1)
|
||||
#define MICROPY_FATFS_RPATH (2)
|
||||
#define MICROPY_FATFS_MAX_SS (4096)
|
||||
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
|
||||
#define MICROPY_FATFS_ENABLE_LFN (1)
|
||||
#define MICROPY_FATFS_RPATH (2)
|
||||
#define MICROPY_FATFS_MAX_SS (4096)
|
||||
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
|
||||
|
||||
#define VFS_BLOCK_SIZE_BYTES (1536) //
|
||||
|
||||
|
@ -30,6 +30,12 @@ unsigned long trng_random_u32(void);
|
|||
#ifndef MICROPY_HW_UART_RTSCTS
|
||||
#define MICROPY_HW_UART_RTSCTS (1)
|
||||
#endif
|
||||
#ifndef MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
#define MICROPY_PY_MACHINE_ADC_READ_TIMED (1)
|
||||
#endif
|
||||
#ifndef MICROPY_PY_MACHINE_DAC_TIMED
|
||||
#define MICROPY_PY_MACHINE_DAC_TIMED (1)
|
||||
#endif
|
||||
|
||||
#define CPU_FREQ (120000000)
|
||||
#define DFLL48M_FREQ (48000000)
|
||||
|
|
|
@ -128,6 +128,11 @@
|
|||
#define MICROPY_PY_MACHINE_WDT_TIMEOUT_MS (1)
|
||||
#define MICROPY_PLATFORM_VERSION "ASF4"
|
||||
|
||||
#if MICROPY_PY_MACHINE_DAC_TIMED || MICROPY_PY_MACHINE_ADC_READ_TIMED
|
||||
#define MICROPY_HW_DMA_MANAGER (1)
|
||||
#define MICROPY_HW_TC_MANAGER (1)
|
||||
#endif
|
||||
|
||||
#define MP_STATE_PORT MP_STATE_VM
|
||||
|
||||
// Miscellaneous settings
|
||||
|
|
|
@ -164,6 +164,51 @@ void Sercom7_Handler(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// DMAC IRQ handler support
|
||||
#if defined(MCU_SAMD21)
|
||||
#define DMAC_FIRST_CHANNEL 0
|
||||
#else
|
||||
#define DMAC_FIRST_CHANNEL 4
|
||||
#endif
|
||||
|
||||
void (*dma_irq_handler_table[DMAC_CH_NUM])(int num) = {};
|
||||
|
||||
void dma_register_irq(int dma_channel, void (*dma_irq_handler)) {
|
||||
if (dma_channel < DMAC_CH_NUM) {
|
||||
dma_irq_handler_table[dma_channel] = dma_irq_handler;
|
||||
}
|
||||
}
|
||||
|
||||
void DMAC0_Handler(void) {
|
||||
if (dma_irq_handler_table[0]) {
|
||||
dma_irq_handler_table[0](0);
|
||||
}
|
||||
}
|
||||
void DMAC1_Handler(void) {
|
||||
if (dma_irq_handler_table[1]) {
|
||||
dma_irq_handler_table[1](1);
|
||||
}
|
||||
}
|
||||
void DMAC2_Handler(void) {
|
||||
if (dma_irq_handler_table[2]) {
|
||||
dma_irq_handler_table[2](2);
|
||||
}
|
||||
}
|
||||
void DMAC3_Handler(void) {
|
||||
if (dma_irq_handler_table[3]) {
|
||||
dma_irq_handler_table[3](3);
|
||||
}
|
||||
}
|
||||
void DMACn_Handler(void) {
|
||||
for (uint32_t mask = 1 << DMAC_FIRST_CHANNEL, dma_channel = DMAC_FIRST_CHANNEL;
|
||||
dma_channel < DMAC_CH_NUM;
|
||||
mask <<= 1, dma_channel += 1) {
|
||||
if ((DMAC->INTSTATUS.reg & mask) && dma_irq_handler_table[dma_channel]) {
|
||||
dma_irq_handler_table[dma_channel](dma_channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
const ISR isr_vector[] __attribute__((section(".isr_vector"))) = {
|
||||
(ISR)&_estack,
|
||||
|
@ -188,7 +233,7 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = {
|
|||
0, // 3 Real-Time Counter (RTC)
|
||||
&EIC_Handler, // 4 External Interrupt Controller (EIC)
|
||||
0, // 5 Non-Volatile Memory Controller (NVMCTRL)
|
||||
0, // 6 Direct Memory Access Controller (DMAC)
|
||||
&DMACn_Handler, // 6 Direct Memory Access Controller (DMAC)
|
||||
USB_Handler_wrapper,// 7 Universal Serial Bus (USB)
|
||||
0, // 8 Event System Interface (EVSYS)
|
||||
&Sercom0_Handler, // 9 Serial Communication Interface 0 (SERCOM0)
|
||||
|
@ -261,11 +306,11 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = {
|
|||
0, // 28 Frequency Meter (FREQM)
|
||||
0, // 29 Non-Volatile Memory Controller (NVMCTRL): NVMCTRL_0 - _7
|
||||
0, // 30 Non-Volatile Memory Controller (NVMCTRL): NVMCTRL_8 - _10
|
||||
0, // 31 Direct Memory Access Controller (DMAC): DMAC_SUSP_0, DMAC_TCMPL_0, DMAC_TERR_0
|
||||
0, // 32 Direct Memory Access Controller (DMAC): DMAC_SUSP_1, DMAC_TCMPL_1, DMAC_TERR_1
|
||||
0, // 33 Direct Memory Access Controller (DMAC): DMAC_SUSP_2, DMAC_TCMPL_2, DMAC_TERR_2
|
||||
0, // 34 Direct Memory Access Controller (DMAC): DMAC_SUSP_3, DMAC_TCMPL_3, DMAC_TERR_3
|
||||
0, // 35 Direct Memory Access Controller (DMAC): DMAC_SUSP_4 - _31, DMAC_TCMPL_4 _31, DMAC_TERR_4- _31
|
||||
&DMAC0_Handler, // 31 Direct Memory Access Controller (DMAC): DMAC_SUSP_0, DMAC_TCMPL_0, DMAC_TERR_0
|
||||
&DMAC1_Handler, // 32 Direct Memory Access Controller (DMAC): DMAC_SUSP_1, DMAC_TCMPL_1, DMAC_TERR_1
|
||||
&DMAC2_Handler, // 33 Direct Memory Access Controller (DMAC): DMAC_SUSP_2, DMAC_TCMPL_2, DMAC_TERR_2
|
||||
&DMAC3_Handler, // 34 Direct Memory Access Controller (DMAC): DMAC_SUSP_3, DMAC_TCMPL_3, DMAC_TERR_3
|
||||
&DMACn_Handler, // 35 Direct Memory Access Controller (DMAC): DMAC_SUSP_4 - _31, DMAC_TCMPL_4 _31, DMAC_TERR_4- _31
|
||||
0, // 36 Event System Interface (EVSYS): EVSYS_EVD_0, EVSYS_OVR_0
|
||||
0, // 37 Event System Interface (EVSYS): EVSYS_EVD_1, EVSYS_OVR_1
|
||||
0, // 38 Event System Interface (EVSYS): EVSYS_EVD_2, EVSYS_OVR_2
|
||||
|
|
|
@ -68,7 +68,6 @@ static void usb_init(void) {
|
|||
void init_us_counter(void) {
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
PM->APBCMASK.bit.TC3_ = 1; // Enable TC3 clock
|
||||
PM->APBCMASK.bit.TC4_ = 1; // Enable TC4 clock
|
||||
// Select multiplexer generic clock source and enable.
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK3 | GCLK_CLKCTRL_ID_TC4_TC5;
|
||||
|
|
|
@ -43,6 +43,7 @@ void USB_Handler_wrapper(void);
|
|||
|
||||
void sercom_enable(Sercom *spi, int state);
|
||||
void sercom_register_irq(int sercom_id, void (*sercom_irq_handler));
|
||||
void dma_register_irq(int dma_channel, void (*dma_irq_handler));
|
||||
|
||||
// Each device has a unique 128-bit serial number. The uniqueness of the serial number is
|
||||
// guaranteed only when using all 128 bits.
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Robert Hammelrath
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "py/mpconfig.h"
|
||||
#include "sam.h"
|
||||
#include "tc_manager.h"
|
||||
|
||||
#if MICROPY_HW_TC_MANAGER
|
||||
|
||||
// List of channel flags: true: channel used, false: channel available
|
||||
// Two Tc instances are used by the usec counter and cannot be assigned.
|
||||
#if defined(MCU_SAMD21)
|
||||
static bool instance_flag[TC_INST_NUM] = {false, true, true};
|
||||
#elif defined(MCU_SAMD51)
|
||||
static bool instance_flag[TC_INST_NUM] = {true, true};
|
||||
#endif
|
||||
Tc *tc_instance_list[TC_INST_NUM] = TC_INSTS;
|
||||
extern const uint16_t prescaler_table[];
|
||||
|
||||
// allocate_tc_instance(): retrieve an available instance. Return the pointer or NULL
|
||||
int allocate_tc_instance(void) {
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(instance_flag); i++) {
|
||||
if (instance_flag[i] == false) { // available
|
||||
instance_flag[i] = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("no Timer available"));
|
||||
}
|
||||
|
||||
// free_tc_instance(n): Declare instance as free
|
||||
void free_tc_instance(int tc_index) {
|
||||
if (tc_index >= 0 && tc_index < MP_ARRAY_SIZE(instance_flag)) {
|
||||
instance_flag[tc_index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int configure_tc(int tc_index, int freq, int event) {
|
||||
uint32_t clock = DFLL48M_FREQ; // Use the fixed 48M clock
|
||||
|
||||
Tc *tc;
|
||||
if (tc_index < MP_ARRAY_SIZE(instance_flag)) {
|
||||
tc = tc_instance_list[tc_index];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
// Check for the right prescaler
|
||||
uint8_t index;
|
||||
uint32_t period;
|
||||
|
||||
for (index = 0; index < 8; index++) {
|
||||
period = clock / prescaler_table[index] / freq;
|
||||
if (period < (1 << 16)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(MCU_SAMD21)
|
||||
|
||||
// Set up the clocks
|
||||
if (tc == TC3) {
|
||||
PM->APBCMASK.bit.TC3_ = 1; // Enable TC3 clock
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_TCC2_TC3;
|
||||
#if TC_INST_NUM > 3
|
||||
} else {
|
||||
if (tc == TC6) {
|
||||
PM->APBCMASK.bit.TC6_ = 1; // Enable TC6 clock
|
||||
} else if (tc == TC7) {
|
||||
PM->APBCMASK.bit.TC7_ = 1; // Enable TC7 clock
|
||||
}
|
||||
// Select multiplexer generic clock source and enable.
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_TC6_TC7;
|
||||
#endif // TC_INST_NUM > 3
|
||||
}
|
||||
// Wait while it updates synchronously.
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
// Configure the timer.
|
||||
tc->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
|
||||
while (tc->COUNT16.CTRLA.bit.SWRST || tc->COUNT16.STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
tc->COUNT16.CTRLA.reg = TC_CTRLA_PRESCALER(index) |
|
||||
TC_CTRLA_MODE_COUNT16 | TC_CTRLA_RUNSTDBY |
|
||||
TC_CTRLA_WAVEGEN_MFRQ;
|
||||
tc->COUNT16.CC[0].reg = period;
|
||||
if (event) {
|
||||
tc->COUNT16.EVCTRL.reg = event;
|
||||
}
|
||||
|
||||
tc->COUNT16.CTRLA.bit.ENABLE = 1;
|
||||
while (tc->COUNT16.STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
|
||||
#elif defined(MCU_SAMD51)
|
||||
|
||||
int gclk_id = TC2_GCLK_ID;
|
||||
// Enable MCLK
|
||||
switch (tc_index) {
|
||||
case 2:
|
||||
MCLK->APBBMASK.bit.TC2_ = 1; // Enable TC2 clock
|
||||
gclk_id = TC2_GCLK_ID;
|
||||
break;
|
||||
case 3:
|
||||
MCLK->APBBMASK.bit.TC3_ = 1; // Enable TC3 clock
|
||||
gclk_id = TC3_GCLK_ID;
|
||||
break;
|
||||
#if TC_INST_NUM > 4
|
||||
case 4:
|
||||
MCLK->APBCMASK.bit.TC4_ = 1; // Enable TC4 clock
|
||||
gclk_id = TC4_GCLK_ID;
|
||||
break;
|
||||
case 5:
|
||||
MCLK->APBCMASK.bit.TC5_ = 1; // Enable TC5 clock
|
||||
gclk_id = TC5_GCLK_ID;
|
||||
break;
|
||||
#if TC_INST_NUM > 6
|
||||
case 6:
|
||||
MCLK->APBDMASK.bit.TC6_ = 1; // Enable TC6 clock
|
||||
gclk_id = TC6_GCLK_ID;
|
||||
break;
|
||||
case 7:
|
||||
MCLK->APBDMASK.bit.TC7_ = 1; // Enable TC7 clock
|
||||
gclk_id = TC7_GCLK_ID;
|
||||
break;
|
||||
#endif // TC_INST_NUM > 6
|
||||
#endif // TC_INST_NUM > 4
|
||||
}
|
||||
// Enable the 48Mhz clock.
|
||||
GCLK->PCHCTRL[gclk_id].reg = GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN;
|
||||
while (GCLK->PCHCTRL[gclk_id].bit.CHEN == 0) {
|
||||
}
|
||||
// Configure the timer.
|
||||
tc->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
|
||||
while (tc->COUNT16.SYNCBUSY.bit.SWRST) {
|
||||
}
|
||||
tc->COUNT16.CTRLA.reg = TC_CTRLA_PRESCALER(index) |
|
||||
TC_CTRLA_MODE_COUNT16 | TC_CTRLA_RUNSTDBY | TC_CTRLA_PRESCSYNC_PRESC;
|
||||
tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
|
||||
tc->COUNT16.CC[0].reg = period;
|
||||
tc->COUNT16.CTRLA.bit.ENABLE = 1;
|
||||
while (tc->COUNT16.SYNCBUSY.bit.ENABLE) {
|
||||
}
|
||||
|
||||
#endif // SAMD21 or SAMD51
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tc_deinit(void) {
|
||||
memset((uint8_t *)instance_flag, 0, sizeof(instance_flag));
|
||||
// The tc instances used by the us counter have to be locked.
|
||||
// That's TC4 and TC5 for SAMD21 with the list starting at TC3
|
||||
// and TC0 and TC1 for SAMD51, with the list starting at TC0
|
||||
#if defined(MCU_SAMD21)
|
||||
instance_flag[1] = instance_flag[2] = true;
|
||||
#elif defined(MCU_SAMD51)
|
||||
instance_flag[0] = instance_flag[1] = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Robert Hammelrath
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_SAMD_TCINSTANCE_H
|
||||
#define MICROPY_INCLUDED_SAMD_TCINSTANCE_H
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
|
||||
int allocate_tc_instance(void);
|
||||
void free_tc_instance(int tc_index);
|
||||
int configure_tc(int tc_index, int freq, int event);
|
||||
void tc_deinit(void);
|
||||
|
||||
extern Tc *tc_instance_list[];
|
||||
|
||||
#endif // MICROPY_INCLUDED_SAMD_TCINSTANCE_H
|
Ładowanie…
Reference in New Issue