pull/9624/merge
Robert Hammelrath 2024-04-22 21:13:29 +08:00 zatwierdzone przez GitHub
commit e29736d6f4
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
16 zmienionych plików z 1203 dodań i 125 usunięć

Wyświetl plik

@ -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
----------------

Wyświetl plik

@ -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.

Wyświetl plik

@ -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 \

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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;

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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