From ea9183bbd068f9e78014a0e8045fa11d2c234794 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 5 Sep 2022 11:24:03 +0200 Subject: [PATCH 01/14] samd/DMA: Add a DMA manager. Used for allocation of DMA channels. It will be needed for planned modules and methods like adc_timed(), dac_timed(), I2S. It includes management code for DMA IRQ handlers, similar to what was made for Sercom. Signed-off-by: robert-hh --- ports/samd/dma_manager.c | 131 +++++++++++++++++++++++++++++++++++++++ ports/samd/dma_manager.h | 38 ++++++++++++ ports/samd/samd_isr.c | 57 +++++++++++++++-- ports/samd/samd_soc.h | 1 + 4 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 ports/samd/dma_manager.c create mode 100644 ports/samd/dma_manager.h diff --git a/ports/samd/dma_manager.c b/ports/samd/dma_manager.c new file mode 100644 index 0000000000000..916b629a52d60 --- /dev/null +++ b/ports/samd/dma_manager.c @@ -0,0 +1,131 @@ +/* + * 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 +#include "py/mpconfig.h" +#include "sam.h" +#include "dma_manager.h" +#include "samd_soc.h" + +// 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 +} diff --git a/ports/samd/dma_manager.h b/ports/samd/dma_manager.h new file mode 100644 index 0000000000000..33c950ebd7e3d --- /dev/null +++ b/ports/samd/dma_manager.h @@ -0,0 +1,38 @@ +/* + * 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); + +extern volatile DmacDescriptor dma_desc[]; + +#endif // MICROPY_INCLUDED_SAMD_DMACHANNEL_H diff --git a/ports/samd/samd_isr.c b/ports/samd/samd_isr.c index 7c4c1d060f768..6888d8a1dc635 100644 --- a/ports/samd/samd_isr.c +++ b/ports/samd/samd_isr.c @@ -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 diff --git a/ports/samd/samd_soc.h b/ports/samd/samd_soc.h index 707d2f8edfa98..2477775735c9b 100644 --- a/ports/samd/samd_soc.h +++ b/ports/samd/samd_soc.h @@ -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. From b28fb652ba77a7dd901a99b95aa76c2957310b51 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 5 Sep 2022 11:25:28 +0200 Subject: [PATCH 02/14] samd/TC_timer: Add a tc_manager function set. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are use to allocate, free and configure a set of TC counter instances. The SAMxx MCU have between 3 to 5 (SAMD21) and 4 to 8 (SAMD51) TC instances. Two of them are used for the µs counter, the remaining 1 - 6 instances are administered here for use by various functions, like timed DMA transfers. Signed-off-by: robert-hh --- ports/samd/tc_manager.c | 178 ++++++++++++++++++++++++++++++++++++++++ ports/samd/tc_manager.h | 39 +++++++++ 2 files changed, 217 insertions(+) create mode 100644 ports/samd/tc_manager.c create mode 100644 ports/samd/tc_manager.h diff --git a/ports/samd/tc_manager.c b/ports/samd/tc_manager.c new file mode 100644 index 0000000000000..10cad8060ae15 --- /dev/null +++ b/ports/samd/tc_manager.c @@ -0,0 +1,178 @@ +/* + * 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 +#include "py/mpconfig.h" +#include "sam.h" +#include "tc_manager.h" + +// 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) { + 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; + + 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 +} diff --git a/ports/samd/tc_manager.h b/ports/samd/tc_manager.h new file mode 100644 index 0000000000000..d90959fa4956c --- /dev/null +++ b/ports/samd/tc_manager.h @@ -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); +void tc_deinit(void); + +extern Tc *tc_instance_list[]; + +#endif // MICROPY_INCLUDED_SAMD_TCINSTANCE_H From 612320883fc8dfba47adafefa8216e9745667676 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 5 Sep 2022 11:29:08 +0200 Subject: [PATCH 03/14] samd/DAC: Add the dac.write_timed(). Call: dac.write_timed(data, freq [, count]) dac.deinit() Working range for dac_timed(): SAMD21: 1 Hz - 100 kHz (1 MHz clock, 10 bit) SAMD51: 1 Hz - ~500 kHz (8 MHz clock, 12 bit) The buffer has to be a byte array or a halfword array, and the data is sent once. The default for count is 1. If set to a value > 0, the data will be transmitted count times. If set to 0 or < 0, the date will be transmitted until deliberately stopped. The playback can be stopped with dac.deinit(). dac.deinit() just releases the timer and DMA channel needed by dac_timed(). The DAC object itself does not have to be released. Signed-off-by: robert-hh --- ports/samd/Makefile | 2 + ports/samd/dma_manager.h | 1 + ports/samd/machine_dac.c | 265 +++++++++++++++++++++++++++++---------- ports/samd/main.c | 4 + ports/samd/samd_soc.c | 1 - 5 files changed, 205 insertions(+), 68 deletions(-) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 005664f178d3c..ca25e63be15d4 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -119,6 +119,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 \ @@ -137,6 +138,7 @@ SRC_C += \ samd_soc.c \ samd_spiflash.c \ usbd.c \ + tc_manager.c \ SHARED_SRC_C += \ drivers/dht/dht.c \ diff --git a/ports/samd/dma_manager.h b/ports/samd/dma_manager.h index 33c950ebd7e3d..3dea43e41e755 100644 --- a/ports/samd/dma_manager.h +++ b/ports/samd/dma_manager.h @@ -32,6 +32,7 @@ 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[]; diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index 7dcc654644d79..d9aad2e1d44d2 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -36,32 +36,39 @@ #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; + bool initialized; mp_hal_pin_obj_t gpio_id; uint8_t vref; + int8_t dma_channel; + int8_t tc_index; + uint32_t count; } 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}, - #endif -}; Dac *const dac_bases[] = DAC_INSTS; +STATIC void dac_init(dac_obj_t *self, Dac *dac); #if defined(MCU_SAMD21) +STATIC dac_obj_t dac_obj[] = { + {{&machine_dac_type}, 0, PIN_PA02}, +}; + #define MAX_DAC_VALUE (1023) #define DEFAULT_DAC_VREF (1) #define MAX_DAC_VREF (2) #elif defined(MCU_SAMD51) +STATIC dac_obj_t dac_obj[] = { + {{&machine_dac_type}, 0, PIN_PA02}, + {{&machine_dac_type}, 1, 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 @@ -72,12 +79,39 @@ static uint8_t dac_vref_table[] = { #define MAX_DAC_VALUE (4095) #define DEFAULT_DAC_VREF (2) #define MAX_DAC_VREF (3) -static bool dac_init[2] = {false, false}; -#endif +#endif // defined SAMD21 or SAMD51 + +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; + } + + #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; + } + #endif +} -static mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, +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 }; @@ -92,10 +126,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 id for DAC")); + mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for DAC")); } uint8_t vref = args[ARG_vref].u_int; @@ -103,88 +137,77 @@ 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 register block - - // initialize DAC + Dac *dac = dac_bases[0]; // Just one DAC + dac_init(self, dac); + // Set the port as given in self->gpio_id as DAC + mp_hal_set_pin_mux(self->gpio_id, ALT_FCT_DAC); - #if defined(MCU_SAMD21) + return MP_OBJ_FROM_PTR(self); +} - // 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) { - } +STATIC void dac_init(dac_obj_t *self, Dac *dac) { + // Init DAC + if (self->initialized == false) { + #if defined(MCU_SAMD21) - #elif defined(MCU_SAMD51) + // 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) { + } - // Configuration SAMD51 - // Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz + #elif defined(MCU_SAMD51) - if (!(dac_init[0] | dac_init[1])) { + // Configuration SAMD51 + // Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC; - GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK3 | \ - GCLK_PCHCTRL_CHEN; + 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; - } - - // Modify DAC config - requires disabling see Section 47.6.2.3 of data sheet - if (!dac_init[self->id]) { - // Disable DAC and wait - dac->CTRLA.bit.ENABLE = 0; - while (dac->SYNCBUSY.bit.ENABLE) { - } - - // Modify configuration - dac->DACCTRL[self->id].reg = DAC_DACCTRL_ENABLE | \ - DAC_DACCTRL_REFRESH(2) | DAC_DACCTRL_CCTRL_CC12M; - dac->DATA[self->id].reg = 0; - dac_init[self->id] = true; - - // Enable DAC and wait + // Enable DAC and wait to be ready dac->CTRLA.bit.ENABLE = 1; while (dac->SYNCBUSY.bit.ENABLE) { } - } - - #endif - // 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); + #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) { +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); } -static mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) { +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; 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")); } + // Re-init, if required + dac_init(self, dac); #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 @@ -192,11 +215,119 @@ 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); -static const mp_rom_map_elem_t dac_locals_dict_table[] = { +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; + // Re-init, if required + dac_init(self, dac); + + 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->dma_channel == -1) { + self->dma_channel = allocate_dma_channel(); + dma_init(); + dma_register_irq(self->dma_channel, dac_irq_handler); + } + if (self->tc_index == -1) { + self->tc_index = allocate_tc_instance(); + } + // Configure TC; no need to check the return value + configure_tc(self->tc_index, freq); + // 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 mp_obj_t dac_deinit(mp_obj_t self_in) { + dac_obj_t *self = self_in; + self->initialized = false; + // Reset the DAC to lower the current consumption as SAMD21 + dac_bases[0]->CTRLA.bit.SWRST = 1; + 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; + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(dac_deinit_obj, dac_deinit); + +STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&dac_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&dac_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) }, }; -static MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( machine_dac_type, diff --git a/ports/samd/main.c b/ports/samd/main.c index a7da95582f76c..df06f0b295946 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -36,6 +36,8 @@ #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); @@ -89,6 +91,8 @@ void samd_main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + dma_deinit(); + tc_deinit(); #if MICROPY_PY_MACHINE_ADC adc_deinit_all(); #endif diff --git a/ports/samd/samd_soc.c b/ports/samd/samd_soc.c index e78032513c216..c4d45724fc3be 100644 --- a/ports/samd/samd_soc.c +++ b/ports/samd/samd_soc.c @@ -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; From c08f09d9df2910ec2106dcf07fd7839f86260a4f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 6 Sep 2022 16:53:45 +0200 Subject: [PATCH 04/14] samd/ADC: Add a adc.read_timed() method. Call: adc.read_timed(buffer, freq) buffer must be preallocated. The size determines the number of 16 bit words to be read. The numeric range of the results is that of the raw ADC. The call returns immediately, and the data transfer is done by DMA. The caller must wait sufficiently long until the data is sampled and can be noticed by a callback. No internal checks are made for a too-high freq value. Read speeds depends on Average and bit length setting: SAMD21: Max. 350kS/s (8 bit, Average 1) SAMD51: Max. 1 MS/s (8 bit, Average 1) Signed-off-by: robert-hh --- ports/samd/dma_manager.c | 2 +- ports/samd/machine_adc.c | 147 ++++++++++++++++++++++++++++++++++++--- ports/samd/machine_dac.c | 18 ++--- ports/samd/tc_manager.c | 5 +- ports/samd/tc_manager.h | 2 +- 5 files changed, 153 insertions(+), 21 deletions(-) diff --git a/ports/samd/dma_manager.c b/ports/samd/dma_manager.c index 916b629a52d60..868606e66b7f1 100644 --- a/ports/samd/dma_manager.c +++ b/ports/samd/dma_manager.c @@ -52,7 +52,7 @@ static volatile DmacDescriptor dma_write_back[MICROPY_HW_DMA_CHANNELS] __attribu // List of channel flags: true: channel used, false: channel available static bool channel_list[MICROPY_HW_DMA_CHANNELS]; -STATIC bool dma_initialized = false; +static bool dma_initialized = false; // allocate_channel(): retrieve an available channel. Return the number or -1 int allocate_dma_channel(void) { diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index 665af6f6f2785..afd9c5d29a5c5 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -31,6 +31,10 @@ #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 _machine_adc_obj_t { mp_obj_base_t base; @@ -39,6 +43,8 @@ typedef struct _machine_adc_obj_t { uint8_t avg; uint8_t bits; uint8_t vref; + int8_t dma_channel; + int8_t tc_index; } machine_adc_obj_t; #define DEFAULT_ADC_BITS 12 @@ -85,6 +91,20 @@ static uint8_t resolution[] = { extern mp_int_t log2i(mp_int_t num); +// 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; + ADC->EVCTRL.bit.STARTEI = 0; + + #elif defined(MCU_SAMD51) + DMAC->Channel[dma_channel].CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL; + #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); @@ -116,8 +136,9 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args 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); @@ -147,6 +168,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,9 +184,107 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { return adc->RESULT.reg * (65536 / (1 << self->bits)); } +static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t freq_in) { + machine_adc_obj_t *self = self_in; + 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) { + int freq = mp_obj_get_int(freq_in); + if (self->dma_channel == -1) { + self->dma_channel = allocate_dma_channel(); + dma_init(); + } + if (self->tc_index == -1) { + self->tc_index = allocate_tc_instance(); + } + // 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) + // dma irq just for SAMD21 to stop the timer based acquisition + dma_register_irq(self->dma_channel, adc_irq_handler); + 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; + + #elif defined(MCU_SAMD51) + configure_tc(self->tc_index, freq, 0); + + // 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) { + } + + 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].CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; + + #endif // defined SAMD21 or SAMD51 + + } + return mp_const_none; +} + // 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 (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; + } } void adc_deinit_all(void) { @@ -175,9 +302,9 @@ static void adc_init(machine_adc_obj_t *self) { #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 +317,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 +330,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 +357,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 diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index d9aad2e1d44d2..01dcc328f176a 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -51,11 +51,11 @@ typedef struct _dac_obj_t { uint32_t count; } dac_obj_t; Dac *const dac_bases[] = DAC_INSTS; -STATIC void dac_init(dac_obj_t *self, Dac *dac); +static void dac_init(dac_obj_t *self, Dac *dac); #if defined(MCU_SAMD21) -STATIC dac_obj_t dac_obj[] = { +static dac_obj_t dac_obj[] = { {{&machine_dac_type}, 0, PIN_PA02}, }; @@ -65,7 +65,7 @@ STATIC dac_obj_t dac_obj[] = { #elif defined(MCU_SAMD51) -STATIC dac_obj_t dac_obj[] = { +static dac_obj_t dac_obj[] = { {{&machine_dac_type}, 0, PIN_PA02}, {{&machine_dac_type}, 1, PIN_PA05}, }; @@ -190,7 +190,7 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { self->initialized = true; } -STATIC void dac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +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); } @@ -215,7 +215,7 @@ 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); -STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { +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; @@ -239,7 +239,7 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { self->tc_index = allocate_tc_instance(); } // Configure TC; no need to check the return value - configure_tc(self->tc_index, freq); + configure_tc(self->tc_index, freq, 0); // Configure DMA for halfword output to the DAC #if defined(MCU_SAMD21) @@ -301,9 +301,9 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); -STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { +static mp_obj_t dac_deinit(mp_obj_t self_in) { dac_obj_t *self = self_in; self->initialized = false; // Reset the DAC to lower the current consumption as SAMD21 @@ -321,7 +321,7 @@ STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(dac_deinit_obj, dac_deinit); -STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { +static const mp_rom_map_elem_t dac_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&dac_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&dac_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) }, diff --git a/ports/samd/tc_manager.c b/ports/samd/tc_manager.c index 10cad8060ae15..fb0c3b77ff48d 100644 --- a/ports/samd/tc_manager.c +++ b/ports/samd/tc_manager.c @@ -57,7 +57,7 @@ void free_tc_instance(int tc_index) { } } -int configure_tc(int tc_index, int freq) { +int configure_tc(int tc_index, int freq, int event) { uint32_t clock = DFLL48M_FREQ; // Use the fixed 48M clock Tc *tc; @@ -105,6 +105,9 @@ int configure_tc(int tc_index, int freq) { 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) { diff --git a/ports/samd/tc_manager.h b/ports/samd/tc_manager.h index d90959fa4956c..04064f89e62cf 100644 --- a/ports/samd/tc_manager.h +++ b/ports/samd/tc_manager.h @@ -31,7 +31,7 @@ int allocate_tc_instance(void); void free_tc_instance(int tc_index); -int configure_tc(int tc_index, int freq); +int configure_tc(int tc_index, int freq, int event); void tc_deinit(void); extern Tc *tc_instance_list[]; From 5719f2d10b2b4e43628fbf124c25f6077b32e10d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 13 Sep 2022 18:50:42 +0200 Subject: [PATCH 05/14] samd/DAC: Add a callback keyword option to machine.DAC(). The callback is called when a dac_timed() sequence finishes. It will be reset with callback=None or omitting the callback option in the constructor. Side change: Set the clock freq. to 48Mhz. Signed-off-by: robert-hh --- ports/samd/machine_dac.c | 62 +++++++++++++++++++++++++++++----------- ports/samd/main.c | 4 +++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index 01dcc328f176a..a9561237933fb 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -49,14 +49,16 @@ typedef struct _dac_obj_t { int8_t dma_channel; int8_t tc_index; uint32_t count; + mp_obj_t callback; } dac_obj_t; Dac *const dac_bases[] = DAC_INSTS; -static void dac_init(dac_obj_t *self, Dac *dac); +STATIC void dac_init(dac_obj_t *self, Dac *dac); +STATIC mp_obj_t dac_deinit(mp_obj_t self_in); #if defined(MCU_SAMD21) -static dac_obj_t dac_obj[] = { - {{&machine_dac_type}, 0, PIN_PA02}, +STATIC dac_obj_t dac_obj[] = { + {{&machine_dac_type}, 0, 0, PIN_PA02}, }; #define MAX_DAC_VALUE (1023) @@ -65,9 +67,9 @@ static dac_obj_t dac_obj[] = { #elif defined(MCU_SAMD51) -static dac_obj_t dac_obj[] = { - {{&machine_dac_type}, 0, PIN_PA02}, - {{&machine_dac_type}, 1, PIN_PA05}, +STATIC dac_obj_t dac_obj[] = { + {{&machine_dac_type}, 0, 0, PIN_PA02, 2, -1, -1, 1, NULL}, + {{&machine_dac_type}, 1, 0, PIN_PA05, 2, -1, -1, 1, NULL}, }; // 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 @@ -82,7 +84,6 @@ static uint8_t dac_vref_table[] = { #endif // defined SAMD21 or SAMD51 - void dac_irq_handler(int dma_channel) { dac_obj_t *self; @@ -94,6 +95,10 @@ void dac_irq_handler(int dma_channel) { self->count -= 1; dma_desc[self->dma_channel].BTCTRL.reg |= DMAC_BTCTRL_VALID; DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; + } else { + if (self->callback != MP_OBJ_NULL) { + mp_sched_schedule(self->callback, self); + } } #elif defined(MCU_SAMD51) @@ -107,6 +112,10 @@ void dac_irq_handler(int dma_channel) { self->count -= 1; dma_desc[self->dma_channel].BTCTRL.reg |= DMAC_BTCTRL_VALID; DMAC->Channel[self->dma_channel].CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; + } else { + if (self->callback != MP_OBJ_NULL) { + mp_sched_schedule(self->callback, self); + } } #endif } @@ -114,10 +123,11 @@ void dac_irq_handler(int dma_channel) { 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} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; // Parse the arguments. @@ -137,6 +147,15 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ self->vref = vref; } + 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->initialized = false; + Dac *dac = dac_bases[0]; // Just one DAC dac_init(self, dac); // Set the port as given in self->gpio_id as DAC @@ -151,9 +170,9 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { #if defined(MCU_SAMD21) // Configuration SAMD21 - // Enable APBC clocks and PCHCTRL clocks; GCLK3 at 1 MHz + // 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_GCLK3 | GCLK_CLKCTRL_ID_DAC; + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_DAC; while (GCLK->STATUS.bit.SYNCBUSY) { } // Reset DAC registers @@ -169,9 +188,9 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { #elif defined(MCU_SAMD51) // Configuration SAMD51 - // Enable APBD clocks and PCHCTRL clocks; GCLK3 at 8 MHz + // 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_GCLK3 | GCLK_PCHCTRL_CHEN; + GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN; // Reset DAC registers dac->CTRLA.bit.SWRST = 1; @@ -190,7 +209,7 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { self->initialized = true; } -static void dac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +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); } @@ -215,7 +234,7 @@ 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); -static mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { +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; @@ -301,9 +320,9 @@ static mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); -static mp_obj_t dac_deinit(mp_obj_t self_in) { +STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { dac_obj_t *self = self_in; self->initialized = false; // Reset the DAC to lower the current consumption as SAMD21 @@ -317,11 +336,20 @@ static mp_obj_t dac_deinit(mp_obj_t self_in) { free_tc_instance(self->tc_index); self->tc_index = -1; } + self->callback = MP_OBJ_NULL; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(dac_deinit_obj, dac_deinit); -static const mp_rom_map_elem_t dac_locals_dict_table[] = { +// Clear the DMA channel entry in the DAC object. +void dac_deinit_channel(void) { + dac_obj[0].dma_channel = -1; + #if defined(MCU_SAMD51) + dac_obj[1].dma_channel = -1; + #endif +} + +STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&dac_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&dac_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) }, diff --git a/ports/samd/main.c b/ports/samd/main.c index df06f0b295946..43c465a8f009a 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -41,6 +41,7 @@ extern uint8_t _sstack, _estack, _sheap, _eheap; extern void adc_deinit_all(void); +extern void dac_deinit_channel(void); extern void pin_irq_deinit_all(void); extern void pwm_deinit_all(void); extern void sercom_deinit_all(void); @@ -96,6 +97,9 @@ void samd_main(void) { #if MICROPY_PY_MACHINE_ADC adc_deinit_all(); #endif + #if MICROPY_PY_MACHINE_DAC + dac_deinit_channel(); + #endif pin_irq_deinit_all(); #if MICROPY_PY_MACHINE_PWM pwm_deinit_all(); From 3fdd764a00f2d49dbbf30eaaf0ee41d577143e82 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 13 Sep 2022 21:26:54 +0200 Subject: [PATCH 06/14] samd/ADC: Add a callback keyword option to machine.ADC(). Enabling a callback that will be called when a adc.read_timed_into() run is finished. That's especially useful with slow sampling rates and/or many samples, avoiding to guess the sampling time. Raise an error is adc.read_u16() is called while a read_timed_into() is active. Other ADC changes: - SAMD51: use ADC1 if both ADC1 and ADC0 are available at a Pin. Signed-off-by: robert-hh --- ports/samd/machine_adc.c | 118 ++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 19 deletions(-) diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index afd9c5d29a5c5..b64d53a43ff3c 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -28,6 +28,13 @@ // This file is never compiled standalone, it's included directly from // extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. +#if MICROPY_PY_MACHINE_ADC + +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mperrno.h" + #include "py/mphal.h" #include "sam.h" #include "pin_af.h" @@ -51,6 +58,7 @@ typedef struct _machine_adc_obj_t { #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 @@ -60,9 +68,19 @@ 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; + bool busy; + mp_obj_t callback; + mp_obj_t self; +} device_mgmt_t; + +device_mgmt_t device_mgmt[ADC_INST_NUM]; + #elif defined(MCU_SAMD51) static uint8_t adc_vref_table[] = { @@ -75,6 +93,20 @@ 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] = { + { 0, 0, -1, MP_OBJ_NULL, MP_OBJ_NULL}, + { 0, 0, -1, MP_OBJ_NULL, MP_OBJ_NULL} +}; #endif // defined(MCU_SAMD21) @@ -82,12 +114,12 @@ 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); @@ -97,11 +129,27 @@ void adc_irq_handler(int dma_channel) { #if defined(MCU_SAMD21) DMAC->CHID.reg = dma_channel; - DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL; + 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->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 } @@ -114,13 +162,16 @@ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_p self->adc_config.channel, self->bits, 1 << self->avg, self->vref); } -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 }; +static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *all_args) { + + enum { ARG_id, ARG_bits, ARG_average, 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} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; // Parse the arguments. @@ -129,7 +180,7 @@ 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); @@ -145,13 +196,20 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args 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; } + 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; + } // 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)); + self->dma_channel = -1; + self->tc_index = -1; adc_init(self); @@ -163,6 +221,10 @@ 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 (device_mgmt[self->adc_config.device].busy != 0) { + mp_raise_OSError(MP_EBUSY); + } + // 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; @@ -194,6 +256,7 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f if (self->dma_channel == -1) { self->dma_channel = allocate_dma_channel(); dma_init(); + dma_register_irq(self->dma_channel, adc_irq_handler); } if (self->tc_index == -1) { self->tc_index = allocate_tc_instance(); @@ -206,8 +269,6 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f // Configure DMA for halfword output to the DAC #if defined(MCU_SAMD21) - // dma irq just for SAMD21 to stop the timer based acquisition - dma_register_irq(self->dma_channel, adc_irq_handler); configure_tc(self->tc_index, freq, TC_EVCTRL_OVFEO); // Enable APBC clock PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; @@ -241,9 +302,11 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f 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; @@ -265,8 +328,16 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f 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 } @@ -277,6 +348,11 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f static void mp_machine_adc_deinit(machine_adc_obj_t *self) { busy_flags &= ~((1 << (self->adc_config.device * 16 + self->adc_config.channel))); 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; + } + #endif dac_stop_dma(self->dma_channel, true); free_dma_channel(self->dma_channel); self->dma_channel = -1; @@ -288,17 +364,21 @@ static void mp_machine_adc_deinit(machine_adc_obj_t *self) { } void adc_deinit_all(void) { - busy_flags = 0; - init_flags[0] = 0; - init_flags[1] = 0; + 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 } 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 @@ -320,7 +400,7 @@ static void adc_init(machine_adc_obj_t *self) { // 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]; + adc->REFCTRL.reg = self->vref; // Average: Accumulate samples and scale them down accordingly adc->AVGCTRL.reg = self->avg | ADC_AVGCTRL_ADJRES(self->avg); // Enable ADC and wait to be ready From 27349a53a58d2a44ed8f76ea94e9c1a63953e1f1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 14 Oct 2022 12:18:28 +0200 Subject: [PATCH 07/14] samd/ADC_DAC: Add adc.busy() and dac.busy() methods. These return True, while a timed action is ongoing. Side change: Reorder some code in machine_dac.c and do not reset DAC twice. Signed-off-by: robert-hh --- ports/samd/machine_adc.c | 7 +++++ ports/samd/machine_dac.c | 58 ++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index b64d53a43ff3c..854260927b80c 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -351,6 +351,7 @@ static void mp_machine_adc_deinit(machine_adc_obj_t *self) { #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); @@ -363,6 +364,12 @@ static void mp_machine_adc_deinit(machine_adc_obj_t *self) { } } +// busy() : Report, if the ADC device is busy +static mp_int_t machine_adc_busy(mp_obj_t self_in) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + return device_mgmt[self->adc_config.device].busy ? true : false; +} + void adc_deinit_all(void) { ch_busy_flags = 0; device_mgmt[0].init = 0; diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index a9561237933fb..d53752b3c0278 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -31,6 +31,8 @@ #include #include "py/obj.h" +#include "py/runtime.h" +#include "py/mperrno.h" #include "py/mphal.h" #include "sam.h" @@ -48,6 +50,7 @@ typedef struct _dac_obj_t { uint8_t vref; int8_t dma_channel; int8_t tc_index; + bool busy; uint32_t count; mp_obj_t callback; } dac_obj_t; @@ -57,19 +60,23 @@ STATIC mp_obj_t dac_deinit(mp_obj_t self_in); #if defined(MCU_SAMD21) -STATIC dac_obj_t dac_obj[] = { - {{&machine_dac_type}, 0, 0, PIN_PA02}, -}; - #define MAX_DAC_VALUE (1023) #define DEFAULT_DAC_VREF (1) #define MAX_DAC_VREF (2) +STATIC dac_obj_t dac_obj[] = { + {{&machine_dac_type}, 0, 0, PIN_PA02, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, +}; + #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, PIN_PA02, 2, -1, -1, 1, NULL}, - {{&machine_dac_type}, 1, 0, PIN_PA05, 2, -1, -1, 1, NULL}, + {{&machine_dac_type}, 0, 0, PIN_PA02, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, + {{&machine_dac_type}, 1, 0, PIN_PA05, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, }; // 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 @@ -78,9 +85,6 @@ 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) #endif // defined SAMD21 or SAMD51 @@ -96,6 +100,7 @@ void dac_irq_handler(int dma_channel) { 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); } @@ -113,6 +118,7 @@ void dac_irq_handler(int dma_channel) { 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); } @@ -136,10 +142,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; @@ -192,9 +198,18 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { 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) { + // 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 { + // 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; @@ -217,6 +232,10 @@ 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->busy != false) { + mp_raise_OSError(MP_EBUSY); + } + int value = mp_obj_get_int(value_in); if (value < 0 || value > MAX_DAC_VALUE) { @@ -259,6 +278,8 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } // 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) @@ -337,10 +358,18 @@ STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { self->tc_index = -1; } self->callback = MP_OBJ_NULL; + self->busy = false; 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); + // Clear the DMA channel entry in the DAC object. void dac_deinit_channel(void) { dac_obj[0].dma_channel = -1; @@ -350,6 +379,7 @@ void dac_deinit_channel(void) { } STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { + { 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), MP_ROM_PTR(&dac_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) }, From 972c732bca2ba1a7e149383959ecd4563200297d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 26 Oct 2022 20:39:44 +0200 Subject: [PATCH 08/14] samd/docs: Document the extensions to DAC and ADC. Signed-off-by: robert-hh --- docs/samd/quickref.rst | 154 +++++++++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 30 deletions(-) diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 781686d2f60e6..cf1ff05a374c3 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -261,30 +261,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: - - - *dest* is the Pin object on which the ADC is output. - -Keyword arguments: +On the SAMD21/SAMD51 ADC functionality is available on Pins labelled 'Ann'. - - *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. +Use the :ref:`machine.ADC ` class:: - The default setting is for 3.3V. Other values are: + from machine import ADC - ==== ============================== =============================== - 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 - ==== ============================== =============================== + 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 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 +==== ============================== =============================== + +The callback keyword option is used for timed ADC sampling. The callback is executed +when all data has been sampled. ADC Methods ``````````` @@ -294,27 +303,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: @@ -327,6 +375,7 @@ vref SAMD21 SAMD51 3 - Buffered external reference ==== ============================ ================================ + DAC Methods ``````````` @@ -335,6 +384,51 @@ 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 the resources used by it, especially the DMA channel +and the Timer. 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. + + Software SPI bus ---------------- From 54d2e5977aeb06bef2b4615d413ac78830fecbb4 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 3 Dec 2022 17:51:34 +0100 Subject: [PATCH 09/14] samd/ADC: Apply the channel's VRef setting at each read. Signed-off-by: robert-hh --- ports/samd/machine_adc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index 854260927b80c..4b1c38797b0eb 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -208,6 +208,7 @@ static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ // flag the device/channel as being in use. ch_busy_flags |= (1 << (self->adc_config.device * 16 + self->adc_config.channel)); + device_mgmt[self->adc_config.device].init = false; self->dma_channel = -1; self->tc_index = -1; @@ -225,6 +226,8 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { mp_raise_OSError(MP_EBUSY); } + // 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; @@ -261,6 +264,8 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f if (self->tc_index == -1) { self->tc_index = allocate_tc_instance(); } + // 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; @@ -407,7 +412,7 @@ static void adc_init(machine_adc_obj_t *self) { // 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 = self->vref; + adc->REFCTRL.reg = adc_vref_table[self->vref]; // Average: Accumulate samples and scale them down accordingly adc->AVGCTRL.reg = self->avg | ADC_AVGCTRL_ADJRES(self->avg); // Enable ADC and wait to be ready From 3d18a0781f0403133d793282ef04ff43329f1d24 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Mar 2023 11:52:11 +0100 Subject: [PATCH 10/14] samd/DAC: Rework the DAC deinit() semantics. Since the two channels of a SAMD51 are not completely independent, dac.deinit() now clears both channels, and both channels have to be re-instantiated after a deinit(). Side change: - rearrange some code lines. Signed-off-by: robert-hh --- docs/samd/quickref.rst | 7 +++-- ports/samd/machine_dac.c | 65 +++++++++++++++++++++++----------------- ports/samd/main.c | 4 +-- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index cf1ff05a374c3..fcba3f30e9810 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -422,11 +422,12 @@ otherwise. .. method:: deinit() -Deinitialize the DAC and release the resources used by it, especially the DMA channel -and the Timer. On most SAMD21 boards, there is just one timer available for +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. +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 diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index d53752b3c0278..55b1dac0b01b7 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -46,8 +46,8 @@ typedef struct _dac_obj_t { mp_obj_base_t base; uint8_t id; bool initialized; - mp_hal_pin_obj_t gpio_id; uint8_t vref; + mp_hal_pin_obj_t gpio_id; int8_t dma_channel; int8_t tc_index; bool busy; @@ -55,7 +55,7 @@ typedef struct _dac_obj_t { mp_obj_t callback; } dac_obj_t; Dac *const dac_bases[] = DAC_INSTS; -STATIC void dac_init(dac_obj_t *self, Dac *dac); +STATIC void dac_init(dac_obj_t *self); STATIC mp_obj_t dac_deinit(mp_obj_t self_in); #if defined(MCU_SAMD21) @@ -65,7 +65,7 @@ STATIC mp_obj_t dac_deinit(mp_obj_t self_in); #define MAX_DAC_VREF (2) STATIC dac_obj_t dac_obj[] = { - {{&machine_dac_type}, 0, 0, PIN_PA02, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, + {{&machine_dac_type}, 0, 0, DEFAULT_DAC_VREF, PIN_PA02}, }; #elif defined(MCU_SAMD51) @@ -75,8 +75,8 @@ STATIC dac_obj_t dac_obj[] = { #define MAX_DAC_VREF (3) STATIC dac_obj_t dac_obj[] = { - {{&machine_dac_type}, 0, 0, PIN_PA02, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, - {{&machine_dac_type}, 1, 0, PIN_PA05, DEFAULT_DAC_VREF, -1, -1, false, 1, NULL}, + {{&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 @@ -161,18 +161,20 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ self->dma_channel = -1; self->tc_index = -1; self->initialized = false; + self->busy = false; - Dac *dac = dac_bases[0]; // Just one DAC - dac_init(self, dac); + 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, Dac *dac) { +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 @@ -194,9 +196,6 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { #elif defined(MCU_SAMD51) // Configuration SAMD51 - // 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; // If the DAC is enabled it was already reset // In that case just disable it. @@ -206,6 +205,9 @@ STATIC void dac_init(dac_obj_t *self, Dac *dac) { 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) { @@ -232,6 +234,10 @@ 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 (self->busy != false) { mp_raise_OSError(MP_EBUSY); } @@ -241,8 +247,6 @@ STATIC mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) { if (value < 0 || value > MAX_DAC_VALUE) { mp_raise_ValueError(MP_ERROR_TEXT("value out of range")); } - // Re-init, if required - dac_init(self, dac); #if defined(MCU_SAMD21) dac->DATA.reg = value; #elif defined(MCU_SAMD51) @@ -257,8 +261,10 @@ 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; - // Re-init, if required - dac_init(self, dac); + + if (self->initialized == false) { + mp_raise_OSError(MP_ENODEV); + } mp_get_buffer_raise(args[1], &src, MP_BUFFER_READ); if (n_args > 3) { @@ -343,11 +349,8 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); -STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { - dac_obj_t *self = self_in; +STATIC void dac_deinit_channel(dac_obj_t *self) { self->initialized = false; - // Reset the DAC to lower the current consumption as SAMD21 - dac_bases[0]->CTRLA.bit.SWRST = 1; if (self->dma_channel >= 0) { dac_stop_dma(self->dma_channel, true); free_dma_channel(self->dma_channel); @@ -359,6 +362,20 @@ STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { } 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); @@ -370,18 +387,10 @@ STATIC mp_obj_t machine_dac_busy(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_dac_busy_obj, machine_dac_busy); -// Clear the DMA channel entry in the DAC object. -void dac_deinit_channel(void) { - dac_obj[0].dma_channel = -1; - #if defined(MCU_SAMD51) - dac_obj[1].dma_channel = -1; - #endif -} - STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&dac_write_obj) }, { 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), MP_ROM_PTR(&dac_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&dac_write_timed_obj) }, }; diff --git a/ports/samd/main.c b/ports/samd/main.c index 43c465a8f009a..7ea2320fdf8c5 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -41,7 +41,7 @@ extern uint8_t _sstack, _estack, _sheap, _eheap; extern void adc_deinit_all(void); -extern void dac_deinit_channel(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); @@ -98,7 +98,7 @@ void samd_main(void) { adc_deinit_all(); #endif #if MICROPY_PY_MACHINE_DAC - dac_deinit_channel(); + dac_deinit_all(); #endif pin_irq_deinit_all(); #if MICROPY_PY_MACHINE_PWM From 0dcc714115b777876a7ccfc419ca3c9aa888ddce Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Mar 2023 16:41:21 +0100 Subject: [PATCH 11/14] samd/ADC_DAC: Make adc.read_timed() and dac.write_timed() configurable. Both together require ~1.9k of flash space, including the DMA-manager and the TC-manager. adc.read_timed() uses ~700 bytes, dac.write_timed() ~600 bytes. Signed-off-by: robert-hh --- ports/samd/dma_manager.c | 4 ++++ ports/samd/machine_adc.c | 39 +++++++++++++++++++++++++++++++-------- ports/samd/machine_dac.c | 33 ++++++++++++++++++++++++++++++--- ports/samd/main.c | 4 ++++ ports/samd/mpconfigport.h | 11 +++++++++++ ports/samd/tc_manager.c | 4 ++++ 6 files changed, 84 insertions(+), 11 deletions(-) diff --git a/ports/samd/dma_manager.c b/ports/samd/dma_manager.c index 868606e66b7f1..2ca7f7ba106d3 100644 --- a/ports/samd/dma_manager.c +++ b/ports/samd/dma_manager.c @@ -30,6 +30,8 @@ #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. @@ -129,3 +131,5 @@ void dac_stop_dma(int dma_channel, bool wait) { } #endif } + +#endif diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index 4b1c38797b0eb..efaf2b708a59a 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -50,8 +50,10 @@ typedef struct _machine_adc_obj_t { uint8_t avg; uint8_t bits; uint8_t vref; + #if MICROPY_PY_MACHINE_ADC_TIMED int8_t dma_channel; int8_t tc_index; + #endif } machine_adc_obj_t; #define DEFAULT_ADC_BITS 12 @@ -74,9 +76,11 @@ static uint8_t adc_vref_table[] = { typedef struct _device_mgmt_t { bool init; + #if MICROPY_PY_MACHINE_ADC_TIMED bool busy; mp_obj_t callback; mp_obj_t self; + #endif } device_mgmt_t; device_mgmt_t device_mgmt[ADC_INST_NUM]; @@ -103,10 +107,7 @@ typedef struct _device_mgmt_t { mp_obj_t self; } device_mgmt_t; -device_mgmt_t device_mgmt[ADC_INST_NUM] = { - { 0, 0, -1, MP_OBJ_NULL, MP_OBJ_NULL}, - { 0, 0, -1, MP_OBJ_NULL, MP_OBJ_NULL} -}; +device_mgmt_t device_mgmt[ADC_INST_NUM]; #endif // defined(MCU_SAMD21) @@ -123,6 +124,8 @@ static void adc_init(machine_adc_obj_t *self); extern mp_int_t log2i(mp_int_t num); +#if MICROPY_PY_MACHINE_ADC_TIMED + // Active just for SAMD21, stops the freerun mode // For SAMD51, just the INT flag is reset. void adc_irq_handler(int dma_channel) { @@ -152,6 +155,7 @@ void adc_irq_handler(int dma_channel) { } #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; @@ -171,7 +175,9 @@ static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ { 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_TIMED { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + #endif }; // Parse the arguments. @@ -199,18 +205,20 @@ static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ if (0 <= vref && vref <= MAX_ADC_VREF) { self->vref = vref; } + // flag the device/channel as being in use. + 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_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; } - - // flag the device/channel as being in use. - ch_busy_flags |= (1 << (self->adc_config.device * 16 + self->adc_config.channel)); - device_mgmt[self->adc_config.device].init = false; self->dma_channel = -1; self->tc_index = -1; + #endif adc_init(self); @@ -222,9 +230,12 @@ 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_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]; @@ -375,6 +386,9 @@ static mp_int_t machine_adc_busy(mp_obj_t self_in) { return device_mgmt[self->adc_config.device].busy ? true : false; } +#endif + +#if MICROPY_PY_MACHINE_ADC_TIMED void adc_deinit_all(void) { ch_busy_flags = 0; device_mgmt[0].init = 0; @@ -384,6 +398,15 @@ void adc_deinit_all(void) { 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 diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index 55b1dac0b01b7..c311faa29379d 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -48,15 +48,16 @@ typedef struct _dac_obj_t { bool initialized; uint8_t vref; 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); -STATIC mp_obj_t dac_deinit(mp_obj_t self_in); #if defined(MCU_SAMD21) @@ -88,6 +89,8 @@ static uint8_t dac_vref_table[] = { #endif // defined SAMD21 or SAMD51 +#if MICROPY_PY_MACHINE_DAC_TIMED + void dac_irq_handler(int dma_channel) { dac_obj_t *self; @@ -126,6 +129,8 @@ void dac_irq_handler(int dma_channel) { #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) { @@ -133,7 +138,9 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ 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. @@ -153,15 +160,17 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ self->vref = vref; } + #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->initialized = false; self->busy = false; + #endif + + self->initialized = false; dac_init(self); // Set the port as given in self->gpio_id as DAC @@ -238,9 +247,11 @@ STATIC mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_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); @@ -257,6 +268,8 @@ 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]; @@ -386,12 +399,26 @@ STATIC mp_obj_t machine_dac_busy(mp_obj_t 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); diff --git a/ports/samd/main.c b/ports/samd/main.c index 7ea2320fdf8c5..4b9f10c2f017b 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -92,8 +92,12 @@ 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 diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 514f383948838..55cc2810eb8c5 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -128,6 +128,17 @@ #define MICROPY_PY_MACHINE_WDT_TIMEOUT_MS (1) #define MICROPY_PLATFORM_VERSION "ASF4" +#ifndef MICROPY_PY_MACHINE_DAC_TIMED +#define MICROPY_PY_MACHINE_DAC_TIMED (1) +#endif +#ifndef MICROPY_PY_MACHINE_ADC_TIMED +#define MICROPY_PY_MACHINE_ADC_TIMED (1) +#endif +#if MICROPY_PY_MACHINE_DAC_TIMED || MICROPY_PY_MACHINE_ADC_TIMED +#define MICROPY_HW_DMA_MANAGER (1) +#define MICROPY_HW_TC_MANAGER (1) +#endif + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/samd/tc_manager.c b/ports/samd/tc_manager.c index fb0c3b77ff48d..b6b981087c431 100644 --- a/ports/samd/tc_manager.c +++ b/ports/samd/tc_manager.c @@ -29,6 +29,8 @@ #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) @@ -179,3 +181,5 @@ void tc_deinit(void) { instance_flag[0] = instance_flag[1] = true; #endif } + +#endif From e6ddbbadc599a0dedaddca1b810070a37c9060d1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 26 Aug 2023 10:43:09 +0200 Subject: [PATCH 12/14] samd: Change the init sequence for adc_timed() and dac_timed(). To leave no half-initialized device if init fails. Signed-off-by: robert-hh --- ports/samd/machine_adc.c | 8 +++++--- ports/samd/machine_dac.c | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index efaf2b708a59a..8eb3fcc72a08e 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -267,14 +267,14 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f mp_get_buffer_raise(values, &src, MP_BUFFER_READ); if (src.len >= 2) { int freq = mp_obj_get_int(freq_in); + 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); } - if (self->tc_index == -1) { - self->tc_index = allocate_tc_instance(); - } // Set the reference voltage. Default: external AREFA. adc->REFCTRL.reg = adc_vref_table[self->vref]; // Set Input channel and resolution @@ -330,6 +330,8 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f 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 | diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index c311faa29379d..28c417d4b4724 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -287,14 +287,14 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } 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); } - if (self->tc_index == -1) { - self->tc_index = allocate_tc_instance(); - } // Configure TC; no need to check the return value configure_tc(self->tc_index, freq, 0); self->busy = true; From d1050dbc4b218f5edcac5153e2b097d7db5a5b0e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 24 Oct 2023 10:01:25 +0200 Subject: [PATCH 13/14] samd/machine_adc.c: Factor out machine.adc_timed(). After machine.ADC has been moved to extmod/machine_adc.c. Adding adc.read_timed() and adc.busy() to extmod/machine_adc.c with a corresponding flag to enable them. ADC/DAC timed are by default enabled only at all SAMD51 devices and at SAMD21 devices with an external flash for the file system. Side changes: - Fix a type in pin_af.c, preventing to use a second ADC channel. - Remove a duplicate definition in mcu/samd21/mpconfigmcu.h. Signed-off-by: robert-hh --- extmod/machine_adc.c | 22 +++++++++++++ ports/samd/machine_adc.c | 49 +++++++++++++---------------- ports/samd/mcu/samd21/mpconfigmcu.h | 7 +++++ ports/samd/mcu/samd51/mpconfigmcu.h | 14 ++++++--- ports/samd/mpconfigport.h | 8 +---- 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/extmod/machine_adc.c b/extmod/machine_adc.c index 11f1123dc794f..bb7d2461f244c 100644 --- a/extmod/machine_adc.c +++ b/extmod/machine_adc.c @@ -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. diff --git a/ports/samd/machine_adc.c b/ports/samd/machine_adc.c index 8eb3fcc72a08e..e5028f293541b 100644 --- a/ports/samd/machine_adc.c +++ b/ports/samd/machine_adc.c @@ -28,17 +28,13 @@ // This file is never compiled standalone, it's included directly from // extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. -#if MICROPY_PY_MACHINE_ADC - #include #include "py/obj.h" -#include "py/runtime.h" #include "py/mperrno.h" -#include "py/mphal.h" +#include "mphalport.h" #include "sam.h" #include "pin_af.h" -#include "modmachine.h" #include "samd_soc.h" #include "dma_manager.h" #include "tc_manager.h" @@ -50,7 +46,7 @@ typedef struct _machine_adc_obj_t { uint8_t avg; uint8_t bits; uint8_t vref; - #if MICROPY_PY_MACHINE_ADC_TIMED + #if MICROPY_PY_MACHINE_ADC_READ_TIMED int8_t dma_channel; int8_t tc_index; #endif @@ -76,7 +72,7 @@ static uint8_t adc_vref_table[] = { typedef struct _device_mgmt_t { bool init; - #if MICROPY_PY_MACHINE_ADC_TIMED + #if MICROPY_PY_MACHINE_ADC_READ_TIMED bool busy; mp_obj_t callback; mp_obj_t self; @@ -124,7 +120,7 @@ static void adc_init(machine_adc_obj_t *self); extern mp_int_t log2i(mp_int_t num); -#if MICROPY_PY_MACHINE_ADC_TIMED +#if MICROPY_PY_MACHINE_ADC_READ_TIMED // Active just for SAMD21, stops the freerun mode // For SAMD51, just the INT flag is reset. @@ -166,8 +162,7 @@ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_p self->adc_config.channel, self->bits, 1 << self->avg, self->vref); } -static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, - const mp_obj_t *all_args) { +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, ARG_callback }; static const mp_arg_t allowed_args[] = { @@ -175,7 +170,7 @@ static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ { 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_TIMED + #if MICROPY_PY_MACHINE_ADC_READ_TIMED { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, #endif }; @@ -209,7 +204,7 @@ static mp_obj_t adc_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ 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_TIMED + #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; @@ -231,7 +226,7 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { // Set the reference voltage. Default: external AREFA. adc->REFCTRL.reg = adc_vref_table[self->vref]; - #if MICROPY_PY_MACHINE_ADC_TIMED + #if MICROPY_PY_MACHINE_ADC_READ_TIMED if (device_mgmt[self->adc_config.device].busy != 0) { mp_raise_OSError(MP_EBUSY); } @@ -260,13 +255,13 @@ static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { return adc->RESULT.reg * (65536 / (1 << self->bits)); } -static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t freq_in) { - machine_adc_obj_t *self = self_in; +#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) { - int freq = mp_obj_get_int(freq_in); if (self->tc_index == -1) { self->tc_index = allocate_tc_instance(); } @@ -359,12 +354,19 @@ static void machine_adc_read_timed(mp_obj_t self_in, mp_obj_t values, mp_obj_t f #endif // defined SAMD21 or SAMD51 } - return mp_const_none; } +// 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) { - busy_flags &= ~((1 << (self->adc_config.device * 16 + self->adc_config.channel))); + 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) { @@ -380,17 +382,10 @@ static void mp_machine_adc_deinit(machine_adc_obj_t *self) { free_tc_instance(self->tc_index); self->tc_index = -1; } + #endif } -// busy() : Report, if the ADC device is busy -static mp_int_t machine_adc_busy(mp_obj_t self_in) { - machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); - return device_mgmt[self->adc_config.device].busy ? true : false; -} - -#endif - -#if MICROPY_PY_MACHINE_ADC_TIMED +#if MICROPY_PY_MACHINE_ADC_READ_TIMED void adc_deinit_all(void) { ch_busy_flags = 0; device_mgmt[0].init = 0; diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index f0a7a73e0c027..a837afd2d8df0 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -79,6 +79,13 @@ unsigned long trng_random_u32(int delay); #define MICROPY_PY_ONEWIRE (SAMD21_EXTRA_FEATURES) #endif +#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 diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 8cce90b886c07..e5b0f75ba4e02 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -18,10 +18,10 @@ unsigned long trng_random_u32(void); #define MICROPY_PY_MACHINE_UART_IRQ (1) // 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 (2048) // @@ -31,6 +31,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) diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 55cc2810eb8c5..f6ee870d78af1 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -128,13 +128,7 @@ #define MICROPY_PY_MACHINE_WDT_TIMEOUT_MS (1) #define MICROPY_PLATFORM_VERSION "ASF4" -#ifndef MICROPY_PY_MACHINE_DAC_TIMED -#define MICROPY_PY_MACHINE_DAC_TIMED (1) -#endif -#ifndef MICROPY_PY_MACHINE_ADC_TIMED -#define MICROPY_PY_MACHINE_ADC_TIMED (1) -#endif -#if MICROPY_PY_MACHINE_DAC_TIMED || MICROPY_PY_MACHINE_ADC_TIMED +#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 From 89f4e2f9a098354e19a198ae4ad9686d8aea96d7 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 29 Jan 2025 16:27:38 +0100 Subject: [PATCH 14/14] samd/machine_dac.c: Replace STATIC with static. That had not been changed. Signed-off-by: robert-hh --- ports/samd/machine_dac.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ports/samd/machine_dac.c b/ports/samd/machine_dac.c index 28c417d4b4724..425c01f4a8094 100644 --- a/ports/samd/machine_dac.c +++ b/ports/samd/machine_dac.c @@ -57,7 +57,7 @@ typedef struct _dac_obj_t { #endif } dac_obj_t; Dac *const dac_bases[] = DAC_INSTS; -STATIC void dac_init(dac_obj_t *self); +static void dac_init(dac_obj_t *self); #if defined(MCU_SAMD21) @@ -65,7 +65,7 @@ STATIC void dac_init(dac_obj_t *self); #define DEFAULT_DAC_VREF (1) #define MAX_DAC_VREF (2) -STATIC dac_obj_t dac_obj[] = { +static dac_obj_t dac_obj[] = { {{&machine_dac_type}, 0, 0, DEFAULT_DAC_VREF, PIN_PA02}, }; @@ -75,7 +75,7 @@ STATIC dac_obj_t dac_obj[] = { #define DEFAULT_DAC_VREF (2) #define MAX_DAC_VREF (3) -STATIC dac_obj_t dac_obj[] = { +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}, }; @@ -131,7 +131,7 @@ void dac_irq_handler(int dma_channel) { #endif -STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, +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, ARG_callback }; @@ -179,7 +179,7 @@ STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ return MP_OBJ_FROM_PTR(self); } -STATIC void dac_init(dac_obj_t *self) { +static void dac_init(dac_obj_t *self) { // Init DAC if (self->initialized == false) { Dac *dac = dac_bases[0]; // Just one DAC @@ -235,12 +235,12 @@ STATIC void dac_init(dac_obj_t *self) { self->initialized = true; } -STATIC void dac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +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); } -STATIC mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t value_in) { +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; @@ -270,7 +270,7 @@ 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) { +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; @@ -360,9 +360,9 @@ STATIC mp_obj_t dac_write_timed(size_t n_args, const mp_obj_t *args) { } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dac_write_timed_obj, 3, 4, dac_write_timed); +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) { +static void dac_deinit_channel(dac_obj_t *self) { self->initialized = false; if (self->dma_channel >= 0) { dac_stop_dma(self->dma_channel, true); @@ -387,18 +387,18 @@ void dac_deinit_all(void) { #endif } -STATIC mp_obj_t dac_deinit(mp_obj_t self_in) { +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) { +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); +static MP_DEFINE_CONST_FUN_OBJ_1(machine_dac_busy_obj, machine_dac_busy); #else void dac_deinit_all(void) { @@ -412,7 +412,7 @@ void dac_deinit_all(void) { #endif -STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { +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) }, @@ -421,7 +421,7 @@ STATIC const mp_rom_map_elem_t dac_locals_dict_table[] = { #endif }; -STATIC MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table); +static MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( machine_dac_type, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy