From 58b9789d0c824c665db744ef3653d6304e908bc0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 21 Apr 2017 14:43:03 -0700 Subject: atmel-samd: Introduce audio sample playback via audioio.AudioOut. --- atmel-samd/Makefile | 12 +- atmel-samd/asf/sam0/drivers/dma/dma.c | 654 +++++++++++++++ atmel-samd/asf/sam0/drivers/dma/dma.h | 882 +++++++++++++++++++++ atmel-samd/asf/sam0/drivers/dma/dma_crc.h | 230 ++++++ atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c | 557 +++++++++++++ atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h | 650 +++++++++++++++ .../asf/sam0/drivers/dma/module_config/conf_dma.h | 51 ++ atmel-samd/asf/sam0/drivers/events/events.h | 729 +++++++++++++++++ atmel-samd/asf/sam0/drivers/events/events_common.h | 71 ++ atmel-samd/asf/sam0/drivers/events/events_hooks.c | 235 ++++++ atmel-samd/asf/sam0/drivers/events/events_hooks.h | 182 +++++ .../sam0/drivers/events/events_sam_d_r/events.c | 328 ++++++++ atmel-samd/asf_conf/conf_dma.h | 51 ++ .../boards/circuitplayground_express/conf_dma.h | 51 ++ atmel-samd/common-hal/audioio/AudioOut.c | 350 ++++++++ atmel-samd/common-hal/audioio/AudioOut.h | 47 ++ atmel-samd/common-hal/audioio/__init__.c | 1 + atmel-samd/common-hal/microcontroller/Pin.c | 1 + atmel-samd/common-hal/pulseio/PulseOut.c | 32 +- atmel-samd/main.c | 5 + atmel-samd/mpconfigport.h | 2 + atmel-samd/shared_dma.c | 54 ++ atmel-samd/shared_dma.h | 37 + extmod/vfs_fat_file.c | 17 +- extmod/vfs_fat_file.h | 26 + shared-bindings/analogio/AnalogIn.h | 4 +- shared-bindings/audioio/AudioOut.c | 222 ++++++ shared-bindings/audioio/AudioOut.h | 46 ++ shared-bindings/audioio/__init__.c | 67 ++ shared-bindings/audioio/__init__.h | 34 + 30 files changed, 5596 insertions(+), 32 deletions(-) create mode 100644 atmel-samd/asf/sam0/drivers/dma/dma.c create mode 100644 atmel-samd/asf/sam0/drivers/dma/dma.h create mode 100644 atmel-samd/asf/sam0/drivers/dma/dma_crc.h create mode 100644 atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c create mode 100644 atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h create mode 100644 atmel-samd/asf/sam0/drivers/dma/module_config/conf_dma.h create mode 100644 atmel-samd/asf/sam0/drivers/events/events.h create mode 100644 atmel-samd/asf/sam0/drivers/events/events_common.h create mode 100644 atmel-samd/asf/sam0/drivers/events/events_hooks.c create mode 100644 atmel-samd/asf/sam0/drivers/events/events_hooks.h create mode 100644 atmel-samd/asf/sam0/drivers/events/events_sam_d_r/events.c create mode 100644 atmel-samd/asf_conf/conf_dma.h create mode 100644 atmel-samd/boards/circuitplayground_express/conf_dma.h create mode 100644 atmel-samd/common-hal/audioio/AudioOut.c create mode 100644 atmel-samd/common-hal/audioio/AudioOut.h create mode 100644 atmel-samd/common-hal/audioio/__init__.c create mode 100644 atmel-samd/shared_dma.c create mode 100644 atmel-samd/shared_dma.h create mode 100644 shared-bindings/audioio/AudioOut.c create mode 100644 shared-bindings/audioio/AudioOut.h create mode 100644 shared-bindings/audioio/__init__.c create mode 100644 shared-bindings/audioio/__init__.h diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 91d0a5be9..61f6ea887 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -48,7 +48,8 @@ INC += -Iasf/common/services/usb/udc/ INC += -Iasf/common/utils INC += -Iasf/common2/services/delay/ INC += $(addprefix -Iasf/sam0/,\ - drivers/extint/ \ + drivers/events \ + drivers/extint \ drivers/port \ drivers/system \ drivers/adc/adc_sam_d_r \ @@ -107,7 +108,6 @@ CFLAGS_CORTEX_M0 = \ -ffunction-sections \ -fdata-sections \ -fshort-enums \ - -flto \ -D ARM_MATH_CM0PLUS=true \ -DSYSTICK_MODE \ -DEXTINT_CALLBACK_MODE=true \ @@ -118,6 +118,7 @@ CFLAGS_CORTEX_M0 = \ -DDAC_CALLBACK_MODE=false \ -DTCC_ASYNC=false \ -DADC_CALLBACK_MODE=false \ + -DEVENTS_INTERRUPT_HOOKS_MODE=false \ -DTC_ASYNC=true \ -DUSB_DEVICE_LPM_SUPPORT \ --param max-inline-insns-single=500 @@ -129,7 +130,7 @@ CFLAGS = $(INC) -Wall -Werror -std=gnu11 -nostdlib $(CFLAGS_CORTEX_M0) $(COPT) ifeq ($(DEBUG), 1) CFLAGS += -Os -ggdb -DNDEBUG -DENABLE_MICRO_TRACE_BUFFER else -CFLAGS += -Os -DNDEBUG +CFLAGS += -Os -DNDEBUG -flto endif ifneq ($(FROZEN_DIR),) @@ -152,6 +153,8 @@ LIBS = -larm_cortexM0l_math -lm -lgcc -lc SRC_ASF = $(addprefix asf/sam0/,\ drivers/adc/adc_sam_d_r/adc.c \ drivers/dac/dac_sam_d_c/dac.c \ + drivers/dma/dma.c \ + drivers/events/events_sam_d_r/events.c \ drivers/extint/extint_callback.c \ drivers/extint/extint_sam_d_r/extint.c \ drivers/nvm/nvm.c \ @@ -183,6 +186,7 @@ SRC_C = \ moduos.c \ mphalport.c \ samd21_pins.c \ + shared_dma.c \ rgb_led_status.c \ tick.c \ $(FLASH_IMPL) \ @@ -228,6 +232,8 @@ SRC_BINDINGS = \ analogio/__init__.c \ analogio/AnalogIn.c \ analogio/AnalogOut.c \ + audioio/__init__.c \ + audioio/AudioOut.c \ digitalio/__init__.c \ digitalio/DigitalInOut.c \ pulseio/__init__.c \ diff --git a/atmel-samd/asf/sam0/drivers/dma/dma.c b/atmel-samd/asf/sam0/drivers/dma/dma.c new file mode 100644 index 000000000..23592cda3 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/dma.c @@ -0,0 +1,654 @@ +/* + * \file + * + * \brief SAM Direct Memory Access Controller Driver + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include +#include "dma.h" +#include "clock.h" +#include "system_interrupt.h" + +struct _dma_module { + volatile bool _dma_init; + volatile uint32_t allocated_channels; + uint8_t free_channels; +}; + +struct _dma_module _dma_inst = { + ._dma_init = false, + .allocated_channels = 0, + .free_channels = CONF_MAX_USED_CHANNEL_NUM, +}; + +/** Maximum retry counter for resuming a job transfer. */ +#define MAX_JOB_RESUME_COUNT 10000 + +/** DMA channel mask. */ +#define DMA_CHANNEL_MASK (0x1f) + +COMPILER_ALIGNED(16) +DmacDescriptor descriptor_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR; + +/** Initial write back memory section. */ +COMPILER_ALIGNED(16) +static DmacDescriptor _write_back_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR; + +/** Internal DMA resource pool. */ +static struct dma_resource* _dma_active_resource[CONF_MAX_USED_CHANNEL_NUM]; + +/* DMA channel interrup flag. */ +uint8_t g_chan_interrupt_flag[CONF_MAX_USED_CHANNEL_NUM]={0}; + +/** + * \brief Find a free channel for a DMA resource. + * + * Find a channel for the requested DMA resource. + * + * \return Status of channel allocation. + * \retval DMA_INVALID_CHANNEL No channel available + * \retval count Allocated channel for the DMA resource + */ +static uint8_t _dma_find_first_free_channel_and_allocate(void) +{ + uint8_t count; + uint32_t tmp; + bool allocated = false; + + system_interrupt_enter_critical_section(); + + tmp = _dma_inst.allocated_channels; + + for (count = 0; count < CONF_MAX_USED_CHANNEL_NUM; ++count) { + if (!(tmp & 0x00000001)) { + /* If free channel found, set as allocated and return + *number */ + + _dma_inst.allocated_channels |= 1 << count; + _dma_inst.free_channels--; + allocated = true; + + break; + } + + tmp = tmp >> 1; + } + + system_interrupt_leave_critical_section(); + + if (!allocated) { + return DMA_INVALID_CHANNEL; + } else { + return count; + } +} + +/** + * \brief Release an allocated DMA channel. + * + * \param[in] channel Channel id to be released + * + */ +static void _dma_release_channel(uint8_t channel) +{ + _dma_inst.allocated_channels &= ~(1 << channel); + _dma_inst.free_channels++; +} + +/** + * \brief Configure the DMA resource. + * + * \param[in] dma_resource Pointer to a DMA resource instance + * \param[out] resource_config Configurations of the DMA resource + * + */ +static void _dma_set_config(struct dma_resource *resource, + struct dma_resource_config *resource_config) +{ + Assert(resource); + Assert(resource_config); + uint32_t temp_CHCTRLB_reg; + system_interrupt_enter_critical_section(); + + /** Select the DMA channel and clear software trigger */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << resource->channel_id)); + + temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(resource_config->priority) | \ + DMAC_CHCTRLB_TRIGSRC(resource_config->peripheral_trigger) | \ + DMAC_CHCTRLB_TRIGACT(resource_config->trigger_action); + + + if(resource_config->event_config.input_action){ + temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVIE | DMAC_CHCTRLB_EVACT( + resource_config->event_config.input_action); + } + + /** Enable event output, the event output selection is configured in + * each transfer descriptor */ + if (resource_config->event_config.event_output_enable) { + temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVOE; + } + + /* Write config to CTRLB register */ + DMAC->CHCTRLB.reg = temp_CHCTRLB_reg; + + + + system_interrupt_leave_critical_section(); +} + +/** + * \brief DMA interrupt service routine. + * + */ +void DMAC_Handler( void ) +{ + uint8_t active_channel; + struct dma_resource *resource; + uint8_t isr; + uint32_t write_size; + uint32_t total_size; + + system_interrupt_enter_critical_section(); + + /* Get Pending channel */ + active_channel = DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk; + + Assert(_dma_active_resource[active_channel]); + + /* Get active DMA resource based on channel */ + resource = _dma_active_resource[active_channel]; + + /* Select the active channel */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + isr = DMAC->CHINTFLAG.reg; + + /* Calculate block transfer size of the DMA transfer */ + total_size = descriptor_section[resource->channel_id].BTCNT.reg; + write_size = _write_back_section[resource->channel_id].BTCNT.reg; + resource->transfered_size = total_size - write_size; + + /* DMA channel interrupt handler */ + if (isr & DMAC_CHINTENCLR_TERR) { + /* Clear transfer error flag */ + DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR; + + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + + /* Execute the callback function */ + if ((resource->callback_enable & (1<callback[DMA_CALLBACK_TRANSFER_ERROR])) { + resource->callback[DMA_CALLBACK_TRANSFER_ERROR](resource); + } + } else if (isr & DMAC_CHINTENCLR_TCMPL) { + /* Clear the transfer complete flag */ + DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; + + /* Set job status */ + resource->job_status = STATUS_OK; + + /* Execute the callback function */ + if ((resource->callback_enable & (1 << DMA_CALLBACK_TRANSFER_DONE)) && + (resource->callback[DMA_CALLBACK_TRANSFER_DONE])) { + resource->callback[DMA_CALLBACK_TRANSFER_DONE](resource); + } + } else if (isr & DMAC_CHINTENCLR_SUSP) { + /* Clear channel suspend flag */ + DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP; + + /* Set job status */ + resource->job_status = STATUS_SUSPEND; + + /* Execute the callback function */ + if ((resource->callback_enable & (1 << DMA_CALLBACK_CHANNEL_SUSPEND)) && + (resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND])){ + resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND](resource); + } + } + + system_interrupt_leave_critical_section(); +} + +/** + * \brief Initializes config with predefined default values. + * + * This function will initialize a given DMA configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structure before being + * modified by the user application. + * + * The default configuration is as follows: + * \li Software trigger is used as the transfer trigger + * \li Priority level 0 + * \li Only software/event trigger + * \li Requires a trigger for each transaction + * \li No event input /output + * \li DMA channel is disabled during sleep mode (if has the feature) + * \param[out] config Pointer to the configuration + * + */ +void dma_get_config_defaults(struct dma_resource_config *config) +{ + Assert(config); + /* Set as priority 0 */ + config->priority = DMA_PRIORITY_LEVEL_0; + /* Only software/event trigger */ + config->peripheral_trigger = 0; + /* Transaction trigger */ + config->trigger_action = DMA_TRIGGER_ACTION_TRANSACTION; + + /* Event configurations, no event input/output */ + config->event_config.input_action = DMA_EVENT_INPUT_NOACT; + config->event_config.event_output_enable = false; +#ifdef FEATURE_DMA_CHANNEL_STANDBY + config->run_in_standby = false; +#endif +} + +/** + * \brief Allocate a DMA with configurations. + * + * This function will allocate a proper channel for a DMA transfer request. + * + * \param[in,out] dma_resource Pointer to a DMA resource instance + * \param[in] transfer_config Configurations of the DMA transfer + * + * \return Status of the allocation procedure. + * + * \retval STATUS_OK The DMA resource was allocated successfully + * \retval STATUS_ERR_NOT_FOUND DMA resource allocation failed + */ +enum status_code dma_allocate(struct dma_resource *resource, + struct dma_resource_config *config) +{ + uint8_t new_channel; + + Assert(resource); + + system_interrupt_enter_critical_section(); + + if (!_dma_inst._dma_init) { + /* Initialize clocks for DMA */ +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + system_ahb_clock_set_mask(MCLK_AHBMASK_DMAC); +#else + system_ahb_clock_set_mask(PM_AHBMASK_DMAC); + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, + PM_APBBMASK_DMAC); +#endif + + /* Perform a software reset before enable DMA controller */ + DMAC->CTRL.reg &= ~DMAC_CTRL_DMAENABLE; + DMAC->CTRL.reg = DMAC_CTRL_SWRST; + + /* Setup descriptor base address and write back section base + * address */ + DMAC->BASEADDR.reg = (uint32_t)descriptor_section; + DMAC->WRBADDR.reg = (uint32_t)_write_back_section; + + /* Enable all priority level at the same time */ + DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); + + _dma_inst._dma_init = true; + } + + /* Find the proper channel */ + new_channel = _dma_find_first_free_channel_and_allocate(); + + /* If no channel available, return not found */ + if (new_channel == DMA_INVALID_CHANNEL) { + system_interrupt_leave_critical_section(); + + return STATUS_ERR_NOT_FOUND; + } + + /* Set the channel */ + resource->channel_id = new_channel; + + /** Perform a reset for the allocated channel */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; + DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; + +#ifdef FEATURE_DMA_CHANNEL_STANDBY + if(config->run_in_standby){ + DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_RUNSTDBY; + } +#endif + + /** Configure the DMA control,channel registers and descriptors here */ + _dma_set_config(resource, config); + + resource->descriptor = NULL; + + /* Log the DMA resource into the internal DMA resource pool */ + _dma_active_resource[resource->channel_id] = resource; + + system_interrupt_leave_critical_section(); + + return STATUS_OK; +} + +/** + * \brief Free an allocated DMA resource. + * + * This function will free an allocated DMA resource. + * + * \param[in,out] resource Pointer to the DMA resource + * + * \return Status of the free procedure. + * + * \retval STATUS_OK The DMA resource was freed successfully + * \retval STATUS_BUSY The DMA resource was busy and can't be freed + * \retval STATUS_ERR_NOT_INITIALIZED DMA resource was not initialized + */ +enum status_code dma_free(struct dma_resource *resource) +{ + Assert(resource); + Assert(resource->channel_id != DMA_INVALID_CHANNEL); + + system_interrupt_enter_critical_section(); + + /* Check if channel is busy */ + if (dma_is_busy(resource)) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Check if DMA resource was not allocated */ + if (!(_dma_inst.allocated_channels & (1 << resource->channel_id))) { + system_interrupt_leave_critical_section(); + return STATUS_ERR_NOT_INITIALIZED; + } + + /* Release the DMA resource */ + _dma_release_channel(resource->channel_id); + + /* Reset the item in the DMA resource pool */ + _dma_active_resource[resource->channel_id] = NULL; + + system_interrupt_leave_critical_section(); + + return STATUS_OK; +} + +/** + * \brief Start a DMA transfer. + * + * This function will start a DMA transfer through an allocated DMA resource. + * + * \param[in,out] resource Pointer to the DMA resource + * + * \return Status of the transfer start procedure. + * + * \retval STATUS_OK The transfer was started successfully + * \retval STATUS_BUSY The DMA resource was busy and the transfer was not started + * \retval STATUS_ERR_INVALID_ARG Transfer size is 0 and transfer was not started + */ +enum status_code dma_start_transfer_job(struct dma_resource *resource) +{ + Assert(resource); + Assert(resource->channel_id != DMA_INVALID_CHANNEL); + + system_interrupt_enter_critical_section(); + + /* Check if resource was busy */ + if (resource->job_status == STATUS_BUSY) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Check if transfer size is valid */ + if (resource->descriptor->BTCNT.reg == 0) { + system_interrupt_leave_critical_section(); + return STATUS_ERR_INVALID_ARG; + } + + /* Enable DMA interrupt */ + system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_DMA); + + /* Set the interrupt flag */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + DMAC->CHINTENSET.reg = (DMAC_CHINTENSET_MASK & g_chan_interrupt_flag[resource->channel_id]); + /* Set job status */ + resource->job_status = STATUS_BUSY; + + /* Set channel x descriptor 0 to the descriptor base address */ + memcpy(&descriptor_section[resource->channel_id], resource->descriptor, + sizeof(DmacDescriptor)); + + /* Enable the transfer channel */ + DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; + + system_interrupt_leave_critical_section(); + + return STATUS_OK; +} + +/** + * \brief Abort a DMA transfer. + * + * This function will abort a DMA transfer. The DMA channel used for the DMA + * resource will be disabled. + * The block transfer count will also be calculated and written to the DMA + * resource structure. + * + * \note The DMA resource will not be freed after calling this function. + * The function \ref dma_free() can be used to free an allocated resource. + * + * \param[in,out] resource Pointer to the DMA resource + * + */ +void dma_abort_job(struct dma_resource *resource) +{ + uint32_t write_size; + uint32_t total_size; + + Assert(resource); + Assert(resource->channel_id != DMA_INVALID_CHANNEL); + + system_interrupt_enter_critical_section(); + + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + DMAC->CHCTRLA.reg = 0; + + system_interrupt_leave_critical_section(); + + /* Get transferred size */ + total_size = descriptor_section[resource->channel_id].BTCNT.reg; + write_size = _write_back_section[resource->channel_id].BTCNT.reg; + resource->transfered_size = total_size - write_size; + + resource->job_status = STATUS_ABORTED; +} + +/** + * \brief Suspend a DMA transfer. + * + * This function will request to suspend the transfer of the DMA resource. + * The channel is kept enabled, can receive transfer triggers (the transfer + * pending bit will be set), but will be removed from the arbitration scheme. + * The channel operation can be resumed by calling \ref dma_resume_job(). + * + * \note This function sets the command to suspend the DMA channel + * associated with a DMA resource. The channel suspend interrupt flag + * indicates whether the transfer is truly suspended. + * + * \param[in] resource Pointer to the DMA resource + * + */ +void dma_suspend_job(struct dma_resource *resource) +{ + Assert(resource); + Assert(resource->channel_id != DMA_INVALID_CHANNEL); + + system_interrupt_enter_critical_section(); + + /* Select the channel */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + + /* Send the suspend request */ + DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_SUSPEND; + + system_interrupt_leave_critical_section(); +} + +/** + * \brief Resume a suspended DMA transfer. + * + * This function try to resume a suspended transfer of a DMA resource. + * + * \param[in] resource Pointer to the DMA resource + * + */ +void dma_resume_job(struct dma_resource *resource) +{ + uint32_t bitmap_channel; + uint32_t count = 0; + + Assert(resource); + Assert(resource->channel_id != DMA_INVALID_CHANNEL); + + /* Get bitmap of the allocated DMA channel */ + bitmap_channel = (1 << resource->channel_id); + + /* Check if channel was suspended */ + if (resource->job_status != STATUS_SUSPEND) { + return; + } + + system_interrupt_enter_critical_section(); + + /* Send resume request */ + DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); + DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_RESUME; + + system_interrupt_leave_critical_section(); + + /* Check if transfer job resumed */ + for (count = 0; count < MAX_JOB_RESUME_COUNT; count++) { + if ((DMAC->BUSYCH.reg & bitmap_channel) == bitmap_channel) { + break; + } + } + + if (count < MAX_JOB_RESUME_COUNT) { + /* Job resumed */ + resource->job_status = STATUS_BUSY; + } else { + /* Job resume timeout */ + resource->job_status = STATUS_ERR_TIMEOUT; + } +} + +/** + * \brief Create a DMA transfer descriptor with configurations. + * + * This function will set the transfer configurations to the DMA transfer + * descriptor. + * + * \param[in] descriptor Pointer to the DMA transfer descriptor + * \param[in] config Pointer to the descriptor configuration structure + * + */ +void dma_descriptor_create(DmacDescriptor* descriptor, + struct dma_descriptor_config *config) +{ + /* Set block transfer control */ + descriptor->BTCTRL.bit.VALID = config->descriptor_valid; + descriptor->BTCTRL.bit.EVOSEL = config->event_output_selection; + descriptor->BTCTRL.bit.BLOCKACT = config->block_action; + descriptor->BTCTRL.bit.BEATSIZE = config->beat_size; + descriptor->BTCTRL.bit.SRCINC = config->src_increment_enable; + descriptor->BTCTRL.bit.DSTINC = config->dst_increment_enable; + descriptor->BTCTRL.bit.STEPSEL = config->step_selection; + descriptor->BTCTRL.bit.STEPSIZE = config->step_size; + + /* Set transfer size, source address and destination address */ + descriptor->BTCNT.reg = config->block_transfer_count; + descriptor->SRCADDR.reg = config->source_address; + descriptor->DSTADDR.reg = config->destination_address; + + /* Set next transfer descriptor address */ + descriptor->DESCADDR.reg = config->next_descriptor_address; +} + +/** + * \brief Add a DMA transfer descriptor to a DMA resource. + * + * This function will add a DMA transfer descriptor to a DMA resource. + * If there was a transfer descriptor already allocated to the DMA resource, + * the descriptor will be linked to the next descriptor address. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] descriptor Pointer to the transfer descriptor + * + * \retval STATUS_OK The descriptor is added to the DMA resource + * \retval STATUS_BUSY The DMA resource was busy and the descriptor is not added + */ +enum status_code dma_add_descriptor(struct dma_resource *resource, + DmacDescriptor* descriptor) +{ + DmacDescriptor* desc = resource->descriptor; + + if (resource->job_status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Look up for an empty space for the descriptor */ + if (desc == NULL) { + resource->descriptor = descriptor; + } else { + /* Looking for end of descriptor link */ + while(desc->DESCADDR.reg != 0) { + desc = (DmacDescriptor*)(desc->DESCADDR.reg); + } + + /* Set to the end of descriptor list */ + desc->DESCADDR.reg = (uint32_t)descriptor; + } + + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/dma/dma.h b/atmel-samd/asf/sam0/drivers/dma/dma.h new file mode 100644 index 000000000..1cf3498d9 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/dma.h @@ -0,0 +1,882 @@ +/** + * \file + * + * \brief SAM Direct Memory Access Controller Driver + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DMA_H_INCLUDED +#define DMA_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup asfdoc_sam0_dma_group SAM Direct Memory Access Controller (DMAC) Driver + * + * This driver for Atmel® | SMART ARMĀ®-based microcontrollers provides an interface for the configuration + * and management of the Direct Memory Access Controller(DMAC) module within + * the device. The DMAC can transfer data between memories and peripherals, and + * thus off-load these tasks from the CPU. The module supports peripheral to + * peripheral, peripheral to memory, memory to peripheral, and memory to memory + * transfers. + * + * The following peripheral is used by the DMAC Driver: + * - DMAC (Direct Memory Access Controller) + * + * The following devices can use this module: + * - Atmel | SMART SAM D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * - Atmel | SMART SAM R30 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_dma_prerequisites + * - \ref asfdoc_sam0_dma_module_overview + * - \ref asfdoc_sam0_dma_special_considerations + * - \ref asfdoc_sam0_dma_extra_info + * - \ref asfdoc_sam0_dma_examples + * - \ref asfdoc_sam0_dma_api_overview + * + * + * \section asfdoc_sam0_dma_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * + * \section asfdoc_sam0_dma_module_overview Module Overview + * + * SAM devices with DMAC enables high data transfer rates with minimum + * CPU intervention and frees up CPU time. With access to all peripherals, + * the DMAC can handle automatic transfer of data to/from modules. + * It supports static and incremental addressing for both source and + * destination. + * + * The DMAC when used with Event System or peripheral triggers, provides a + * considerable advantage by reducing the power consumption and performing + * data transfer in the background. + * For example, if the ADC is configured to generate an event, it can trigger + * the DMAC to transfer the data into another peripheral or SRAM. + * The CPU can remain in sleep during this time to reduce the power consumption. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
DeviceDma channel number
SAM D21/R21/C20/C2112
SAM D09/D10/D116
SAM L21,SAMR3016
+ * The DMA channel operation can be suspended at any time by software, by events + * from event system, or after selectable descriptor execution. The operation + * can be resumed by software or by events from the event system. + * The DMAC driver for SAM supports four types of transfers such as + * peripheral to peripheral, peripheral to memory, memory to peripheral, and + * memory to memory. + * + * The basic transfer unit is a beat, which is defined as a single bus access. + * There can be multiple beats in a single block transfer and multiple block + * transfers in a DMA transaction. + * DMA transfer is based on descriptors, which holds transfer properties + * such as the source and destination addresses, transfer counter, and other + * additional transfer control information. + * The descriptors can be static or linked. When static, a single block transfer + * is performed. When linked, a number of transfer descriptors can be used to + * enable multiple block transfers within a single DMA transaction. + * + * The implementation of the DMA driver is based on the idea that the DMA channel + * is a finite resource of entities with the same abilities. A DMA channel resource + * is able to move a defined set of data from a source address to destination + * address triggered by a transfer trigger. On the SAM devices there are 12 + * DMA resources available for allocation. Each of these DMA resources can trigger + * interrupt callback routines and peripheral events. + * The other main features are: + * + * - Selectable transfer trigger source + * - Software + * - Event System + * - Peripheral + * - Event input and output is supported for the four lower channels + * - Four level channel priority + * - Optional interrupt generation on transfer complete, channel error, or channel suspend + * - Supports multi-buffer or circular buffer mode by linking multiple descriptors + * - Beat size configurable as 8-bit, 16-bit, or 32-bit + * + * A simplified block diagram of the DMA Resource can be seen in + * \ref asfdoc_sam0_dma_module_block_diagram "the figure below". + * + * \anchor asfdoc_sam0_dma_module_block_diagram + * \dot + * digraph overview { + * splines = false; + * rankdir=LR; + * + * mux1 [label="Transfer Trigger", shape=box]; + * + * dma [label="DMA Channel", shape=polygon, sides=6, orientation=60, style=filled, fillcolor=darkolivegreen1, height=1, width=1]; + * descriptor [label="Transfer Descriptor", shape=box, style=filled, fillcolor=lightblue]; + * + * mux1 -> dma; + * descriptor -> dma; + * + * interrupt [label="Interrupt", shape=box]; + * events [label="Events", shape=box]; + * + * dma:e -> interrupt:w; + * dma:e -> events:w; + * + * {rank=same; descriptor dma} + * + * } + * \enddot + * + * \subsection asfdoc_sam0_dma_features Driver Feature Macro Definition + * + * + * + * + * + * + * + * + * + *
Driver Feature MacroSupported devices
FEATURE_DMA_CHANNEL_STANDBYSAM L21/L22/C20/C21/R30
+ * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_dma_module_overview_dma_transf_term Terminology Used in DMAC Transfers + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Name Description
Beat It is a single bus access by the DMAC. + * Configurable as 8-bit, 16-bit, or 32-bit. + *
Burst It is a transfer of n-beats (n=1,4,8,16). + * For the DMAC module in SAM, the burst size is one beat. + * Arbitration takes place each time a burst transfer is completed. + *
Block transfer A single block transfer is a configurable number of (1 to 64k) + * beat transfers + *
+ * + * \subsection asfdoc_sam0_dma_module_overview_dma_channels DMA Channels + * The DMAC in each device consists of several DMA channels, which + * along with the transfer descriptors defines the data transfer properties. + * - The transfer control descriptor defines the source and destination + * addresses, source and destination address increment settings, the + * block transfer count, and event output condition selection + * - Dedicated channel registers control the peripheral trigger source, + * trigger mode settings, event input actions, and channel priority level + * settings + * + * With a successful DMA resource allocation, a dedicated + * DMA channel will be assigned. The channel will be occupied until the + * DMA resource is freed. A DMA resource handle is used to identify the specific + * DMA resource. + * When there are multiple channels with active requests, the arbiter prioritizes + * the channels requesting access to the bus. + * + * \subsection asfdoc_sam0_dma_module_overview_dma_trigger DMA Triggers + * DMA transfer can be started only when a DMA transfer request is acknowledged/granted by the arbiter. A + * transfer request can be triggered from software, peripheral, or an event. There + * are dedicated source trigger selections for each DMA channel usage. + + * + * \subsection asfdoc_sam0_dma_module_overview_dma_transfer_descriptor DMA Transfer Descriptor + * The transfer descriptor resides in the SRAM and + * defines these channel properties. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Field name Field width
Descriptor Next Address 32 bits
Destination Address 32 bits
Source Address 32 bits
Block Transfer Counter 16 bits
Block Transfer Control 16 bits
+ * + * Before starting a transfer, at least one descriptor should be configured. + * After a successful allocation of a DMA channel, the transfer descriptor can + * be added with a call to \ref dma_add_descriptor(). If there is a transfer + * descriptor already allocated to the DMA resource, the descriptor will + * be linked to the next descriptor address. + * + * \subsection asfdoc_sam0_dma_module_overview_dma_output DMA Interrupts/Events + * Both an interrupt callback and an peripheral event can be triggered by the + * DMA transfer. Three types of callbacks are supported by the DMA driver: + * transfer complete, channel suspend, and transfer error. Each of these callback + * types can be registered and enabled for each channel independently through + * the DMA driver API. + * + * The DMAC module can also generate events on transfer complete. Event + * generation is enabled through the DMA channel, event channel configuration, + * and event user multiplexing is done through the events driver. + * + * The DMAC can generate events in the below cases: + * + * - When a block transfer is complete + * + * - When each beat transfer within a block transfer is complete + * + * \section asfdoc_sam0_dma_special_considerations Special Considerations + * + * There are no special considerations for this module. + * + * + * \section asfdoc_sam0_dma_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_dma_extra. This includes: + * - \ref asfdoc_sam0_dma_extra_acronyms + * - \ref asfdoc_sam0_dma_extra_dependencies + * - \ref asfdoc_sam0_dma_extra_errata + * - \ref asfdoc_sam0_dma_extra_history + * + * + * \section asfdoc_sam0_dma_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_dma_exqsg. + * + * + * \section asfdoc_sam0_dma_api_overview API Overview + * @{ + */ + +#include +#include "conf_dma.h" + +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) || defined(__DOXYGEN__) +#define FEATURE_DMA_CHANNEL_STANDBY +#endif + +/** DMA invalid channel number. */ +#define DMA_INVALID_CHANNEL 0xff + +/** ExInitial description section. */ +extern DmacDescriptor descriptor_section[CONF_MAX_USED_CHANNEL_NUM]; + +/* DMA channel interrup flag. */ +extern uint8_t g_chan_interrupt_flag[CONF_MAX_USED_CHANNEL_NUM]; + +/** DMA priority level. */ +enum dma_priority_level { + /** Priority level 0. */ + DMA_PRIORITY_LEVEL_0, + /** Priority level 1. */ + DMA_PRIORITY_LEVEL_1, + /** Priority level 2. */ + DMA_PRIORITY_LEVEL_2, + /** Priority level 3. */ + DMA_PRIORITY_LEVEL_3, +}; + +/** DMA input actions. */ +enum dma_event_input_action { + /** No action. */ + DMA_EVENT_INPUT_NOACT, + /** Normal transfer and periodic transfer trigger. */ + DMA_EVENT_INPUT_TRIG, + /** Conditional transfer trigger. */ + DMA_EVENT_INPUT_CTRIG, + /** Conditional block transfer. */ + DMA_EVENT_INPUT_CBLOCK, + /** Channel suspend operation. */ + DMA_EVENT_INPUT_SUSPEND, + /** Channel resume operation. */ + DMA_EVENT_INPUT_RESUME, + /** Skip next block suspend action. */ + DMA_EVENT_INPUT_SSKIP, +}; + +/** + * Address increment step size. These bits select the address increment step + * size. The setting apply to source or destination address, depending on + * STEPSEL setting. + */ +enum dma_address_increment_stepsize { + /** The address is incremented by (beat size * 1). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_1 = 0, + /** The address is incremented by (beat size * 2). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_2, + /** The address is incremented by (beat size * 4). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_4, + /** The address is incremented by (beat size * 8). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_8, + /** The address is incremented by (beat size * 16). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_16, + /** The address is incremented by (beat size * 32). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_32, + /** The address is incremented by (beat size * 64). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_64, + /** The address is incremented by (beat size * 128). */ + DMA_ADDRESS_INCREMENT_STEP_SIZE_128, +}; + +/** + * DMA step selection. This bit determines whether the step size setting + * is applied to source or destination address. + */ +enum dma_step_selection { + /** Step size settings apply to the destination address. */ + DMA_STEPSEL_DST = 0, + /** Step size settings apply to the source address. */ + DMA_STEPSEL_SRC, +}; + +/** The basic transfer unit in DMAC is a beat, which is defined as a + * single bus access. Its size is configurable and applies to both read + * and write. */ +enum dma_beat_size { + /** 8-bit access. */ + DMA_BEAT_SIZE_BYTE = 0, + /** 16-bit access. */ + DMA_BEAT_SIZE_HWORD, + /** 32-bit access. */ + DMA_BEAT_SIZE_WORD, +}; + +/** + * Block action definitions. + */ +enum dma_block_action { + /** No action. */ + DMA_BLOCK_ACTION_NOACT = 0, + /** Channel in normal operation and sets transfer complete interrupt flag + * after block transfer. */ + DMA_BLOCK_ACTION_INT, + /** Trigger channel suspend after block transfer and sets channel + * suspend interrupt flag once the channel is suspended. */ + DMA_BLOCK_ACTION_SUSPEND, + /** Sets transfer complete interrupt flag after a block transfer and + * trigger channel suspend. The channel suspend interrupt flag will be set + * once the channel is suspended. */ + DMA_BLOCK_ACTION_BOTH, +}; + +/** Event output selection. */ +enum dma_event_output_selection { + /** Event generation disable. */ + DMA_EVENT_OUTPUT_DISABLE = 0, + /** Event strobe when block transfer complete. */ + DMA_EVENT_OUTPUT_BLOCK, + /** Event output reserved. */ + DMA_EVENT_OUTPUT_RESERVED, + /** Event strobe when beat transfer complete. */ + DMA_EVENT_OUTPUT_BEAT, +}; + +/** DMA trigger action type. */ +enum dma_transfer_trigger_action{ + /** Perform a block transfer when triggered. */ + DMA_TRIGGER_ACTION_BLOCK = DMAC_CHCTRLB_TRIGACT_BLOCK_Val, + /** Perform a beat transfer when triggered. */ + DMA_TRIGGER_ACTION_BEAT = DMAC_CHCTRLB_TRIGACT_BEAT_Val, + /** Perform a transaction when triggered. */ + DMA_TRIGGER_ACTION_TRANSACTION = DMAC_CHCTRLB_TRIGACT_TRANSACTION_Val, +}; + +/** + * Callback types for DMA callback driver. + */ +enum dma_callback_type { + /** Callback for any of transfer errors. A transfer error is flagged + * if a bus error is detected during an AHB access or when the DMAC + * fetches an invalid descriptor. */ + DMA_CALLBACK_TRANSFER_ERROR, + /** Callback for transfer complete. */ + DMA_CALLBACK_TRANSFER_DONE, + /** Callback for channel suspend. */ + DMA_CALLBACK_CHANNEL_SUSPEND, + /** Number of available callbacks. */ + DMA_CALLBACK_N, +}; + +/** + * DMA transfer descriptor configuration. When the source or destination address + * increment is enabled, the addresses stored into the configuration structure + * must correspond to the end of the transfer. + * + */ +struct dma_descriptor_config { + /** Descriptor valid flag used to identify whether a descriptor is + valid or not */ + bool descriptor_valid; + /** This is used to generate an event on specific transfer action in + a channel. Supported only in four lower channels. */ + enum dma_event_output_selection event_output_selection; + /** Action taken when a block transfer is completed */ + enum dma_block_action block_action; + /** Beat size is configurable as 8-bit, 16-bit, or 32-bit */ + enum dma_beat_size beat_size; + /** Used for enabling the source address increment */ + bool src_increment_enable; + /** Used for enabling the destination address increment */ + bool dst_increment_enable; + /** This bit selects whether the source or destination address is + using the step size settings */ + enum dma_step_selection step_selection; + /** The step size for source/destination address increment. + The next address is calculated + as next_addr = addr + (2^step_size * beat size). */ + enum dma_address_increment_stepsize step_size; + /** It is the number of beats in a block. This count value is + * decremented by one after each beat data transfer. */ + uint16_t block_transfer_count; + /** Transfer source address */ + uint32_t source_address; + /** Transfer destination address */ + uint32_t destination_address; + /** Set to zero for static descriptors. This must have a valid memory + address for linked descriptors. */ + uint32_t next_descriptor_address; +}; + +/** Configurations for DMA events. */ +struct dma_events_config { + /** Event input actions */ + enum dma_event_input_action input_action; + /** Enable DMA event output */ + bool event_output_enable; +}; + +/** DMA configurations for transfer. */ +struct dma_resource_config { + /** DMA transfer priority */ + enum dma_priority_level priority; + /**DMA peripheral trigger index */ + uint8_t peripheral_trigger; + /** DMA trigger action */ + enum dma_transfer_trigger_action trigger_action; +#ifdef FEATURE_DMA_CHANNEL_STANDBY + /** Keep DMA channel enabled in standby sleep mode if true */ + bool run_in_standby; +#endif + /** DMA events configurations */ + struct dma_events_config event_config; +}; + +/** Forward definition of the DMA resource. */ +struct dma_resource; +/** Type definition for a DMA resource callback function. */ +typedef void (*dma_callback_t)(struct dma_resource *const resource); + +/** Structure for DMA transfer resource. */ +struct dma_resource { + /** Allocated DMA channel ID */ + uint8_t channel_id; + /** Array of callback functions for DMA transfer job */ + dma_callback_t callback[DMA_CALLBACK_N]; + /** Bit mask for enabled callbacks */ + uint8_t callback_enable; + /** Status of the last job */ + volatile enum status_code job_status; + /** Transferred data size */ + uint32_t transfered_size; + /** DMA transfer descriptor */ + DmacDescriptor* descriptor; +}; + +/** + * \brief Get DMA resource status. + * + * \param[in] resource Pointer to the DMA resource + * + * \return Status of the DMA resource. + */ +static inline enum status_code dma_get_job_status(struct dma_resource *resource) +{ + Assert(resource); + + return resource->job_status; +} + +/** + * \brief Check if the given DMA resource is busy. + * + * \param[in] resource Pointer to the DMA resource + * + * \return Status which indicates whether the DMA resource is busy. + * + * \retval true The DMA resource has an on-going transfer + * \retval false The DMA resource is not busy + */ +static inline bool dma_is_busy(struct dma_resource *resource) +{ + Assert(resource); + + return (resource->job_status == STATUS_BUSY); +} + +/** + * \brief Enable a callback function for a dedicated DMA resource. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_enable_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + Assert(resource); + + resource->callback_enable |= 1 << type; + g_chan_interrupt_flag[resource->channel_id] |= (1UL << type); +} + +/** + * \brief Disable a callback function for a dedicated DMA resource. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_disable_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + Assert(resource); + + resource->callback_enable &= ~(1 << type); + g_chan_interrupt_flag[resource->channel_id] &= (~(1UL << type) & DMAC_CHINTENSET_MASK); + DMAC->CHINTENCLR.reg = (1UL << type); +} + +/** + * \brief Register a callback function for a dedicated DMA resource. + * + * There are three types of callback functions, which can be registered: + * - Callback for transfer complete + * - Callback for transfer error + * - Callback for channel suspend + * + * \param[in] resource Pointer to the DMA resource + * \param[in] callback Pointer to the callback function + * \param[in] type Callback function type + * + */ +static inline void dma_register_callback(struct dma_resource *resource, + dma_callback_t callback, enum dma_callback_type type) +{ + Assert(resource); + + resource->callback[type] = callback; +} + +/** + * \brief Unregister a callback function for a dedicated DMA resource. + * + * There are three types of callback functions: + * - Callback for transfer complete + * - Callback for transfer error + * - Callback for channel suspend + * + * The application can unregister any of the callback functions which + * are already registered and are no longer needed. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_unregister_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + Assert(resource); + + resource->callback[type] = NULL; +} + +/** + * \brief Will set a software trigger for resource. + * + * This function is used to set a software trigger on the DMA channel + * associated with resource. If a trigger is already pending no new trigger + * will be generated for the channel. + * + * \param[in] resource Pointer to the DMA resource + */ +static inline void dma_trigger_transfer(struct dma_resource *resource) { + Assert(resource); + + DMAC->SWTRIGCTRL.reg |= (1 << resource->channel_id); +} + +/** + * \brief Initializes DMA transfer configuration with predefined default values. + * + * This function will initialize a given DMA descriptor configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structure before being + * modified by the user application. + * + * The default configuration is as follows: + * \li Set the descriptor as valid + * \li Disable event output + * \li No block action + * \li Set beat size as byte + * \li Enable source increment + * \li Enable destination increment + * \li Step size is applied to the destination address + * \li Address increment is beat size multiplied by 1 + * \li Default transfer size is set to 0 + * \li Default source address is set to NULL + * \li Default destination address is set to NULL + * \li Default next descriptor not available + * \param[out] config Pointer to the configuration + * + */ +static inline void dma_descriptor_get_config_defaults(struct dma_descriptor_config *config) +{ + Assert(config); + + /* Set descriptor as valid */ + config->descriptor_valid = true; + /* Disable event output */ + config->event_output_selection = DMA_EVENT_OUTPUT_DISABLE; + /* No block action */ + config->block_action = DMA_BLOCK_ACTION_NOACT; + /* Set beat size to one byte */ + config->beat_size = DMA_BEAT_SIZE_BYTE; + /* Enable source increment */ + config->src_increment_enable = true; + /* Enable destination increment */ + config->dst_increment_enable = true; + /* Step size is applied to the destination address */ + config->step_selection = DMA_STEPSEL_DST; + /* Address increment is beat size multiplied by 1*/ + config->step_size = DMA_ADDRESS_INCREMENT_STEP_SIZE_1; + /* Default transfer size is set to 0 */ + config->block_transfer_count = 0; + /* Default source address is set to NULL */ + config->source_address = (uint32_t)NULL; + /* Default destination address is set to NULL */ + config->destination_address = (uint32_t)NULL; + /** Next descriptor address set to 0 */ + config->next_descriptor_address = 0; +} + +/** + * \brief Update DMA descriptor. + * + * This function can update the descriptor of an allocated DMA resource. + * + */ +static inline void dma_update_descriptor(struct dma_resource *resource, + DmacDescriptor* descriptor) +{ + Assert(resource); + + resource->descriptor = descriptor; +} + +/** + * \brief Reset DMA descriptor. + * + * This function will clear the DESCADDR register of an allocated DMA resource. + * + */ +static inline void dma_reset_descriptor(struct dma_resource *resource) +{ + Assert(resource); + + resource->descriptor = NULL; +} + +void dma_get_config_defaults(struct dma_resource_config *config); +enum status_code dma_allocate(struct dma_resource *resource, + struct dma_resource_config *config); +enum status_code dma_free(struct dma_resource *resource); +enum status_code dma_start_transfer_job(struct dma_resource *resource); +void dma_abort_job(struct dma_resource *resource); +void dma_suspend_job(struct dma_resource *resource); +void dma_resume_job(struct dma_resource *resource); +void dma_descriptor_create(DmacDescriptor* descriptor, + struct dma_descriptor_config *config); +enum status_code dma_add_descriptor(struct dma_resource *resource, + DmacDescriptor* descriptor); + +/** @} */ + +/** + * \page asfdoc_sam0_dma_extra Extra Information for DMAC Driver + * + * \section asfdoc_sam0_dma_extra_acronyms Acronyms + * Below is a table listing the acronyms used in this module, along with their + * intended meanings. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
DMADirect Memory Access
DMACDirect Memory Access Controller
CPUCentral Processing Unit
+ * + * + * \section asfdoc_sam0_dma_extra_dependencies Dependencies + * This driver has the following dependencies: + * + * - \ref asfdoc_sam0_system_clock_group "System Clock Driver" + * + * + * \section asfdoc_sam0_dma_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_dma_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Changelog
Add SAM C21 support
Add SAM L21 support
Add SAM R30 support
Initial Release
+ */ + + /** + * \page asfdoc_sam0_dma_exqsg Examples for DMAC Driver + * + * This is a list of the available Quick Start Guides (QSGs) and example + * applications for \ref asfdoc_sam0_dma_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_dma_basic_use_case + * + * \note More DMA usage examples are available in peripheral QSGs. + * A quick start guide for TC/TCC + * shows the usage of DMA event trigger; SERCOM SPI/USART/I2C has example for + * DMA transfer from peripheral to memory or from memory to peripheral; + * ADC/DAC shows peripheral to peripheral transfer. + * + * \page asfdoc_sam0_dma_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev.DateComments
42257C12/2015Added suppport for SAM L21/L22, SAM C21, SAM D09, SAMR30 and SAM DA1
42257B12/2014Added support for SAM R21 and SAM D10/D11
42257A02/2014Initial release
+ */ + +#ifdef __cplusplus +} +#endif + +#endif /* DMA_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/dma/dma_crc.h b/atmel-samd/asf/sam0/drivers/dma/dma_crc.h new file mode 100644 index 000000000..3d8c27c19 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/dma_crc.h @@ -0,0 +1,230 @@ +/** + * \file + * + * \brief SAM DMA cyclic redundancy check (CRC) Driver + * + * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DMA_CRC_H_INCLUDED +#define DMA_CRC_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** DMA channel n offset. */ +#define DMA_CRC_CHANNEL_N_OFFSET 0x20 + +/** CRC Polynomial Type. */ +enum crc_polynomial_type { + /** CRC16 (CRC-CCITT). */ + CRC_TYPE_16, + /** CRC32 (IEEE 802.3). */ + CRC_TYPE_32, +}; + +/** CRC Beat Type. */ +enum crc_beat_size { + /** Byte bus access. */ + CRC_BEAT_SIZE_BYTE, + /** Half-word bus access. */ + CRC_BEAT_SIZE_HWORD, + /** Word bus access. */ + CRC_BEAT_SIZE_WORD, +}; + +/** Configurations for CRC calculation. */ +struct dma_crc_config { + /** CRC polynomial type. */ + enum crc_polynomial_type type; + /** CRC beat size. */ + enum crc_beat_size size; +}; + +/** + * \brief Get DMA CRC default configurations. + * + * The default configuration is as follows: + * \li Polynomial type is set to CRC-16(CRC-CCITT) + * \li CRC Beat size: BYTE + * + * \param[in] config default configurations + */ +static inline void dma_crc_get_config_defaults(struct dma_crc_config *config) +{ + Assert(config); + + config->type = CRC_TYPE_16; + config->size = CRC_BEAT_SIZE_BYTE; +} + +/** + * \brief Enable DMA CRC module with an DMA channel. + * + * This function enables a CRC calculation with an allocated DMA channel. This channel ID + * can be gotten from a successful \ref dma_allocate. + * + * \param[in] channel_id DMA channel expected with CRC calculation + * \param[in] config CRC calculation configurations + * + * \return Status of the DMC CRC. + * \retval STATUS_OK Get the DMA CRC module + * \retval STATUS_BUSY DMA CRC module is already taken and not ready yet + */ +static inline enum status_code dma_crc_channel_enable(uint32_t channel_id, + struct dma_crc_config *config) +{ + if (DMAC->CRCSTATUS.reg & DMAC_CRCSTATUS_CRCBUSY) { + return STATUS_BUSY; + } + + DMAC->CRCCTRL.reg = DMAC_CRCCTRL_CRCBEATSIZE(config->size) | + DMAC_CRCCTRL_CRCPOLY(config->type) | + DMAC_CRCCTRL_CRCSRC(channel_id+DMA_CRC_CHANNEL_N_OFFSET); + + DMAC->CTRL.reg |= DMAC_CTRL_CRCENABLE; + + return STATUS_OK; +} + +/** + * \brief Disable DMA CRC module. + * + */ +static inline void dma_crc_disable(void) +{ + DMAC->CTRL.reg &= ~DMAC_CTRL_CRCENABLE; + DMAC->CRCCTRL.reg = 0; +} + +/** + * \brief Get DMA CRC checksum value. + * + * \return Calculated CRC checksum. + */ +static inline uint32_t dma_crc_get_checksum(void) +{ + if (DMAC->CRCCTRL.bit.CRCSRC == DMAC_CRCCTRL_CRCSRC_IO_Val) { + DMAC->CRCSTATUS.reg = DMAC_CRCSTATUS_CRCBUSY; + } + + return DMAC->CRCCHKSUM.reg; +} + +/** + * \brief Enable DMA CRC module with I/O. + * + * This function enables a CRC calculation with I/O mode. + * + * \param[in] config CRC calculation configurations. + * + * \return Status of the DMC CRC. + * \retval STATUS_OK Get the DMA CRC module + * \retval STATUS_BUSY DMA CRC module is already taken and not ready yet + */ +static inline enum status_code dma_crc_io_enable( + struct dma_crc_config *config) +{ + if (DMAC->CRCSTATUS.reg & DMAC_CRCSTATUS_CRCBUSY) { + return STATUS_BUSY; + } + + if (DMAC->CTRL.reg & DMAC_CTRL_CRCENABLE) { + return STATUS_BUSY; + } + + DMAC->CRCCTRL.reg = DMAC_CRCCTRL_CRCBEATSIZE(config->size) | + DMAC_CRCCTRL_CRCPOLY(config->type) | + DMAC_CRCCTRL_CRCSRC_IO; + + if (config->type == CRC_TYPE_32) { + DMAC->CRCCHKSUM.reg = 0xFFFFFFFF; + } + + DMAC->CTRL.reg |= DMAC_CTRL_CRCENABLE; + + return STATUS_OK; +} + +/** + * \brief Calculate CRC with I/O. + * + * This function calculate the CRC of the input data buffer. + * + * \param[in] buffer CRC Pointer to calculation buffer + * \param[in] total_beat_size Total beat size to be calculated + * + * \return Calculated CRC checksum value. + */ +static inline void dma_crc_io_calculation(void *buffer, + uint32_t total_beat_size) +{ + uint32_t counter = total_beat_size; + uint8_t *buffer_8; + uint16_t *buffer_16; + uint32_t *buffer_32; + + for (counter=0; counterCRCCTRL.bit.CRCBEATSIZE == CRC_BEAT_SIZE_BYTE) { + buffer_8 = buffer; + DMAC->CRCDATAIN.reg = buffer_8[counter]; + } else if (DMAC->CRCCTRL.bit.CRCBEATSIZE == CRC_BEAT_SIZE_HWORD) { + buffer_16 = buffer; + DMAC->CRCDATAIN.reg = buffer_16[counter]; + } else if (DMAC->CRCCTRL.bit.CRCBEATSIZE == CRC_BEAT_SIZE_WORD) { + buffer_32 = buffer; + DMAC->CRCDATAIN.reg = buffer_32[counter]; + } + /* Wait several cycle to make sure CRC complete */ + nop(); + nop(); + nop(); + nop(); + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* DMA_CRC_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c b/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c new file mode 100644 index 000000000..d14f0e5ee --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c @@ -0,0 +1,557 @@ +/* + * \file + * + * \brief Direct Memory Access Controller Driver for SAMB + * + * Copyright (C) 2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include +#include "dma_sam_b.h" + +struct _dma_module { + volatile bool _dma_init; + volatile uint32_t allocated_channels; + uint8_t free_channels; +}; + +struct _dma_module _dma_inst = { + ._dma_init = false, + .allocated_channels = 0, + .free_channels = CONF_MAX_USED_CHANNEL_NUM, +}; + +/** Internal DMA resource pool. */ +static struct dma_resource* _dma_active_resource[CONF_MAX_USED_CHANNEL_NUM]; + +/** + * \brief Get the assigned channel DMA value. + * + * \param[in] channel DMA channel index + * \param[in] DMA register address + * + * \return The value of DMA register. + */ +static uint32_t get_channel_reg_val(uint8_t channel, uint32_t reg) +{ + return *(uint32_t*)(reg + 0x100*channel); +} + +/** + * \brief Set the assigned channel DMA value. + * + * \param[in] channel DMA channel index + * \param[in] DMA register address + * \param[in] The value to be set + * + */ +static void set_channel_reg_val(uint8_t channel, uint32_t reg, uint32_t val) +{ + *(uint32_t*)(reg + 0x100*channel) = val; +} + +/** + * \brief Get the DMA status. + * + * \param[in] channel DMA channel index + * + * \return The status of DMA + */ +uint8_t dma_get_status(uint8_t channel) +{ + return (uint8_t)get_channel_reg_val(channel, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_RAWSTAT_REG.reg); +} + +/** + * \brief Get the DMA interrupt status. + * + * \param[in] channel DMA channel index + * + * \return The interrupt of status DMA + */ +uint8_t dma_get_interrupt_status(uint8_t channel) +{ + return get_channel_reg_val(channel, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_STATUS_REG.reg); +} + +/** + * \brief Get the DMA interrupt status. + * + * \param[in] channel DMA channel index + * \param[in] flag The interrupt flag want to clear + * + */ +void dma_clear_interrupt_status(uint8_t channel, uint8_t flag) +{ + set_channel_reg_val(channel, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_CLEAR_REG.reg, 1 << flag); +} + +/** + * \brief Find a free channel for a DMA resource. + * + * Find a channel for the requested DMA resource. + * + * \return Status of channel allocation. + * \retval DMA_INVALID_CHANNEL No channel available + * \retval count Allocated channel for the DMA resource + */ +static uint8_t _dma_find_first_free_channel_and_allocate(void) +{ + uint8_t count; + uint32_t tmp; + bool allocated = false; + + tmp = _dma_inst.allocated_channels; + + for (count = 0; count < CONF_MAX_USED_CHANNEL_NUM; ++count) { + if (!(tmp & 0x00000001)) { + /* If free channel found, set as allocated and return + *number */ + + _dma_inst.allocated_channels |= 1 << count; + _dma_inst.free_channels--; + allocated = true; + + break; + } + + tmp = tmp >> 1; + } + + if (!allocated) { + return DMA_INVALID_CHANNEL; + } else { + return count; + } +} + +/** + * \brief Release an allocated DMA channel. + * + * \param[in] channel Channel id to be released + * + */ +static void _dma_release_channel(uint8_t channel) +{ + _dma_inst.allocated_channels &= ~(1 << channel); + _dma_inst.free_channels++; +} + +/** + * \brief Initializes config with predefined default values. + * + * This function will initialize a given DMA configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structure before being + * modified by the user application. + * + * The default configuration is as follows: + * \li Set source max burst number as 1 + * \li Set source tokens as 1 + * \li Set source peripheral as memory + * \li Set source peripheral delay as 0 + * \li Disable source top priority + * \li Set source top priority channel as 0 + * \li Disable source high priority + * \li Set source high priority channel as 0 + * \li Set destination max burst number as 1 + * \li Set destination tokens as 1 + * \li Set destination peripheral as memory + * \li Set destination peripheral delay as 0 + * \li Disable destination top priority + * \li Set destination top priority channel as 0 + * \li Disable destination high priority + * \li Set destination high priority channel as 0 + * \li Disable the joint mode + * \li Disable the endian swap + * \param[out] config Pointer to the configuration + * + */ +void dma_get_config_defaults(struct dma_resource_config *config) +{ + /* DMA source configuration */ + config->src.max_burst = 1; + config->src.tokens = 1; + config->src.enable_inc_addr = true; + config->src.periph = MEMORY_DMA_PERIPHERAL; + config->src.periph_delay = 0; + config->src.enable_proi_top = false; + config->src.proi_top_index = 0; + config->src.enable_proi_high = false; + config->src.proi_high_index = 0; + /* DMA destination configuration */ + config->des.max_burst = 1; + config->des.tokens = 1; + config->des.enable_inc_addr = true; + config->des.periph = MEMORY_DMA_PERIPHERAL; + config->des.periph_delay = 0; + config->des.enable_proi_top = false; + config->des.proi_top_index = 0; + config->des.enable_proi_high = false; + config->des.proi_high_index = 0; + /* DMA channel configuration */ + config->enable_joint_mode = false; + config->swap = DMA_ENDIAN_NO_SWAP; +} + +/** + * \brief Configure the DMA resource. + * + * \param[in] dma_resource Pointer to a DMA resource instance + * \param[out] config Configurations of the DMA resource + * + */ +static void _dma_set_config(struct dma_resource *resource, + struct dma_resource_config *config) +{ + uint32_t regval = 0; + + /* Static register configuration */ + regval = PROV_DMA_CTRL_CH0_STATIC_REG0_RD_BURST_MAX_SIZE(config->src.max_burst)| + PROV_DMA_CTRL_CH0_STATIC_REG0_RD_TOKENS(config->src.tokens) | + (config->src.enable_inc_addr << PROV_DMA_CTRL_CH0_STATIC_REG0_RD_INCR_Pos); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_STATIC_REG0.reg, regval); + /* Static register1 configuration */ + regval = PROV_DMA_CTRL_CH0_STATIC_REG1_WR_BURST_MAX_SIZE(config->des.max_burst) | + PROV_DMA_CTRL_CH0_STATIC_REG1_WR_TOKENS(config->des.tokens) | + (config->des.enable_inc_addr << PROV_DMA_CTRL_CH0_STATIC_REG0_RD_INCR_Pos); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_STATIC_REG1.reg, regval); + /* Static register2 configuration */ + regval = (config->enable_joint_mode << PROV_DMA_CTRL_CH0_STATIC_REG2_JOINT_Pos) | + PROV_DMA_CTRL_CH0_STATIC_REG2_END_SWAP(config->swap); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_STATIC_REG2.reg, regval); + /* Static register4 configuration */ + regval = PROV_DMA_CTRL_CH0_STATIC_REG4_RD_PERIPH_NUM(config->src.periph) | + PROV_DMA_CTRL_CH0_STATIC_REG4_RD_PERIPH_DELAY(config->src.periph_delay) | + PROV_DMA_CTRL_CH0_STATIC_REG4_WR_PERIPH_NUM(config->des.periph) | + PROV_DMA_CTRL_CH0_STATIC_REG4_WR_PERIPH_DELAY(config->des.periph_delay); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_STATIC_REG4.reg, regval); + /* Priority channels configuration */ + regval = PROV_DMA_CTRL_CORE_PRIORITY_RD_PRIO_TOP_NUM(config->src.proi_top_index) | + (PROV_DMA_CTRL_CORE_PRIORITY_RD_PRIO_TOP << config->src.enable_proi_top) | + PROV_DMA_CTRL_CORE_PRIORITY_RD_PRIO_HIGH_NUM(config->src.proi_high_index) | + (PROV_DMA_CTRL_CORE_PRIORITY_RD_PRIO_HIGH << config->src.enable_proi_high) | + PROV_DMA_CTRL_CORE_PRIORITY_WR_PRIO_TOP_NUM(config->des.proi_top_index) | + (PROV_DMA_CTRL_CORE_PRIORITY_WR_PRIO_TOP << config->des.enable_proi_top) | + PROV_DMA_CTRL_CORE_PRIORITY_WR_PRIO_HIGH_NUM(config->des.proi_high_index) | + (PROV_DMA_CTRL_CORE_PRIORITY_WR_PRIO_HIGH << config->des.enable_proi_high); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CORE_PRIORITY.reg, regval); + /* Initial the global variety */ + for (int i = 0; i < DMA_CALLBACK_N; i++) { + resource->callback[i] = NULL; + } + resource->callback_enable = 0; +} + +/** + * \brief Free an allocated DMA resource. + * + * This function will free an allocated DMA resource. + * + * \param[in,out] resource Pointer to the DMA resource + * + * \return Status of the free procedure. + * + * \retval STATUS_OK The DMA resource was freed successfully + * \retval STATUS_BUSY The DMA resource was busy and can't be freed + * \retval STATUS_ERR_NOT_INITIALIZED DMA resource was not initialized + */ +enum status_code dma_free(struct dma_resource *resource) +{ + /* Check if channel is busy */ + if (dma_get_job_status(resource) == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Check if DMA resource was not allocated */ + if (!(_dma_inst.allocated_channels & (1 << resource->channel_id))) { + return STATUS_ERR_NOT_INITIALIZED; + } + + /* Release the DMA resource */ + _dma_release_channel(resource->channel_id); + + /* Reset the item in the DMA resource pool */ + _dma_active_resource[resource->channel_id] = NULL; + + return STATUS_OK; +} + +/** + * \brief Add a DMA transfer descriptor to a DMA resource. + * + * This function will add a DMA transfer descriptor to a DMA resource. + * If there was a transfer descriptor already allocated to the DMA resource, + * the descriptor will be linked to the next descriptor address. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] descriptor Pointer to the transfer descriptor + * + * \retval STATUS_OK The descriptor is added to the DMA resource + * \retval STATUS_BUSY The DMA resource was busy and the descriptor is not added + */ +enum status_code dma_add_descriptor(struct dma_resource *resource, + struct dma_descriptor *descriptor) +{ + struct dma_descriptor *desc = resource->descriptor; + + /* Check if channel is busy */ + if (dma_get_job_status(resource) == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Look up for an empty space for the descriptor */ + if (desc == NULL) { + resource->descriptor = descriptor; + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CMD_REG0.reg, descriptor->read_start_addr); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CMD_REG1.reg, descriptor->write_start_addr); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CMD_REG2.reg, descriptor->buffer_size); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CMD_REG3.reg, 3); + } else { + /* Looking for end of descriptor link */ + while(((uint32_t)desc->cmd.next_addr) != 0) { + desc = (struct dma_descriptor*)((uint32_t)desc->next); + } + if (resource->descriptor->cmd.next_addr == 0x0) { + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CMD_REG3.reg, ((uint32_t)descriptor & (~0x3))); + } + /* Set to the end of descriptor list */ + desc->next = (uint32_t)descriptor; + /* The end of list should point to 0 */ + if (descriptor->cmd.next_addr != 0) { + /* Enable transferred interrupt, and channel stops when buffer done */ + descriptor->next = 0x3; + } + } + + return STATUS_OK; +} + +/** + * \brief Start a DMA transfer. + * + * This function will start a DMA transfer through an allocated DMA resource. + * + * \param[in,out] resource Pointer to the DMA resource + * + * \return Status of the transfer start procedure. + * + * \retval STATUS_OK The transfer was started successfully + * \retval STATUS_BUSY The DMA resource was busy and the transfer was not started + * \retval STATUS_ERR_INVALID_ARG Transfer size is 0 and transfer was not started + */ +enum status_code dma_start_transfer_job(struct dma_resource *resource) +{ + volatile uint32_t regval; + + /* Check if resource was busy */ + if (resource->job_status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Check if transfer size is valid */ + if (resource->descriptor->buffer_size == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Clear the interrupt flag */ + regval = get_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_STATUS_REG.reg); + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_CLEAR_REG.reg, regval); + /* Set the interrupt flag */ + regval = PROV_DMA_CTRL_CH0_INT_ENABLE_REG_MASK & resource->callback_enable; + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_ENABLE_REG.reg, regval); + /* Set job status */ + resource->job_status = STATUS_BUSY; + + /* Enable the transfer channel */ + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CH_ENABLE_REG.reg, 1); + /* Start the transfer channel */ + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_CH_START_REG.reg, 1); + + return STATUS_OK; +} +/** + * \brief Get the channel index + * + * \param[in] channel Channel active + * + */ +static uint8_t get_channel_index(uint8_t channel) +{ + uint8_t index = 0; + + channel = channel & 0x0f; + do { + channel = channel >> 1; + index++; + } while (channel); + + return (index - 1); +} + +/** + * \brief DMA interrupt service routine. + * + */ +static void dma_isr_handler( void ) +{ + uint8_t active_channel; + static uint8_t channel_index; // + struct dma_resource *resource; + uint8_t isr; + uint8_t isr_flag = 0; + + /* Get active channel */ + active_channel = PROV_DMA_CTRL0->CORE_INT_STATUS.reg & + PROV_DMA_CTRL_CORE_INT_STATUS_CHANNEL__Msk; + + do { + channel_index = get_channel_index(active_channel); + /* Get active DMA resource based on channel */ + resource = _dma_active_resource[channel_index]; + isr = get_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_STATUS_REG.reg); + /* Calculate block transfer size of the DMA transfer */ + resource->transfered_size = get_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_COUNT_REG.reg); + + /* DMA channel interrupt handler */ + if (isr & (1 << DMA_CALLBACK_TRANSFER_DONE)) { + /* Transfer complete flag */ + isr_flag = DMA_CALLBACK_TRANSFER_DONE; + /* Set job status */ + resource->job_status = STATUS_OK; + } else if (isr & (1 << DMA_CALLBACK_READ_ERR)) { + /* Read error flag */ + isr_flag = DMA_CALLBACK_READ_ERR; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_WRITE_ERR)) { + /* Write error flag */ + isr_flag = DMA_CALLBACK_WRITE_ERR; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_FIFO_OVERFLOW)) { + /* Overflow flag */ + isr_flag = DMA_CALLBACK_FIFO_OVERFLOW; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_FIFO_UNDERFLOW)) { + /* Underflow flag */ + isr_flag = DMA_CALLBACK_FIFO_UNDERFLOW; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_READ_TIMEOUT)) { + /* Read timeout flag */ + isr_flag = DMA_CALLBACK_READ_TIMEOUT; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_WRITE_TIMEOUT)) { + /* Write timeout flag */ + isr_flag = DMA_CALLBACK_WRITE_TIMEOUT; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } else if (isr & (1 << DMA_CALLBACK_WDT_TRIGGER)) { + /* Watchdog error flag */ + isr_flag = DMA_CALLBACK_WDT_TRIGGER; + /* Set I/O ERROR status */ + resource->job_status = STATUS_ERR_IO; + } + + if (isr) { + /* Clear the watch dog error flag */ + set_channel_reg_val(resource->channel_id, (uint32_t)&PROV_DMA_CTRL0->CH0_INT_CLEAR_REG.reg, 1<callback_enable & (1<callback[isr_flag])) { + resource->callback[isr_flag](resource); + } + } + isr &= ~(1<IRQ_MUX_IO_SEL_3.bit.MUX_15 = LPMCU_MISC_REGS_IRQ_MUX_IO_SEL_3_MUX_15_16_Val; + system_register_isr(31, (uint32_t)dma_isr_handler); + + _dma_inst._dma_init = true; + } + + new_channel = _dma_find_first_free_channel_and_allocate(); + /* If no channel available, return not found */ + if (new_channel == DMA_INVALID_CHANNEL) { + return STATUS_ERR_NOT_FOUND; + } + + /* Set the channel */ + resource->channel_id = new_channel; + /* Configure the DMA control,channel registers and descriptors here */ + _dma_set_config(resource, config); + + resource->descriptor = NULL; + + /* Log the DMA resource into the internal DMA resource pool */ + _dma_active_resource[resource->channel_id] = resource; + + return STATUS_OK; +} \ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h b/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h new file mode 100644 index 000000000..ae3ecede8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h @@ -0,0 +1,650 @@ +/** + * \file + * + * \brief Direct Memory Access Controller Driver for SAMB + * + * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DMA_H_INCLUDED +#define DMA_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup asfdoc_samb_dma_group SAM Direct Memory Access Controller Driver (DMAC) + * + * This driver for Atmel® | SMART SAM devices provides an interface for the configuration + * and management of the Direct Memory Access Controller(DMAC) module within + * the device. The DMAC can transfer data between memories and peripherals, and + * thus off-load these tasks from the CPU. The module supports peripheral to + * peripheral, peripheral to memory, memory to peripheral, and memory to memory + * transfers. + * + * The following peripherals are used by the DMAC Driver: + * - DMAC (Direct Memory Access Controller) + * + * The following devices can use this module: + * - Atmel | SMART SAM B11 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_samb_dma_prerequisites + * - \ref asfdoc_samb_dma_module_overview + * - \ref asfdoc_samb_dma_special_considerations + * - \ref asfdoc_samb_dma_extra_info + * - \ref asfdoc_samb_dma_examples + * - \ref asfdoc_samb_dma_api_overview + * + * + * \section asfdoc_samb_dma_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * + * \section asfdoc_samb_dma_module_overview Module Overview + * + * SAM devices with DMAC enables high data transfer rates with minimum + * CPU intervention and frees up CPU time. With access to all peripherals, + * the DMAC can handle automatic transfer of data to/from modules. + * It supports static and incremental addressing for both source and + * destination. + * + * The DMAC when used with peripheral triggers, provides a + * considerable advantage by reducing the power consumption and performing + * data transfer in the background. + * The CPU can remain in sleep during this time to reduce power consumption. + * + * + * + * + * + * + * + * + * + * + *
DeviceDma channel number
SAMB114
+ * The DMA channel operation can be suspended at any time by software, + * or after selectable descriptor execution. The DMAC driver for SAM + * supports four types of transfers such as peripheral to peripheral, + * peripheral to memory, memory to peripheral, and memory to memory. + * + * The basic transfer unit is a beat which is defined as a single bus access. + * There can be multiple beats in a single block transfer and multiple block + * transfers in a DMA transaction. + * DMA transfer is based on descriptors, which holds transfer properties + * such as the source and destination addresses, transfer counter, and other + * additional transfer control information. + * The descriptors can be static or linked. When static, a single block transfer + * is performed. When linked, a number of transfer descriptors can be used to + * enable multiple block transfers within a single DMA transaction. + * + * The implementation of the DMA driver is based on the idea that DMA channel + * is a finite resource of entities with the same abilities. A DMA channel resource + * is able to move a defined set of data from a source address to destination + * address triggered by a transfer trigger. On the SAM devices there are 12 + * DMA resources available for allocation. Each of these DMA resources can trigger + * interrupt callback routines. + * The other main features are: + * + * - Selectable transfer trigger source + * - Software + * - Peripheral + * - Tree level channel priority + * - Normal level + * - High level + * - Top level + * - Optional interrupt generation on transfer complete, channel error + * - Supports multi-buffer or circular buffer mode by linking multiple descriptors + * - Beat size configurable as 8-bit, 16-bit, or 32-bit + * + * A simplified block diagram of the DMA Resource can be seen in + * \ref asfdoc_samb_dma_module_block_diagram "the figure below". + * + * \anchor asfdoc_samb_dma_module_block_diagram + * \dot + * digraph overview { + * splines = false; + * rankdir=LR; + * + * mux1 [label="Transfer Trigger", shape=box]; + * + * dma [label="DMA Channel", shape=polygon, sides=6, orientation=60, style=filled, fillcolor=darkolivegreen1, height=1, width=1]; + * descriptor [label="Transfer Descriptor", shape=box, style=filled, fillcolor=lightblue]; + * + * mux1 -> dma; + * descriptor -> dma; + * + * interrupt [label="Interrupt", shape=box]; + * events [label="Events", shape=box]; + * + * dma:e -> interrupt:w; + * dma:e -> events:w; + * + * {rank=same; descriptor dma} + * + * } + * \enddot + * + * \subsection asfdoc_samb_dma_module_overview_dma_channels DMA Channels + * The DMAC in each device consists of several DMA channels, which + * along with the transfer descriptors defines the data transfer properties. + * - The transfer control descriptor defines the source and destination + * addresses, source and destination address increment settings, the + * block transfer count + * - Dedicated channel registers control the peripheral trigger source, + * trigger mode settings, and channel priority level settings + * + * With a successful DMA resource allocation, a dedicated + * DMA channel will be assigned. The channel will be occupied until the + * DMA resource is freed. A DMA resource handle is used to identify the specific + * DMA resource. + * When there are multiple channels with active requests, the arbiter prioritizes + * the channels requesting access to the bus. + * + * \subsection asfdoc_samb_dma_module_overview_dma_trigger DMA Triggers + * DMA transfer can be started only when a DMA transfer request is acknowledged/granted by the arbiter. A + * transfer request can be triggered from software, peripheral. There + * are dedicated source trigger selections for each DMA channel usage. + * + * \subsection asfdoc_samb_dma_module_overview_dma_transfer_descriptor DMA Transfer Descriptor + * The transfer descriptor resides in the SRAM and + * defines these channel properties. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Field nameField width
Source Address32 bits
Destination Address32 bits
Block Transfer Counter32 bits
Descriptor Next Address30 bits
Block Transfer Interrupt1 bit
Block Transfer Stop Control1 bit
+ * + * Before starting a transfer, at least one descriptor should be configured. + * After a successful allocation of a DMA channel, the transfer descriptor can + * be added with a call to \ref dma_add_descriptor(). If there is a transfer + * descriptor already allocated to the DMA resource, the descriptor will + * be linked to the next descriptor address. + * + * \subsection asfdoc_samb_dma_module_overview_dma_output DMA Interrupts + * Both an interrupt callback and an peripheral can be triggered by the + * DMA transfer. Three types of callbacks are supported by the DMA driver: + * transfer complete, channel suspend, and transfer error. Each of these callback + * types can be registered and enabled for each channel independently through + * the DMA driver API. + * + * + * \section asfdoc_samb_dma_special_considerations Special Considerations + * + * There are no special considerations for this module. + * + * + * \section asfdoc_samb_dma_extra_info Extra Information + * + * For extra information, see \ref asfdoc_samb_dma_extra. This includes: + * - \ref asfdoc_samb_dma_extra_acronyms + * - \ref asfdoc_samb_dma_extra_dependencies + * - \ref asfdoc_samb_dma_extra_errata + * - \ref asfdoc_samb_dma_extra_history + * + * + * \section asfdoc_samb_dma_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_samb_dma_exqsg. + * + * + * \section asfdoc_samb_dma_api_overview API Overview + * @{ + */ + +#include +#include +#include "conf_dma.h" + +/** DMA IRQn number. */ +#define PROV_DMA_CTRL0_IRQn 15 + +/** DMA invalid channel number. */ +#define DMA_INVALID_CHANNEL 0xff + +/** DMA peripheral index */ +enum dma_peripheral_index { + MEMORY_DMA_PERIPHERAL = 0, + UART0RX_DMA_PERIPHERAL, + UART0TX_DMA_PERIPHERAL, + UART1RX_DMA_PERIPHERAL, + UART1TX_DMA_PERIPHERAL, + SPI0RX_DMA_PERIPHERAL, + SPI0TX_DMA_PERIPHERAL, + SPI1RX_DMA_PERIPHERAL, + SPI1TX_DMA_PERIPHERAL, + I2C0RX_DMA_PERIPHERAL, + I2C0TX_DMA_PERIPHERAL, + I2C1RX_DMA_PERIPHERAL, + I2C1TX_DMA_PERIPHERAL, + DUALTIMER0_DMA_PERIPHERAL = 15, + TIMER0_DMA_PERIPHERAL, +}; + +/** DMA channel index */ +enum dma_ch_index { + /** DMA channel 0 */ + DMA_CHANNEL_0 = 0, + /** DMA channel 1 */ + DMA_CHANNEL_1, + /** DMA channel 2 */ + DMA_CHANNEL_2, + /** DMA channel 3 */ + DMA_CHANNEL_3, +}; + +enum dma_endian_swap { + /** DMA endian no swap */ + DMA_ENDIAN_NO_SWAP, + /** DMA endian 16-bit */ + DMA_ENDIAN_SIZE_16, + /** DMA endian 32-bit */ + DMA_ENDIAN_SIZE_32, + /** DMA endian 64-bit */ + DMA_ENDIAN_SIZE_64, +}; + +/** + * Callback types for DMA callback driver. + */ +enum dma_callback_type { + /** Callback for transfer complete */ + DMA_CALLBACK_TRANSFER_DONE, + /** AHB read slave error */ + DMA_CALLBACK_READ_ERR, + /** AHB write slave error */ + DMA_CALLBACK_WRITE_ERR, + /** FIFO has been overflown */ + DMA_CALLBACK_FIFO_OVERFLOW, + /** FIFO has been underflows */ + DMA_CALLBACK_FIFO_UNDERFLOW, + /** Read timeout on AHB bus (timeout value fixed at 1024 cycles) */ + DMA_CALLBACK_READ_TIMEOUT, + /** Write timeout on AHB bus (timeout value fixed at 1024 cycles) */ + DMA_CALLBACK_WRITE_TIMEOUT, + /** Channel active but did not start a burst for 2048 cycles */ + DMA_CALLBACK_WDT_TRIGGER, + /** Number of available callbacks */ + DMA_CALLBACK_N, +}; + +/** + * DMA transfer descriptor configuration. When the source or destination address + * increment is enabled, the addresses stored into the configuration structure + * must correspond to the end of the transfer. + */ +struct dma_descriptor { + /** Start address of read buffer */ + uint32_t read_start_addr; + /** Start address of write buffer */ + uint32_t write_start_addr; + /** Size (in bytes) of buffer to transfer */ + uint32_t buffer_size; + union { + struct { + /** Active high interrupt enable once buffer has been transferred */ + uint32_t set_interrupt:1; + /** If set, channel stops when buffer done, otherwise load from cmd_next_addr */ + uint32_t last:1; + /** Address of next command if cmd_last is not set */ + uint32_t next_addr:30; + } cmd; + uint32_t next; + }; +}; +/** Structure for DMA source/description */ +struct dma_config { + /** Maximum number of bytes of an AHB read/write burst */ + uint8_t max_burst; + /** Number of AHB read/write commands to issue before channel is released */ + uint8_t tokens; + /** If true, the controller will increment the next burst address */ + bool enable_inc_addr; + /** Index of peripheral to read/write from (0 if memory or no peripheral flow control) */ + enum dma_peripheral_index periph; + /** + * Number of cycles to wait for read/write request signal to update + * after issuing the read/write clear signal + */ + uint8_t periph_delay; + /** Top priority enable */ + bool enable_proi_top; + /** Top priority channel index */ + uint8_t proi_top_index; + /** High priority enable */ + bool enable_proi_high; + /** High priority channel index */ + uint8_t proi_high_index; +}; + +/** Structure for DMA transfer resource */ +struct dma_resource_config { + struct dma_config src; + struct dma_config des; + /** If true, channel will work in joint mode */ + bool enable_joint_mode; + /** Endian Byte Swapping */ + enum dma_endian_swap swap; +}; + +/** Forward definition of the DMA resource */ +struct dma_resource; +/** Type definition for a DMA resource callback function */ +typedef void (*dma_callback_t)(struct dma_resource *const resource); + +/** Structure for DMA transfer resource */ +struct dma_resource { + /** Allocated DMA channel ID */ + uint8_t channel_id; + /** Array of callback functions for DMA transfer job */ + dma_callback_t callback[DMA_CALLBACK_N]; + /** Bit mask for enabled callbacks */ + uint8_t callback_enable; + /** Status of the last job */ + volatile enum status_code job_status; + /** Transferred data size */ + uint32_t transfered_size; + /** DMA transfer descriptor */ + struct dma_descriptor* descriptor; +}; + +/** + * \brief Get DMA resource status. + * + * \param[in] resource Pointer to the DMA resource + * + * \return Status of the DMA resource. + */ +static inline enum status_code dma_get_job_status(struct dma_resource *resource) +{ + return resource->job_status; +} + +/** + * \brief Enable a callback function for a dedicated DMA resource. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_enable_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + resource->callback_enable |= 1 << type; +} + +/** + * \brief Disable a callback function for a dedicated DMA resource. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_disable_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + resource->callback_enable &= ~(1 << type); +} + +/** + * \brief Register a callback function for a dedicated DMA resource. + * + * There are three types of callback functions, which can be registered: + * - Callback for transfer complete + * - Callback for transfer error + * - Callback for channel suspend + * + * \param[in] resource Pointer to the DMA resource + * \param[in] callback Pointer to the callback function + * \param[in] type Callback function type + * + */ +static inline void dma_register_callback(struct dma_resource *resource, + dma_callback_t callback, enum dma_callback_type type) +{ + resource->callback[type] = callback; +} + +/** + * \brief Unregister a callback function for a dedicated DMA resource. + * + * There are three types of callback functions: + * - Callback for transfer complete + * - Callback for transfer error + * - Callback for channel suspend + * + * The application can unregister any of the callback functions which + * are already registered and are no longer needed. + * + * \param[in] resource Pointer to the DMA resource + * \param[in] type Callback function type + * + */ +static inline void dma_unregister_callback(struct dma_resource *resource, + enum dma_callback_type type) +{ + resource->callback[type] = NULL; +} + +/** + * \brief Initializes DMA transfer configuration with predefined default values. + * + * This function will initialize a given DMA descriptor configuration structure to + * a set of known default values. This function should be called on + * any new instance of the configuration structure before being + * modified by the user application. + * + * The default configuration is as follows: + * \li Set the read start address as 0 + * \li Set the write start address as 0 + * \li Set buffer size as 1 + * \li Set beat size as byte + * \li Enable the interrupt + * \li Enable the channel stops when buffer done + * \li Set next command address to 0 + * \param[out] config Pointer to the configuration + * + */ +static inline void dma_descriptor_get_config_defaults(struct dma_descriptor *config) +{ + /* Default read buffer size is set to 0 */ + config->read_start_addr = 0; + /* Default write buffer size is set to 0 */ + config->write_start_addr = 0; + /* Set beat size to one byte */ + config->buffer_size = 1; + /* Enable transferred interrupt */ + config->cmd.set_interrupt = 1; + /* Channel stops when buffer done */ + config->cmd.last = 1; + /* Set next command to 0 */ + config->cmd.next_addr = 0; +} + +/** + * \brief Update DMA descriptor. + * + * This function can update the descriptor of an allocated DMA resource. + * + */ +static inline void dma_update_descriptor(struct dma_resource *resource, + struct dma_descriptor* descriptor) +{ + resource->descriptor = descriptor; +} + +/** + * \brief Reset DMA descriptor. + * + * This function will clear the DESCADDR register of an allocated DMA resource. + * + */ +static inline void dma_reset_descriptor(struct dma_resource *resource) +{ + resource->descriptor = NULL; +} + +void dma_get_config_defaults(struct dma_resource_config *config); +enum status_code dma_allocate(struct dma_resource *resource, + struct dma_resource_config *config); +enum status_code dma_add_descriptor(struct dma_resource *resource, + struct dma_descriptor* descriptor); +enum status_code dma_start_transfer_job(struct dma_resource *resource); +enum status_code dma_free(struct dma_resource *resource); +uint8_t dma_get_status(uint8_t channel); +uint8_t dma_get_interrupt_status(uint8_t channel); +void dma_clear_interrupt_status(uint8_t channel, uint8_t flag); +/** @} */ + +/** + * \page asfdoc_samb_dma_extra Extra Information for DMAC Driver + * + * \section asfdoc_samb_dma_extra_acronyms Acronyms + * Below is a table listing the acronyms used in this module, along with their + * intended meanings. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
DMADirect Memory Access
DMACDirect Memory Access Controller
CPUCentral Processing Unit
+ * + * + * \section asfdoc_samb_dma_extra_dependencies Dependencies + * There are no dependencies related to this driver. + * + * + * \section asfdoc_samb_dma_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_samb_dma_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + *
Changelog
Initial Release
+ */ + + /** + * \page asfdoc_samb_dma_exqsg Examples for DMAC Driver + * + * This is a list of the available Quick Start Guides (QSGs) and example + * applications for \ref asfdoc_samb_dma_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that QSGs can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_samb_dma_basic_use_case + * + * \note More DMA usage examples are available in peripheral QSGs. + * + * \page asfdoc_samb_dma_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
A09/2015Initial release
+ */ + +#ifdef __cplusplus +} +#endif + +#endif /* DMA_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/dma/module_config/conf_dma.h b/atmel-samd/asf/sam0/drivers/dma/module_config/conf_dma.h new file mode 100644 index 000000000..fff0a2c81 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dma/module_config/conf_dma.h @@ -0,0 +1,51 @@ +/** + * \file + * + * \brief SAM Direct Memory Access Driver Configuration Header + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef CONF_DMA_H_INCLUDED +#define CONF_DMA_H_INCLUDED + +# define CONF_MAX_USED_CHANNEL_NUM 5 + +#endif diff --git a/atmel-samd/asf/sam0/drivers/events/events.h b/atmel-samd/asf/sam0/drivers/events/events.h new file mode 100644 index 000000000..318b89a4c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/events/events.h @@ -0,0 +1,729 @@ +/** + * \file + * + * \brief SAM Event System Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef EVENTS_H_INCLUDED +#define EVENTS_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup asfdoc_sam0_events_group SAM Event System (EVENTS) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the device's peripheral + * event resources and users within the device, including enabling and disabling + * of peripheral source selection and synchronization of clock domains between + * various modules. The following API modes is covered by this manual: + * - Polled API + * \if EVENTS_INTERRUPT_HOOK_MODE + * - Interrupt hook API + * \endif + * + * The following peripheral is used by this module: + * - EVSYS (Event System Management) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * - Atmel | SMART SAM R30 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_events_prerequisites + * - \ref asfdoc_sam0_events_module_overview + * - \ref asfdoc_sam0_events_special_considerations + * - \ref asfdoc_sam0_events_extra_info + * - \ref asfdoc_sam0_events_examples + * - \ref asfdoc_sam0_events_api_overview + * + * + * \section asfdoc_sam0_events_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * + * \section asfdoc_sam0_events_module_overview Module Overview + * + * Peripherals within the SAM devices are capable of generating two types of + * actions in response to given stimulus; set a register flag for later + * intervention by the CPU (using interrupt or polling methods), or generate + * event signals, which can be internally routed directly to other + * peripherals within the device. The use of events allows for direct actions + * to be performed in one peripheral in response to a stimulus in another + * without CPU intervention. This can lower the overall power consumption of the + * system if the CPU is able to remain in sleep modes for longer periods + * (SleepWalking), and lowers the latency of the system response. + * + * The event system is comprised of a number of freely configurable Event + * resources, plus a number of fixed Event Users. Each Event resource can be + * configured to select the input peripheral that will generate the events + * signal, as well as the synchronization path and edge detection mode. + * The fixed-function Event Users, connected to peripherals within the device, + * can then subscribe to an Event resource in a one-to-many relationship in order + * to receive events as they are generated. An overview of the event system + * chain is shown in + * \ref asfdoc_sam0_events_module_overview_fig "the figure below". + * + * \anchor asfdoc_sam0_events_module_overview_fig + * \dot + * digraph overview { + * rankdir=LR; + * node [label="Source\nPeripheral\n(Generator)" shape=ellipse style=filled fillcolor=lightgray] src_peripheral; + * node [label="Event\nResource A" shape=square style=""] event_gen0; + * node [label="Event\nUser X" shape=square style=""] event_user0; + * node [label="Event\nUser Y" shape=square style=""] event_user1; + * node [label="Destination\nPeripheral\n(User)" shape=ellipse style=filled fillcolor=lightgray] dst_peripheral0; + * node [label="Destination\nPeripheral\n(User)" shape=ellipse style=filled fillcolor=lightgray] dst_peripheral1; + * + * src_peripheral -> event_gen0; + * event_gen0 -> event_user0; + * event_gen0 -> event_user1; + * event_user0 -> dst_peripheral0; + * event_user1 -> dst_peripheral1; + * } + * \enddot + * + * There are many different events that can be routed in the device, which can + * then trigger many different actions. For example, an Analog Comparator module + * could be configured to generate an event when the input signal rises above + * the compare threshold, which then triggers a Timer Counter module to capture + * the current count value for later use. + * + * \subsection asfdoc_sam0_events_module_overview_event_channels Event Channels + * The Event module in each device consists of several channels, which can be + * freely linked to an event generator (i.e. a peripheral within the device + * that is capable of generating events). Each channel can be individually + * configured to select the generator peripheral, signal path, and edge detection + * applied to the input event signal, before being passed to any event user(s). + * + * Event channels can support multiple users within the device in a standardized + * manner. When an Event User is linked to an Event Channel, the channel will + * automatically handshake with all attached users to ensure that all modules + * correctly receive and acknowledge the event. + * + * \subsection asfdoc_sam0_events_module_overview_event_users Event Users + * Event Users are able to subscribe to an Event Channel, once it has been + * configured. Each Event User consists of a fixed connection to one of the + * peripherals within the device (for example, an ADC module, or Timer module) + * and is capable of being connected to a single Event Channel. + * + * \subsection asfdoc_sam0_events_module_overview_edge_detection Edge Detection + * For asynchronous events, edge detection on the event input is not possible, + * and the event signal must be passed directly between the event generator and + * event user. For synchronous and re-synchronous events, the input signal from + * the event generator must pass through an edge detection unit, so that only + * the rising, falling, or both edges of the event signal triggers an action in + * the event user. + * + * \subsection asfdoc_sam0_events_module_overview_path_selection Path Selection + * The event system in the SAM devices supports three signal path types from + * the event generator to Event Users: asynchronous, synchronous, and + * re-synchronous events. + * + * \subsubsection asfdoc_sam0_events_module_overview_path_selection_async Asynchronous Paths + * Asynchronous event paths allow for an asynchronous connection between the + * event generator and Event Users, when the source and destination + * peripherals share the same \ref asfdoc_sam0_system_clock_group "Generic Clock" + * channel. In this mode the event is propagated between the source and + * destination directly to reduce the event latency, thus no edge detection is + * possible. The asynchronous event chain is shown in + * \ref asfdoc_sam0_events_module_async_path_fig "the figure below". + * + * \anchor asfdoc_sam0_events_module_async_path_fig + * \dot + * digraph overview { + * rankdir=LR; + * node [label="Source\nPeripheral" shape=ellipse style=filled fillcolor=lightgray] src_peripheral; + * node [label=" EVSYS | Event\nChannel/User" fillcolor=white style="dashed" shape=record] events_chan; + * node [label="Destination\nPeripheral" shape=ellipse style=filled fillcolor=lightgray] dst_peripheral; + * + * src_peripheral -> events_chan; + * events_chan -> dst_peripheral; + * + * } + * \enddot + * \note Identically shaped borders in the diagram indicate a shared generic clock channel. + * + * \subsubsection asfdoc_sam0_events_module_overview_path_selection_sync Synchronous Paths + * The Synchronous event path should be used when edge detection or interrupts + * from the event channel are required, and the source event generator and the + * event channel shares the same Generic Clock channel. The synchronous event + * chain is shown in + * \ref asfdoc_sam0_events_module_sync_path_fig "the figure below". + * + * Not all peripherals support Synchronous event paths; refer to the device datasheet. + * + * \anchor asfdoc_sam0_events_module_sync_path_fig + * \dot + * digraph overview { + * rankdir=LR; + * node [label="Source\nPeripheral" shape=ellipse style="filled, dashed" fillcolor=lightgray] src_peripheral; + * node [label=" EVSYS | Event\nChannel/User" fillcolor=white shape=record style="dashed"] events_chan; + * node [label="Destination\nPeripheral" shape=ellipse style="filled, solid" fillcolor=lightgray] dst_peripheral; + * + * src_peripheral -> events_chan; + * events_chan -> dst_peripheral; + * + * } + * \enddot + * \note Identically shaped borders in the diagram indicate a shared generic clock channel. + * + * \subsubsection asfdoc_sam0_events_module_overview_path_selection_resync Re-synchronous Paths + * Re-synchronous event paths are a special form of synchronous events, where + * when edge detection or interrupts from the event channel are required, but + * the event generator and the event channel use different Generic Clock + * channels. The re-synchronous path allows the Event System to synchronize the + * incoming event signal from the Event Generator to the clock of the Event + * System module to avoid missed events, at the cost of a higher latency due to + * the re-synchronization process. The re-synchronous event chain is shown in + * \ref asfdoc_sam0_events_module_resync_path_fig "the figure below". + * + * Not all peripherals support re-synchronous event paths; refer to the device datasheet. + * \anchor asfdoc_sam0_events_module_resync_path_fig + * \dot + * digraph overview { + * rankdir=LR; + * node [label="Source\nPeripheral" shape=ellipse style="filled, dotted" fillcolor=lightgray] src_peripheral; + * node [label=" EVSYS | Event\nChannel/User" fillcolor=white shape=record style="dashed"] events_chan; + * node [label="Destination\nPeripheral" shape=ellipse style=filled fillcolor=lightgray] dst_peripheral; + * + * src_peripheral -> events_chan; + * events_chan -> dst_peripheral; + * + * } + * \enddot + * \note Identically shaped borders in the diagram indicate a shared generic clock channel. + * + * \subsection asfdoc_sam0_events_module_overview_physical Physical Connection + * + * \ref asfdoc_sam0_events_module_int_connections_fig "The diagram below" + * shows how this module is interconnected within the device. + * + * \anchor asfdoc_sam0_events_module_int_connections_fig + * \dot + * digraph overview { + * rankdir=LR; + * node [label="Source\nPeripherals" shape=ellipse style=filled fillcolor=lightgray] src_peripheral; + * + * subgraph driver { + * node [label=" EVSYS | Event Channels" fillcolor=white shape=record] events_chan; + * node [label=" EVSYS | Event Users" fillcolor=white shape=record] events_user; + * } + * + * node [label="Destination\nPeripherals" shape=ellipse style=filled fillcolor=lightgray] dst_peripheral; + * + * src_peripheral -> events_chan:f1 [label="Source\nMUXs"]; + * events_chan:f1 -> events_user:f1 [label="Channel\nMUXs"]; + * events_user:f1 -> dst_peripheral; + * } + * \enddot + * + * \subsection asfdoc_sam0_events_module_overview_config Configuring Events + * For SAM devices, several steps are required to properly configure an + * event chain, so that hardware peripherals can respond to events generated by + * each other, as listed below. + * + * \subsubsection asfdoc_sam0_events_module_overview_config_src Source Peripheral + * -# The source peripheral (that will generate events) must be configured and + * enabled. + * -# The source peripheral (that will generate events) must have an output + * event enabled. + + * \subsubsection asfdoc_sam0_events_module_overview_config_evsys Event System + * -# An event system channel must be allocated and configured with the + * correct source peripheral selected as the channel's event generator. + * -# The event system user must be configured and enabled, and attached to + # event channel previously allocated. + + * \subsubsection asfdoc_sam0_events_module_overview_config_dst Destination Peripheral + * -# The destination peripheral (that will receive events) must be configured + * and enabled. + * -# The destination peripheral (that will receive events) must have an input + * event enabled. + * + * + * \section asfdoc_sam0_events_special_considerations Special Considerations + * + * There are no special considerations for this module. + * + * + * \section asfdoc_sam0_events_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_events_extra. This includes: + * - \ref asfdoc_sam0_events_extra_acronyms + * - \ref asfdoc_sam0_events_extra_dependencies + * - \ref asfdoc_sam0_events_extra_errata + * - \ref asfdoc_sam0_events_extra_history + * + * + * \section asfdoc_sam0_events_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_events_exqsg. + * + * + * \section asfdoc_sam0_events_api_overview API Overview + * @{ + */ + +#include +#include "events_common.h" + +/** + * \brief Edge detect enum. + * + * Event channel edge detect setting. + * + */ +enum events_edge_detect { + /** No event output */ + EVENTS_EDGE_DETECT_NONE, + /** Event on rising edge */ + EVENTS_EDGE_DETECT_RISING, + /** Event on falling edge */ + EVENTS_EDGE_DETECT_FALLING, + /** Event on both edges */ + EVENTS_EDGE_DETECT_BOTH, +}; + +/** + * \brief Path selection enum. + * + * Event channel path selection. + * + */ +enum events_path_selection { + /** Select the synchronous path for this event channel */ + EVENTS_PATH_SYNCHRONOUS, + /** Select the resynchronizer path for this event channel */ + EVENTS_PATH_RESYNCHRONIZED, + /** Select the asynchronous path for this event channel */ + EVENTS_PATH_ASYNCHRONOUS, +}; + +/** + * \brief Events configuration struct. + * + * This event configuration struct is used to configure each of the channels. + * + */ +struct events_config { + /** Select edge detection mode */ + enum events_edge_detect edge_detect; + /** Select events channel path */ + enum events_path_selection path; + /** Set event generator for the channel */ + uint8_t generator; + /** Clock source for the event channel */ + uint8_t clock_source; +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + /** Run in standby mode for the channel */ + bool run_in_standby; + /** Run On Demand */ + bool on_demand; +#endif +}; + +/** + * \brief No event generator definition. + * + * Use this to disable any peripheral event input to a channel. This can be useful + * if you only want to use a channel for software generated events. + * + */ + +///@cond INTERNAL +/** + * \internal + * Status bit offsets in the status register/interrupt register. + * + * @{ + */ +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) +# define _EVENTS_START_OFFSET_BUSY_BITS 16 +# define _EVENTS_START_OFFSET_USER_READY_BIT 0 +# define _EVENTS_START_OFFSET_DETECTION_BIT 16 +# define _EVENTS_START_OFFSET_OVERRUN_BIT 0 +#else /* SAM D/R */ +# define _EVENTS_START_OFFSET_BUSY_BITS 8 +# define _EVENTS_START_OFFSET_USER_READY_BIT 0 +# define _EVENTS_START_OFFSET_DETECTION_BIT 8 +# define _EVENTS_START_OFFSET_OVERRUN_BIT 0 +#endif +/** @} */ +///@endcond + +/** +* Definition for no generator selection. +*/ +#define EVSYS_ID_GEN_NONE 0 + +/** + * \brief Event channel resource. + * + * Event resource structure. + * + * \note The fields in this structure should not be altered by the user application; + * they are reserved for driver internals only. + */ +struct events_resource { +#if !defined(__DOXYGEN__) + /** Channel allocated for the event resource */ + uint8_t channel; + /** Channel setting in CHANNEL register */ + uint32_t channel_reg; +#endif +}; + +#if EVENTS_INTERRUPT_HOOKS_MODE == true +typedef void (*events_interrupt_hook)(struct events_resource *resource); + +/** + * \brief Event hook. + * + * Event hook structure. + * + */ +struct events_hook { + /** Event resource */ + struct events_resource *resource; + /** Event hook function */ + events_interrupt_hook hook_func; + /** Next event hook */ + struct events_hook *next; +}; +#endif + +/** + * \brief Initializes an event configurations struct to defaults. + * + * Initailizes an event configuration struct to predefined safe default settings. + * + * \param[in] config Pointer to an instance of \ref struct events_config + * + */ +void events_get_config_defaults(struct events_config *config); + +/** + * \brief Allocate an event channel and set configuration. + * + * Allocates an event channel from the event channel pool and sets + * the channel configuration. + * + * \param[out] resource Pointer to a \ref events_resource struct instance + * \param[in] config Pointer to a \ref events_config struct + * + * \return Status of the configuration procedure. + * \retval STATUS_OK Allocation and configuration went successful + * \retval STATUS_ERR_NOT_FOUND No free event channel found + * + */ +enum status_code events_allocate(struct events_resource *resource, struct events_config *config); + +/** + * \brief Attach user to the event channel. + * + * Attach a user peripheral to the event channel to receive events. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] user_id A number identifying the user peripheral found in the device header file + * + * \return Status of the user attach procedure. + * \retval STATUS_OK No errors detected when attaching the event user + */ +enum status_code events_attach_user(struct events_resource *resource, uint8_t user_id); + +/** + * \brief Detach a user peripheral from the event channel. + * + * Deattach a user peripheral from the event channels so it does not receive any more events. + * + * \param[in] resource Pointer to an \ref event_resource struct instance + * \param[in] user_id A number identifying the user peripheral found in the device header file + * + * \return Status of the user detach procedure. + * \retval STATUS_OK No errors detected when detaching the event user + */ +enum status_code events_detach_user(struct events_resource *resource, uint8_t user_id); + +/** + * \brief Check if a channel is busy. + * + * Check if a channel is busy, a channel stays busy until all users connected to the channel + * has handled an event. + * + * \param[in] resource Pointer to a \ref events_resource struct instance + * + * \return Status of the channels busy state. + * \retval true One or more users connected to the channel has not handled the last event + * \retval false All users are ready to handle new events + */ +bool events_is_busy(struct events_resource *resource); + +/** + * \brief Trigger software event. + * + * Trigger an event by software. + * + * \note Software event works on either a synchronous path or resynchronized path, and + * edge detection must be configured to rising-edge detection. + * + * \param[in] resource Pointer to an \ref events_resource struct + * + * \return Status of the event software procedure. + * \retval STATUS_OK No error was detected when the software tigger signal was issued + * \retval STATUS_ERR_UNSUPPORTED_DEV If the channel path is asynchronous and/or the + * edge detection is not set to RISING + */ +enum status_code events_trigger(struct events_resource *resource); + +/** + * \brief Check if all users connected to the channel are ready. + * + * Check if all users connected to the channel are ready to handle incoming events. + * + * \param[in] resource Pointer to an \ref events_resource struct + * + * \return The ready status of users connected to an event channel. + * \retval true All the users connected to the event channel are ready to handle incoming events + * \retval false One or more users connected to the event channel are not ready to handle incoming events + */ +bool events_is_users_ready(struct events_resource *resource); + +/** + * \brief Check if an event is detected on the event channel. + * + * Check if an event has been detected on the channel. + * + * \note This function will clear the event detected interrupt flag. + * + * \param[in] resource Pointer to an \ref events_resource struct + * + * \return Status of the event detection interrupt flag. + * \retval true Event has been detected + * \retval false Event has not been detected + */ +bool events_is_detected(struct events_resource *resource); + +/** + * \brief Check if there has been an overrun situation on this channel. + * + * \note This function will clear the event overrun detected interrupt flag. + * + * \param[in] resource Pointer to an \ref events_resource struct + * + * \return Status of the event overrun interrupt flag. + * \retval true Event overrun has been detected + * \retval false Event overrun has not been detected + */ +bool events_is_overrun(struct events_resource *resource); + +/** + * \brief Release allocated channel back the the resource pool. + * + * Release an allocated channel back to the resource pool to make it available for other purposes. + * + * \param[in] resource Pointer to an \ref events_resource struct + * + * \return Status of the channel release procedure. + * \retval STATUS_OK No error was detected when the channel was released + * \retval STATUS_BUSY One or more event users have not processed the last event + * \retval STATUS_ERR_NOT_INITIALIZED Channel not allocated, and can therefore not be released + */ +enum status_code events_release(struct events_resource *resource); + +/** + * \brief Get the number of free channels. + * + * Get the number of allocatable channels in the events system resource pool. + * + * \return The number of free channels in the event system. + * + */ +uint8_t events_get_free_channels(void); + + +///@cond INTERNAL +/** + * \internal + * Function to find bit position in the CHSTATUS and INTFLAG register, + * and return bit mask of this position. + * + * @{ + */ +uint32_t _events_find_bit_position(uint8_t channel, uint8_t start_offset); +/** @} */ +///@endcond + + +/** @} */ + +/** + * \page asfdoc_sam0_events_extra Extra Information for EVENTS Driver + * + * \section asfdoc_sam0_events_extra_acronyms Acronyms + * Below is a table listing the acronyms used in this module, along with their + * intended meanings. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
CPUCentral Processing Unit
MUXMultiplexer
+ * + * + * \section asfdoc_sam0_events_extra_dependencies Dependencies + * This driver has the following dependencies: + * + * - \ref asfdoc_sam0_system_clock_group "System Clock Driver" + * + * + * \section asfdoc_sam0_events_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_events_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Changelog
Fix a bug in internal function _events_find_bit_position()
Rewrite of events driver
Initial Release
+ */ + + /** + * \page asfdoc_sam0_events_exqsg Examples for EVENTS Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_events_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_events_basic_use_case + * \if EVENTS_INTERRUPT_HOOK_MODE + * - \subpage asfdoc_sam0_events_interrupt_hook_use_case + * \endif + * + * \page asfdoc_sam0_events_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
42108G12/2015Added support for SAM D09 and SAM L22
42108F08/2015Added support for SAM L21, SAM DA1, SAMR30 and SAM C20/C21
42108E12/2014Added support for interrupt hook mode. + * Added support for SAM R21 and SAM D10/D11.
42108D01/2014Update to support SAM D21 and corrected documentation typos
42108C11/2013Fixed incorrect documentation for the event signal paths. Added + * configuration steps overview to the documentation.
42108B06/2013Corrected documentation typos
42108A06/2013Initial release
+ */ + +#ifdef __cplusplus +} +#endif + +#endif /* EVENTS_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/events/events_common.h b/atmel-samd/asf/sam0/drivers/events/events_common.h new file mode 100644 index 000000000..b40db7cec --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/events/events_common.h @@ -0,0 +1,71 @@ +/* + * \file + * + * \brief SAM Event System Controller Driver + * + * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef _EVENTS_COMMON_H_INCLUDED_ +#define _EVENTS_COMMON_H_INCLUDED_ + +/** + * \internal Internal module structure to manage necessary globals + * + * + */ +struct _events_module { + /* Allocated channels bitmask where 1 means allocated */ + volatile uint32_t allocated_channels; + /* Free channels */ + uint8_t free_channels; + +#if EVENTS_INTERRUPT_HOOKS_MODE == true + /* Buffer to store a copy of the current interrupt flags */ + volatile uint32_t interrupt_flag_buffer; + /* Buffer to store acknowledged interrupt sources */ + volatile uint32_t interrupt_flag_ack_buffer; + + /* Interrup hook linked list start pointer */ + struct events_hook *hook_list; +#endif +}; + +#endif diff --git a/atmel-samd/asf/sam0/drivers/events/events_hooks.c b/atmel-samd/asf/sam0/drivers/events/events_hooks.c new file mode 100644 index 000000000..ee912b060 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/events/events_hooks.c @@ -0,0 +1,235 @@ +/* + * \file + * + * \brief SAM Event System Controller Driver + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#include "events.h" +#include "events_hooks.h" +#include "system_interrupt.h" + +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) +# define _EVENTS_INTFLAGS_DETECT 0x0fff0000 +# define _EVENTS_INTFLAGS_OVERRUN 0x00000fff +#else +# define _EVENTS_INTFLAGS_DETECT 0x0f00ff00 +# define _EVENTS_INTFLAGS_OVERRUN 0x000f00ff +#endif +#define _EVENTS_INTFLAGS_MASK (_EVENTS_INTFLAGS_DETECT | _EVENTS_INTFLAGS_OVERRUN) + +extern struct _events_module _events_inst; + +enum status_code events_create_hook(struct events_hook *hook, events_interrupt_hook func) +{ + /* Initialize the hook struct members */ + hook->next = NULL; + hook->resource = NULL; + hook->hook_func = func; + + return STATUS_OK; +} + +enum status_code events_add_hook(struct events_resource *resource, struct events_hook *hook) +{ + struct events_hook *tmp_hook = NULL; + + /* Associate the hook with the resource */ + hook->resource = resource; + + /* Check if this is the first hook in the list */ + if (_events_inst.hook_list == NULL) { + _events_inst.hook_list = hook; + } else { + tmp_hook = _events_inst.hook_list; + + /* Find the first free place in the list */ + while (tmp_hook->next != NULL) { + tmp_hook = tmp_hook->next; + } + + /* Put the hook into the next free place in the list */ + tmp_hook->next = hook; + } + + /* Check if interrupts from the EVSYS module is enabled in the interrupt controller */ + if (!system_interrupt_is_enabled(SYSTEM_INTERRUPT_MODULE_EVSYS)) { + system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_EVSYS); + } + + return STATUS_OK; +} + +enum status_code events_del_hook(struct events_resource *resource, struct events_hook *hook) +{ + struct events_hook *tmp_hook = _events_inst.hook_list; + struct events_hook *last_hook = NULL; + + if (tmp_hook != NULL) { + /* Check if the first hook in the list is the one we are looking for */ + if (tmp_hook != hook) { + /* Don't double check the first hook */ + tmp_hook = tmp_hook->next; + + /* Check if the current hook is the one we are looking for */ + while (tmp_hook != hook) { + + /* If the current hook pointer is NULL the hook is not found in the list */ + if(tmp_hook == NULL) { + return STATUS_ERR_NOT_FOUND; + } + + last_hook = tmp_hook; + tmp_hook = tmp_hook->next; + + } + /* Remove the current hook from the list */ + last_hook->next = tmp_hook->next; + } else { + _events_inst.hook_list = tmp_hook->next; + } + } else { + /* No hooks where found in the list */ + return STATUS_ERR_NO_MEMORY; + } + + return STATUS_OK; +} + +enum status_code events_enable_interrupt_source(struct events_resource *resource, enum events_interrupt_source source) +{ + Assert((source == EVENTS_INTERRUPT_DETECT) || (source == EVENTS_INTERRUPT_OVERRUN)); + + if (source == EVENTS_INTERRUPT_DETECT) { + EVSYS->INTENSET.reg = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_DETECTION_BIT); + } else if (source == EVENTS_INTERRUPT_OVERRUN) { + EVSYS->INTENSET.reg = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_OVERRUN_BIT); + } else { + return STATUS_ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +enum status_code events_disable_interrupt_source(struct events_resource *resource, enum events_interrupt_source source) +{ + Assert((source == EVENTS_INTERRUPT_DETECT) || (source == EVENTS_INTERRUPT_OVERRUN)); + + if (source == EVENTS_INTERRUPT_DETECT) { + EVSYS->INTENCLR.reg = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_DETECTION_BIT); + } else if (source == EVENTS_INTERRUPT_OVERRUN) { + EVSYS->INTENCLR.reg = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_OVERRUN_BIT); + } else { + return STATUS_ERR_INVALID_ARG; + } + + return STATUS_OK; +} + + +bool events_is_interrupt_set(struct events_resource *resource, enum events_interrupt_source source) +{ + Assert((source == EVENTS_INTERRUPT_DETECT) || (source == EVENTS_INTERRUPT_OVERRUN)); + + uint32_t bitpos; + + if (source == EVENTS_INTERRUPT_DETECT) { + bitpos = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_DETECTION_BIT); + } else if (source == EVENTS_INTERRUPT_OVERRUN) { + bitpos = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_OVERRUN_BIT); + } else { + return false; + } + + return (bool)(_events_inst.interrupt_flag_buffer & bitpos); +} + +enum status_code events_ack_interrupt(struct events_resource *resource, enum events_interrupt_source source) +{ + Assert((source == EVENTS_INTERRUPT_DETECT) || (source == EVENTS_INTERRUPT_OVERRUN)); + + uint32_t bitpos; + + if (source == EVENTS_INTERRUPT_DETECT) { + bitpos = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_DETECTION_BIT); + } else if (source == EVENTS_INTERRUPT_OVERRUN) { + bitpos = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_OVERRUN_BIT); + } else { + return STATUS_ERR_INVALID_ARG; + } + + _events_inst.interrupt_flag_ack_buffer |= bitpos; + + return STATUS_OK; +} + +void EVSYS_Handler(void) +{ + struct events_hook *current_hook = _events_inst.hook_list; + uint32_t flag; + + /* Synch the interrupt flag buffer with the hardware register */ + flag = EVSYS->INTFLAG.reg; + _events_inst.interrupt_flag_buffer |= flag; + /* Clear all hardware interrupt flags */ + EVSYS->INTFLAG.reg = _EVENTS_INTFLAGS_MASK; + + /* Traverse the linked list */ + while (current_hook != NULL) { + current_hook->hook_func(current_hook->resource); + current_hook = current_hook->next; + } + + /* Clear acknowledged interrupt sources from the interrupt flag buffer */ + flag = _events_inst.interrupt_flag_ack_buffer; + _events_inst.interrupt_flag_buffer &= ~flag; +} + + + diff --git a/atmel-samd/asf/sam0/drivers/events/events_hooks.h b/atmel-samd/asf/sam0/drivers/events/events_hooks.h new file mode 100644 index 000000000..f16e5e336 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/events/events_hooks.h @@ -0,0 +1,182 @@ +/** + * \file + * + * \brief SAM Event System Driver + * + * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include "events.h" + +#ifndef _EVENTS_HOOKS_H_INCLUDED_ +#define _EVENTS_HOOKS_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_events_group + * @{ + * + */ + +/** + * \brief Interrupt source enumerator. + * + * Interrupt source selector definitions. + * + */ +enum events_interrupt_source { + /** Overrun in event channel detected interrupt */ + EVENTS_INTERRUPT_OVERRUN, + /** Event signal propagation in event channel detected interrupt */ + EVENTS_INTERRUPT_DETECT, +}; + +/** + * \brief Initializes an interrupt hook for insertion in the event interrupt hook queue. + * + * Initializes a hook structure so it is ready for insertion in the interrupt hook queue. + * + * \param[out] hook Pointer to an \ref events_hook struct instance + * \param[in] hook_func Pointer to a function containing the interrupt hook code + * + * \return Status of the hook creation procedure. + * \retval STATUS_OK Creation and initialization of interrupt hook went successful + * + */ +enum status_code events_create_hook(struct events_hook *hook, events_interrupt_hook hook_func); + +/** + * \brief Insert hook into the event drivers interrupt hook queue. + * + * Inserts a hook into the event drivers interrupt hook queue. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] hook Pointer to an \ref events_hook struct instance + * + * \return Status of the insertion procedure. + * \retval STATUS_OK Insertion of hook went successful + * + */ +enum status_code events_add_hook(struct events_resource *resource, struct events_hook *hook); + +/** + * \brief Remove hook from the event drivers interrupt hook queue. + * + * Removes a hook from the event drivers interrupt hook queue. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] hook Pointer to an \ref events_hook struct instance + * + * \return Status of the removal procedure. + * \retval STATUS_OK Removal of hook went successful + * \retval STATUS_ERR_NO_MEMORY There are no hooks instances in the event driver interrupt hook list + * \retval STATUS_ERR_NOT_FOUND Interrupt hook not found in the event drivers interrupt hook list + * + */ +enum status_code events_del_hook(struct events_resource *resource, struct events_hook *hook); + +/** + * \brief Enable interrupt source. + * + * Enable an interrupt source so can trigger execution of an interrupt hook. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] source One of the members in the \ref events_interrupt_source enumerator + * + * \return Status of the interrupt source enable procedure. + * \retval STATUS_OK Enabling of the interrupt source was successful + * \retval STATUS_ERR_INVALID_ARG Interrupt source does not exist + * + */ +enum status_code events_enable_interrupt_source(struct events_resource *resource, enum events_interrupt_source source); + +/** + * \brief Disable interrupt source. + * + * Disable an interrupt source so can trigger execution of an interrupt hook. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] source One of the members in the \ref events_interrupt_source enumerator + * + * \return Status of the interrupt source enable procedure. + * \retval STATUS_OK Enabling of the interrupt source went successful + * \retval STATUS_ERR_INVALID_ARG Interrupt source does not exist + * + */ +enum status_code events_disable_interrupt_source(struct events_resource *resource, enum events_interrupt_source source); + +/** + * \brief Check if interrupt source is set. + * + * Check if an interrupt source is set and should be processed. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] source One of the members in the \ref events_interrupt_source enumerator + * + * \return Status of the interrupt source. + * \retval true Interrupt source is set + * \retval false Interrupt source is not set + * + */ +bool events_is_interrupt_set(struct events_resource *resource, enum events_interrupt_source source); + +/** + * \brief Acknowledge an interrupt source. + * + * Acknowledge an interrupt source so the interrupt state is cleared in hardware. + * + * \param[in] resource Pointer to an \ref events_resource struct instance + * \param[in] source One of the members in the \ref events_interrupt_source enumerator + * + * \return Status of the interrupt source. + * \retval STATUS_OK Interrupt source was acknowledged successfully + * + */ +enum status_code events_ack_interrupt(struct events_resource *resource, enum events_interrupt_source source); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/atmel-samd/asf/sam0/drivers/events/events_sam_d_r/events.c b/atmel-samd/asf/sam0/drivers/events/events_sam_d_r/events.c new file mode 100644 index 000000000..b2180b999 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/events/events_sam_d_r/events.c @@ -0,0 +1,328 @@ +/* + * \file + * + * \brief SAM Event System Controller Driver + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#include +#include +#include +#include + +#define EVENTS_INVALID_CHANNEL 0xff + +struct _events_module _events_inst = { + .allocated_channels = 0, + .free_channels = EVSYS_CHANNELS, + +#if EVENTS_INTERRUPT_HOOKS_MODE == true + .interrupt_flag_buffer = 0, + .interrupt_flag_ack_buffer = 0, + + .hook_list = NULL, +#endif +}; + +/** + * \internal + * + */ +uint32_t _events_find_bit_position(uint8_t channel, uint8_t start_offset) +{ + uint32_t pos; + + if (channel < _EVENTS_START_OFFSET_BUSY_BITS) { + pos = 0x01UL << (start_offset + channel); + } else { + pos = 0x01UL << (start_offset + channel + _EVENTS_START_OFFSET_BUSY_BITS); + } + + return pos; +} + +static uint8_t _events_find_first_free_channel_and_allocate(void) +{ + uint8_t count; + uint32_t tmp; + bool allocated = false; + + system_interrupt_enter_critical_section(); + + tmp = _events_inst.allocated_channels; + + for(count = 0; count < EVSYS_CHANNELS; ++count) { + + if(!(tmp & 0x00000001)) { + /* If free channel found, set as allocated and return number */ + + _events_inst.allocated_channels |= 1 << count; + _events_inst.free_channels--; + allocated = true; + + break; + + } + + tmp = tmp >> 1; + } + + system_interrupt_leave_critical_section(); + + if(!allocated) { + return EVENTS_INVALID_CHANNEL; + } else { + return count; + } +} + +static void _events_release_channel(uint8_t channel) +{ + system_interrupt_enter_critical_section(); + + _events_inst.allocated_channels &= ~(1 << channel); + _events_inst.free_channels++; + + system_interrupt_leave_critical_section(); +} + + +/* This function is called by the system_init function, but should not be a public API call */ +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +#endif +void _system_events_init(void) +{ + /* Enable EVSYS register interface */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_EVSYS); + + /* Make sure the EVSYS module is properly reset */ + EVSYS->CTRL.reg = EVSYS_CTRL_SWRST; + + while (EVSYS->CTRL.reg & EVSYS_CTRL_SWRST) { + } +} +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +void events_get_config_defaults(struct events_config *config) +{ + /* Check that config is something other than NULL */ + Assert(config); + + config->edge_detect = EVENTS_EDGE_DETECT_RISING; + config->path = EVENTS_PATH_SYNCHRONOUS; + config->generator = EVSYS_ID_GEN_NONE; + config->clock_source = GCLK_GENERATOR_0; +} + +enum status_code events_allocate( + struct events_resource *resource, + struct events_config *config) +{ + uint8_t new_channel; + + Assert(resource); + + new_channel = _events_find_first_free_channel_and_allocate(); + + if(new_channel == EVENTS_INVALID_CHANNEL) { + return STATUS_ERR_NOT_FOUND; + } + + resource->channel = new_channel; + + if (config->path != EVENTS_PATH_ASYNCHRONOUS) { + /* Set up a GLCK channel to use with the specific channel */ + struct system_gclk_chan_config gclk_chan_conf; + + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = + (enum gclk_generator)config->clock_source; + system_gclk_chan_set_config(EVSYS_GCLK_ID_0 + new_channel, &gclk_chan_conf); + system_gclk_chan_enable(EVSYS_GCLK_ID_0 + new_channel); + } + + /* Save channel setting and configure it after user multiplexer */ + resource->channel_reg = EVSYS_CHANNEL_CHANNEL(new_channel) | + EVSYS_CHANNEL_EVGEN(config->generator) | + EVSYS_CHANNEL_PATH(config->path) | + EVSYS_CHANNEL_EDGSEL(config->edge_detect); + + + return STATUS_OK; +} + + +enum status_code events_release(struct events_resource *resource) +{ + enum status_code err = STATUS_OK; + + Assert(resource); + + /* Check if channel is busy */ + if(events_is_busy(resource)) { + return STATUS_BUSY; + } + + if (!(_events_inst.allocated_channels & (1<channel))) { + err = STATUS_ERR_NOT_INITIALIZED; + } else { + _events_release_channel(resource->channel); + } + + return err; +} + +enum status_code events_trigger(struct events_resource *resource) +{ + + Assert(resource); + + system_interrupt_enter_critical_section(); + + /* Because of indirect access the channel must be set first */ + ((uint8_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(resource->channel); + + /* Assert if event path is asynchronous */ + if (EVSYS->CHANNEL.reg & EVSYS_CHANNEL_PATH(EVENTS_PATH_ASYNCHRONOUS)) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + /* Assert if event edge detection is not set to RISING */ + if (!(EVSYS->CHANNEL.reg & EVSYS_CHANNEL_EDGSEL(EVENTS_EDGE_DETECT_RISING))) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + + /* The GCLKREQ bit has to be set while triggering the software event */ + EVSYS->CTRL.reg = EVSYS_CTRL_GCLKREQ; + + ((uint16_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(resource->channel) | + EVSYS_CHANNEL_SWEVT; + + EVSYS->CTRL.reg &= ~EVSYS_CTRL_GCLKREQ; + + system_interrupt_leave_critical_section(); + + return STATUS_OK; +} + +bool events_is_busy(struct events_resource *resource) +{ + Assert(resource); + + return EVSYS->CHSTATUS.reg & (_events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_BUSY_BITS)); +} + +bool events_is_users_ready(struct events_resource *resource) +{ + Assert(resource); + + return EVSYS->CHSTATUS.reg & (_events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_USER_READY_BIT)); +} + +bool events_is_detected(struct events_resource *resource) +{ + Assert(resource); + + uint32_t flag = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_DETECTION_BIT); + + /* Clear flag when read */ + if (EVSYS->INTFLAG.reg & flag) { + EVSYS->INTFLAG.reg = flag; + return true; + } + + return false; +} + +bool events_is_overrun(struct events_resource *resource) +{ + Assert(resource); + + uint32_t flag = _events_find_bit_position(resource->channel, + _EVENTS_START_OFFSET_OVERRUN_BIT); + + /* Clear flag when read */ + if (EVSYS->INTFLAG.reg & flag) { + EVSYS->INTFLAG.reg = flag; + return true; + } + + return false; +} + +enum status_code events_attach_user(struct events_resource *resource, uint8_t user_id) +{ + Assert(resource); + + /* First configure user multiplexer: channel number is n + 1 */ + EVSYS->USER.reg = EVSYS_USER_CHANNEL(resource->channel + 1) | + EVSYS_USER_USER(user_id); + + /* Then configure the channel */ + EVSYS->CHANNEL.reg = resource->channel_reg; + + return STATUS_OK; +} + +enum status_code events_detach_user(struct events_resource *resource, uint8_t user_id) +{ + + Assert(resource); + + /* Write 0 to the channel bit field to select no input */ + EVSYS->USER.reg = EVSYS_USER_USER(user_id); + + return STATUS_OK; +} + +uint8_t events_get_free_channels() +{ + return _events_inst.free_channels; +} diff --git a/atmel-samd/asf_conf/conf_dma.h b/atmel-samd/asf_conf/conf_dma.h new file mode 100644 index 000000000..82e59025d --- /dev/null +++ b/atmel-samd/asf_conf/conf_dma.h @@ -0,0 +1,51 @@ +/** + * \file + * + * \brief SAM D21 Direct Memory Access Driver Configuration Header + * + * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef CONF_DMA_H_INCLUDED +#define CONF_DMA_H_INCLUDED + +# define CONF_MAX_USED_CHANNEL_NUM 2 + +#endif diff --git a/atmel-samd/boards/circuitplayground_express/conf_dma.h b/atmel-samd/boards/circuitplayground_express/conf_dma.h new file mode 100644 index 000000000..ffb71f634 --- /dev/null +++ b/atmel-samd/boards/circuitplayground_express/conf_dma.h @@ -0,0 +1,51 @@ +/** + * \file + * + * \brief SAM D21 Direct Memory Access Driver Configuration Header + * + * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef CONF_DMA_H_INCLUDED +#define CONF_DMA_H_INCLUDED + +# define CONF_MAX_USED_CHANNEL_NUM 1 + +#endif diff --git a/atmel-samd/common-hal/audioio/AudioOut.c b/atmel-samd/common-hal/audioio/AudioOut.c new file mode 100644 index 000000000..e61391e0e --- /dev/null +++ b/atmel-samd/common-hal/audioio/AudioOut.c @@ -0,0 +1,350 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "common-hal/audioio/AudioOut.h" +#include "shared-bindings/audioio/AudioOut.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "asf/sam0/drivers/dac/dac.h" +#include "asf/sam0/drivers/dma/dma.h" +#include "asf/sam0/drivers/events/events.h" +#include "asf/sam0/drivers/tc/tc.h" +#include "samd21_pins.h" +#include "shared_dma.h" + +#undef ENABLE + +// Shared with PWMOut +// TODO(tannewt): Factor these out so audioio can exist without PWMOut. +extern uint32_t target_timer_frequencies[TC_INST_NUM + TCC_INST_NUM]; +extern uint8_t timer_refcount[TC_INST_NUM + TCC_INST_NUM]; +extern const uint16_t prescaler[8]; + +// This timer is shared amongst all AudioOut objects under the assumption that +// the code is single threaded. +static struct tc_module* tc_instance; +static struct dac_module* dac_instance; +static struct events_resource* tc_event; +static struct events_resource* dac_event; + +// The AudioOut object is being currently played. Only it can pause the timer +// and change its frequency. +static audioio_audioout_obj_t* active_audioout; + +static uint8_t refcount = 0; + +void audioout_reset(void) { + // Only reset DMA. PWMOut will reset the timer. + refcount = 0; + tc_instance = NULL; + if (dac_instance != NULL) { + dac_reset(dac_instance); + } + dac_instance = NULL; + + if (tc_event != NULL) { + events_detach_user(tc_event, EVSYS_ID_USER_DAC_START); + events_release(tc_event); + } + tc_event = NULL; + + if (dac_event != NULL) { + events_detach_user(dac_event, EVSYS_ID_USER_DMAC_CH_0); + events_release(dac_event); + } + dac_event = NULL; + + dma_abort_job(&audio_dma); +} + +static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t* pin, uint32_t frequency) { + assert_pin_free(pin); + + // Configure the DAC to output on input event and to output an empty event + // that triggers the DMA to load the next sample. + dac_instance = gc_alloc(sizeof(struct dac_module), false); + if (dac_instance == NULL) { + mp_raise_msg(&mp_type_MemoryError, ""); + } + struct dac_config config_dac; + dac_get_config_defaults(&config_dac); + config_dac.left_adjust = true; + config_dac.reference = DAC_REFERENCE_AVCC; + config_dac.clock_source = GCLK_GENERATOR_0; + enum status_code status = dac_init(dac_instance, DAC, &config_dac); + if (status != STATUS_OK) { + common_hal_audioio_audioout_deinit(self); + mp_raise_OSError(MP_EIO); + return; + } + + struct dac_chan_config channel_config; + dac_chan_get_config_defaults(&channel_config); + dac_chan_set_config(dac_instance, DAC_CHANNEL_0, &channel_config); + dac_chan_enable(dac_instance, DAC_CHANNEL_0); + + struct dac_events events_dac = { .generate_event_on_buffer_empty = true, + .on_event_start_conversion = true }; + dac_enable_events(dac_instance, &events_dac); + + dac_enable(dac_instance); + + // Figure out which timer we are using. + Tc *t = NULL; + Tc *tcs[TC_INST_NUM] = TC_INSTS; + for (uint8_t i = TC_INST_NUM; i > 0; i--) { + if (tcs[i - 1]->COUNT16.CTRLA.bit.ENABLE == 0) { + t = tcs[i - 1]; + break; + } + } + if (t == NULL) { + common_hal_audioio_audioout_deinit(self); + mp_raise_RuntimeError("All timers in use"); + return; + } + tc_instance = gc_alloc(sizeof(struct tc_module), false); + if (tc_instance == NULL) { + common_hal_audioio_audioout_deinit(self); + mp_raise_msg(&mp_type_MemoryError, ""); + } + + // Don't bother setting the period. We set it before you playback anything. + struct tc_config config_tc; + tc_get_config_defaults(&config_tc); + + config_tc.counter_size = TC_COUNTER_SIZE_16BIT; + config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV1; + config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; + if (tc_init(tc_instance, t, &config_tc) != STATUS_OK) { + common_hal_audioio_audioout_deinit(self); + mp_printf(&mp_plat_print, "tc \n"); + mp_raise_OSError(MP_EIO); + return; + }; + + struct tc_events events_tc; + events_tc.generate_event_on_overflow = true; + tc_enable_events(tc_instance, &events_tc); + + tc_enable(tc_instance); + tc_stop_counter(tc_instance); + + // Connect the timer overflow event, which happens at the target frequency, + // to the DAC conversion trigger. + tc_event = gc_alloc(sizeof(struct events_resource), false); + if (tc_event == NULL) { + common_hal_audioio_audioout_deinit(self); + mp_raise_msg(&mp_type_MemoryError, ""); + } + struct events_config config; + events_get_config_defaults(&config); + + uint8_t generator = EVSYS_ID_GEN_TC3_OVF; + if (t == TC4) { + generator = EVSYS_ID_GEN_TC4_OVF; + } else if (t == TC5) { + generator = EVSYS_ID_GEN_TC5_OVF; +#ifdef TC6 + } else if (t == TC6) { + generator = EVSYS_ID_GEN_TC6_OVF; +#endif +#ifdef TC7 + } else if (t == TC7) { + generator = EVSYS_ID_GEN_TC7_OVF; +#endif + } + + config.generator = generator; + config.path = EVENTS_PATH_ASYNCHRONOUS; + if (events_allocate(tc_event, &config) != STATUS_OK || + events_attach_user(tc_event, EVSYS_ID_USER_DAC_START) != STATUS_OK) { + common_hal_audioio_audioout_deinit(self); + mp_raise_OSError(MP_EIO); + return; + } + + // Connect the DAC to DMA + dac_event = gc_alloc(sizeof(struct events_resource), false); + if (tc_event == NULL) { + common_hal_audioio_audioout_deinit(self); + mp_raise_msg(&mp_type_MemoryError, ""); + } + events_get_config_defaults(&config); + config.generator = EVSYS_ID_GEN_DAC_EMPTY; + config.path = EVENTS_PATH_ASYNCHRONOUS; + if (events_allocate(dac_event, &config) != STATUS_OK || + events_attach_user(dac_event, EVSYS_ID_USER_DMAC_CH_0) != STATUS_OK) { + common_hal_audioio_audioout_deinit(self); + mp_raise_OSError(MP_EIO); + return; + } + + // Leave the DMA setup to the specific constructor. +} + +void common_hal_audioio_audioout_construct_from_buffer(audioio_audioout_obj_t* self, + const mcu_pin_obj_t* pin, + uint16_t* buffer, + uint32_t len) { + self->pin = pin; + if (pin != &pin_PA02) { + mp_raise_ValueError("Invalid pin"); + } + if (refcount == 0) { + refcount++; + shared_construct(self, pin, 8000); + } + + self->buffer = (uint8_t*) buffer; + // Input len is a count. Internal len is in bytes. + self->len = 2 * len; + self->frequency = 8000; +} + +void common_hal_audioio_audioout_construct_from_file(audioio_audioout_obj_t* self, + const mcu_pin_obj_t* pin, + pyb_file_obj_t* file) { + mp_raise_NotImplementedError("File playback not supported yet."); +} + +void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self) { + refcount--; + if (refcount == 0) { + if (tc_instance != NULL) { + tc_reset(tc_instance); + gc_free(tc_instance); + tc_instance = NULL; + } + if (dac_instance != NULL) { + dac_reset(dac_instance); + gc_free(dac_instance); + dac_instance = NULL; + } + if (tc_event != NULL) { + events_detach_user(tc_event, EVSYS_ID_USER_DAC_START); + events_release(tc_event); + gc_free(tc_event); + tc_event = NULL; + } + if (dac_event != NULL) { + events_release(dac_event); + gc_free(dac_event); + dac_event = NULL; + } + reset_pin(self->pin->pin); + } +} + +static void set_timer_frequency(uint32_t frequency) { + uint32_t system_clock = system_cpu_clock_get_hz(); + uint32_t new_top; + uint8_t new_divisor; + for (new_divisor = 0; new_divisor < 8; new_divisor++) { + new_top = (system_clock / prescaler[new_divisor] / frequency) - 1; + if (new_top < (1u << 16)) { + break; + } + } + uint8_t old_divisor = tc_instance->hw->COUNT16.CTRLA.bit.PRESCALER; + if (new_divisor != old_divisor) { + tc_disable(tc_instance); + tc_instance->hw->COUNT16.CTRLA.bit.PRESCALER = new_divisor; + tc_enable(tc_instance); + } + while (tc_is_syncing(tc_instance)) { + /* Wait for sync */ + } + tc_instance->hw->COUNT16.CC[0].reg = new_top; + while (tc_is_syncing(tc_instance)) { + /* Wait for sync */ + } +} + +void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, bool loop) { + common_hal_audioio_audioout_get_playing(self); + // Shut down any active playback. + if (active_audioout != NULL) { + tc_stop_counter(tc_instance); + dma_abort_job(&audio_dma); + } + struct dma_descriptor_config descriptor_config; + dma_descriptor_get_config_defaults(&descriptor_config); + descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD; + descriptor_config.dst_increment_enable = false; + // Block transfer count is the number of beats per block (aka descriptor). + // In this case there are two bytes per beat so divide the length by two. + descriptor_config.block_transfer_count = self->len / 2; + descriptor_config.source_address = ((uint32_t)self->buffer + self->len); + descriptor_config.destination_address = ((uint32_t)&DAC->DATABUF.reg); + if (loop) { + descriptor_config.next_descriptor_address = ((uint32_t)audio_dma.descriptor); + } + + dma_descriptor_create(audio_dma.descriptor, &descriptor_config); + active_audioout = self; + dma_start_transfer_job(&audio_dma); + + set_timer_frequency(self->frequency); + tc_start_counter(tc_instance); +} + +void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) { + if (common_hal_audioio_audioout_get_playing(self)) { + tc_stop_counter(tc_instance); + dma_abort_job(&audio_dma); + active_audioout = NULL; + // TODO(tannewt): Disable the DAC to save power. + } +} + +bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self) { + if (!dma_is_busy(&audio_dma)) { + active_audioout = NULL; + } + return active_audioout == self; +} + +void common_hal_audioio_audioout_set_frequency(audioio_audioout_obj_t* self, + uint32_t frequency) { + if (frequency == 0 || frequency > 350000) { + mp_raise_ValueError("Unsupported playback frequency"); + } + self->frequency = frequency; + + if (common_hal_audioio_audioout_get_playing(self)) { + set_timer_frequency(frequency); + } +} + +uint32_t common_hal_audioio_audioout_get_frequency(audioio_audioout_obj_t* self) { + return self->frequency; +} diff --git a/atmel-samd/common-hal/audioio/AudioOut.h b/atmel-samd/common-hal/audioio/AudioOut.h new file mode 100644 index 000000000..7f4d83040 --- /dev/null +++ b/atmel-samd/common-hal/audioio/AudioOut.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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_ATMEL_SAMD_COMMON_HAL_AUDIOIO_AUDIOOUT_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_AUDIOIO_AUDIOOUT_H__ + +#include "common-hal/microcontroller/Pin.h" +#include "asf/sam0/drivers/tc/tc.h" + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; + uint32_t frequency; + uint8_t* buffer; + // TODO(tannewt): Add a second buffer for double buffering from a file system. + // Length of buffer in bytes. + uint32_t len; +} audioio_audioout_obj_t; + +void audioout_reset(void); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_AUDIOIO_AUDIOOUT_H__ diff --git a/atmel-samd/common-hal/audioio/__init__.c b/atmel-samd/common-hal/audioio/__init__.c new file mode 100644 index 000000000..404f021a1 --- /dev/null +++ b/atmel-samd/common-hal/audioio/__init__.c @@ -0,0 +1 @@ +// No audioio module functions. diff --git a/atmel-samd/common-hal/microcontroller/Pin.c b/atmel-samd/common-hal/microcontroller/Pin.c index fe8011ad8..3ac3430b2 100644 --- a/atmel-samd/common-hal/microcontroller/Pin.c +++ b/atmel-samd/common-hal/microcontroller/Pin.c @@ -97,6 +97,7 @@ bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t* pin) { return !apa102_sck_in_use; } #endif + PortGroup *const port = system_pinmux_get_group_from_gpio_pin(pin->pin); uint32_t pin_index = (pin->pin); PORT_PINCFG_Type state = port->PINCFG[pin_index]; diff --git a/atmel-samd/common-hal/pulseio/PulseOut.c b/atmel-samd/common-hal/pulseio/PulseOut.c index 1df4e30db..be95a1418 100644 --- a/atmel-samd/common-hal/pulseio/PulseOut.c +++ b/atmel-samd/common-hal/pulseio/PulseOut.c @@ -32,6 +32,7 @@ #include "asf/sam0/drivers/port/port.h" #include "mpconfigport.h" +#include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/pulseio/PulseOut.h" @@ -39,7 +40,7 @@ // This timer is shared amongst all PulseOut objects under the assumption that // the code is single threaded. -static struct tc_module tc_instance; +static struct tc_module* tc_instance; static uint8_t refcount = 0; static __IO PORT_PINCFG_Type *active_pincfg = NULL; @@ -68,7 +69,7 @@ void pulse_finish(struct tc_module *const module) { return; } current_compare = (current_compare + pulse_buffer[pulse_index] * 3 / 4) & 0xffff; - tc_set_compare_value(&tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare); + tc_set_compare_value(tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare); if (pulse_index % 2 == 0) { turn_on(active_pincfg); } @@ -76,6 +77,7 @@ void pulse_finish(struct tc_module *const module) { void pulseout_reset() { refcount = 0; + tc_instance = 0; active_pincfg = NULL; } @@ -94,6 +96,10 @@ void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, if (t == NULL) { mp_raise_RuntimeError("All timers in use"); } + tc_instance = gc_alloc(sizeof(struct tc_module), false); + if (t == NULL) { + mp_raise_msg(&mp_type_MemoryError, ""); + } struct tc_config config_tc; tc_get_config_defaults(&config_tc); @@ -102,10 +108,10 @@ void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, config_tc.clock_prescaler = TC_CTRLA_PRESCALER_DIV64; config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_FREQ; - tc_init(&tc_instance, t, &config_tc); - tc_register_callback(&tc_instance, pulse_finish, TC_CALLBACK_CC_CHANNEL0); - tc_enable(&tc_instance); - tc_stop_counter(&tc_instance); + tc_init(tc_instance, t, &config_tc); + tc_register_callback(tc_instance, pulse_finish, TC_CALLBACK_CC_CHANNEL0); + tc_enable(tc_instance); + tc_stop_counter(tc_instance); } refcount++; @@ -130,7 +136,9 @@ void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) { refcount--; if (refcount == 0) { - tc_reset(&tc_instance); + tc_reset(tc_instance); + gc_free(tc_instance); + tc_instance = NULL; } } @@ -144,11 +152,11 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu pulse_length = length; current_compare = pulses[0] * 3 / 4; - tc_set_compare_value(&tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare); + tc_set_compare_value(tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare); - tc_enable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0); + tc_enable_callback(tc_instance, TC_CALLBACK_CC_CHANNEL0); turn_on(active_pincfg); - tc_start_counter(&tc_instance); + tc_start_counter(tc_instance); while(pulse_index < length) { // Do other things while we wait. The interrupts will handle sending the @@ -158,7 +166,7 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu #endif } - tc_stop_counter(&tc_instance); - tc_disable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0); + tc_stop_counter(tc_instance); + tc_disable_callback(tc_instance, TC_CALLBACK_CC_CHANNEL0); active_pincfg = NULL; } diff --git a/atmel-samd/main.c b/atmel-samd/main.c index 70e1551ed..bc2979898 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -25,6 +25,7 @@ #include #include "common-hal/analogio/AnalogIn.h" +#include "common-hal/audioio/AudioOut.h" #include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PWMOut.h" @@ -40,6 +41,7 @@ #include "autoreset.h" #include "mpconfigboard.h" #include "rgb_led_status.h" +#include "shared_dma.h" #include "tick.h" fs_user_mount_t fs_user_mount_flash; @@ -180,6 +182,7 @@ void reset_samd21(void) { system_pinmux_group_set_config(&(PORT->Group[0]), pin_mask[0] & ~MICROPY_PORT_A, &config); system_pinmux_group_set_config(&(PORT->Group[1]), pin_mask[1] & ~MICROPY_PORT_B, &config); + audioout_reset(); pwmout_reset(); usb_hid_reset(); @@ -504,6 +507,8 @@ void samd21_init(void) { nvm_get_config_defaults(&config_nvm); config_nvm.manual_page_write = false; nvm_set_config(&config_nvm); + + init_shared_dma(); } extern uint32_t _estack; diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index a0b92d13b..16d9cb075 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -125,6 +125,7 @@ typedef long mp_off_t; // extra built in modules to add to the list of known ones extern const struct _mp_obj_module_t microcontroller_module; extern const struct _mp_obj_module_t bitbangio_module; +extern const struct _mp_obj_module_t audioio_module; extern const struct _mp_obj_module_t analogio_module; extern const struct _mp_obj_module_t digitalio_module; extern const struct _mp_obj_module_t pulseio_module; @@ -143,6 +144,7 @@ extern const struct _mp_obj_module_t usb_hid_module; #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_FRAMEBUF (1) #define EXTRA_BUILTIN_MODULES \ + { MP_OBJ_NEW_QSTR(MP_QSTR_audioio), (mp_obj_t)&audioio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_pulseio), (mp_obj_t)&pulseio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module } #define EXPRESS_BOARD diff --git a/atmel-samd/shared_dma.c b/atmel-samd/shared_dma.c new file mode 100644 index 000000000..b4bbdd07a --- /dev/null +++ b/atmel-samd/shared_dma.c @@ -0,0 +1,54 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "shared_dma.h" + +// We allocate two DMA resources for the entire lifecycle of the board (not the +// vm) because the general_dma resource will be shared between the REPL and SPI +// flash. Both uses must block each other in order to prevent conflict. +struct dma_resource audio_dma; +struct dma_resource general_dma; + +void init_shared_dma(void) { + struct dma_resource_config config; + dma_get_config_defaults(&config); + + // This allocates the lowest channel first so make sure the audio is first + // so it gets the highest priority. + config.peripheral_trigger = DAC_DMAC_ID_EMPTY; + config.trigger_action = DMA_TRIGGER_ACTION_BEAT; + config.event_config.input_action = DMA_EVENT_INPUT_TRIG; + dma_allocate(&audio_dma, &config); + // Turn on the transfer complete interrupt so that the job_status changes to done. + g_chan_interrupt_flag[audio_dma.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE); + + dma_get_config_defaults(&config); + dma_allocate(&general_dma, &config); + + // Be sneaky and reuse the active descriptor memory. + audio_dma.descriptor = &descriptor_section[audio_dma.channel_id]; + general_dma.descriptor = &descriptor_section[general_dma.channel_id]; +} diff --git a/atmel-samd/shared_dma.h b/atmel-samd/shared_dma.h new file mode 100644 index 000000000..409b06bac --- /dev/null +++ b/atmel-samd/shared_dma.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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_ATMEL_SAMD_SHARED_DMA_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_SHARED_DMA_H__ + +#include "asf/sam0/drivers/dma/dma.h" + +extern struct dma_resource audio_dma; +extern struct dma_resource general_dma; + +void init_shared_dma(void); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_SHARED_DMA_H__ diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 77848b5a5..ef57225a9 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -24,7 +24,8 @@ * THE SOFTWARE. */ -#include "py/mpconfig.h" +#include "extmod/vfs_fat_file.h" + // *_ADHOC part is for cc3200 port which doesn't use general uPy // infrastructure and instead duplicates code. TODO: Resolve. #if MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC @@ -37,15 +38,6 @@ #include "py/stream.h" #include "py/mperrno.h" #include "lib/fatfs/ff.h" -#include "extmod/vfs_fat_file.h" - -#if MICROPY_VFS_FAT -#define mp_type_fileio fatfs_type_fileio -#define mp_type_textio fatfs_type_textio -#endif - -extern const mp_obj_type_t mp_type_fileio; -extern const mp_obj_type_t mp_type_textio; // this table converts from FRESULT to POSIX errno const byte fresult_to_errno_table[20] = { @@ -71,11 +63,6 @@ const byte fresult_to_errno_table[20] = { [FR_INVALID_PARAMETER] = MP_EINVAL, }; -typedef struct _pyb_file_obj_t { - mp_obj_base_t base; - FIL fp; -} pyb_file_obj_t; - STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_printf(print, "", mp_obj_get_type_str(self_in), MP_OBJ_TO_PTR(self_in)); diff --git a/extmod/vfs_fat_file.h b/extmod/vfs_fat_file.h index 5c271b6ee..5dc627986 100644 --- a/extmod/vfs_fat_file.h +++ b/extmod/vfs_fat_file.h @@ -24,9 +24,35 @@ * THE SOFTWARE. */ +#ifndef __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__ +#define __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__ + +#include "lib/fatfs/ff.h" +#include "py/mpconfig.h" +#include "py/obj.h" + +#if MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC + extern const byte fresult_to_errno_table[20]; +#if MICROPY_VFS_FAT +#define mp_type_fileio fatfs_type_fileio +#define mp_type_textio fatfs_type_textio +#endif + +extern const mp_obj_type_t mp_type_fileio; +extern const mp_obj_type_t mp_type_textio; + +typedef struct _pyb_file_obj_t { + mp_obj_base_t base; + FIL fp; +} pyb_file_obj_t; + mp_obj_t fatfs_builtin_open(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs); MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj); mp_obj_t fat_vfs_listdir(const char *path, bool is_str_type); + +#endif // MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC + +#endif // __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__ diff --git a/shared-bindings/analogio/AnalogIn.h b/shared-bindings/analogio/AnalogIn.h index 4f31537de..0ddc1ea0c 100644 --- a/shared-bindings/analogio/AnalogIn.h +++ b/shared-bindings/analogio/AnalogIn.h @@ -34,7 +34,7 @@ extern const mp_obj_type_t analogio_analogin_type; void common_hal_analogio_analogin_construct(analogio_analogin_obj_t* self, const mcu_pin_obj_t *pin); void common_hal_analogio_analogin_deinit(analogio_analogin_obj_t* self); -uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self); -float common_hal_analogio_analogin_get_reference_voltage(analogio_analogin_obj_t *self); +uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t* self); +float common_hal_analogio_analogin_get_reference_voltage(analogio_analogin_obj_t* self); #endif // __MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO_ANALOGIN_H__ diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c new file mode 100644 index 000000000..81117abb2 --- /dev/null +++ b/shared-bindings/audioio/AudioOut.c @@ -0,0 +1,222 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "lib/utils/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audioio/AudioOut.h" + +//| .. currentmodule:: audioio +//| +//| :class:`AudioOut` -- Output an analog audio signal +//| ======================================================== +//| +//| AudioOut can be used to output an analog audio signal on a given pin. +//| +//| .. class:: AudioOut(pin, sample_source) +//| +//| Create a AudioOut object associated with the given pin. This allows you to +//| play audio signals out on the given pin. Sample_source must be a `bytes-like object `_. +//| +//| The sample itself should consist of 16 bit samples and be mono. +//| Microcontrollers with a lower output resolution will use the highest order +//| bits to output. For example, the SAMD21 has a 10 bit DAC that ignores the +//| lowest 6 bits when playing 16 bit samples. +//| +//| :param ~microcontroller.Pin pin: The pin to output to +//| :param bytes-like sample_source: The source of the sample +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audioio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| b = array.array("H", [0] * length) +//| for i in range(length): +//| b[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) +//| +//| with audioio.AudioOut(board.SPEAKER, sin_wave) as sample: +//| sample.play(loop=True) +//| time.sleep(1) +//| sample.stop() +//| +STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 2, true); + mp_obj_t pin_obj = args[0]; + assert_pin(pin_obj, false); + const mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_obj); + // We explicitly don't check whether the pin is free because multiple + // AudioOuts may share it. + + // create PWM object from the given pin + audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t); + self->base.type = &audioio_audioout_type; + mp_buffer_info_t bufinfo; + if (MP_OBJ_IS_TYPE(args[1], &fatfs_type_fileio)) { + common_hal_audioio_audioout_construct_from_file(self, pin, MP_OBJ_TO_PTR(args[1])); + } else if (mp_get_buffer(args[1], &bufinfo, MP_BUFFER_READ)) { + if (bufinfo.len % 2 == 1) { + mp_raise_ValueError("sample_source must be an even number of bytes (two per sample)"); + } + common_hal_audioio_audioout_construct_from_buffer(self, pin, ((uint16_t*)bufinfo.buf), bufinfo.len / 2); + } else { + mp_raise_TypeError("sample_source must be a file or bytes-like object."); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the PWMOut and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_audioout_deinit(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_audioout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_deinit_obj, audioio_audioout_deinit); + +//| .. method:: __enter__() +//| +//| No-op used by Context Managers. +//| +// Provided by context manager helper. + +//| .. method:: __exit__() +//| +//| Automatically deinitializes the hardware when exiting a context. +//| +STATIC mp_obj_t audioio_audioout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_audioout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_audioout___exit___obj, 4, 4, audioio_audioout_obj___exit__); + + +//| .. method:: play(loop=False) +//| +//| Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +STATIC mp_obj_t audioio_audioout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_loop, MP_ARG_BOOL, {.u_bool = false} }, + }; + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + common_hal_audioio_audioout_play(self, args[ARG_loop].u_bool); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_audioout_play_obj, 1, audioio_audioout_obj_play); + +//| .. method:: stop() +//| +//| Stops playback of this sample. If another sample is playing instead, it +//| won't be stopped. +//| +STATIC mp_obj_t audioio_audioout_obj_stop(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_audioout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_stop_obj, audioio_audioout_obj_stop); + +//| .. attribute:: playing +//| +//| True when the audio sample is being output. +//| +STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_audioio_audioout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_playing_obj, audioio_audioout_obj_get_playing); + +const mp_obj_property_t audioio_audioout_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_audioout_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: frequency +//| +//| 32 bit value that dictates how quickly samples are loaded into the DAC +//| in Hertz (cycles per second). When the sample is looped, this can change +//| the pitch output without changing the underlying sample. +//| +STATIC mp_obj_t audioio_audioout_obj_get_frequency(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_audioout_get_frequency(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_frequency_obj, audioio_audioout_obj_get_frequency); + +STATIC mp_obj_t audioio_audioout_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_audioout_set_frequency(self, mp_obj_get_int(frequency)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audioio_audioout_set_frequency_obj, audioio_audioout_obj_set_frequency); + +const mp_obj_property_t audioio_audioout_frequency_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_audioout_get_frequency_obj, + (mp_obj_t)&audioio_audioout_set_frequency_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_audioout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_audioout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_audioout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_audioout_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_audioout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&audioio_audioout_frequency_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_audioout_locals_dict, audioio_audioout_locals_dict_table); + +const mp_obj_type_t audioio_audioout_type = { + { &mp_type_type }, + .name = MP_QSTR_AudioOut, + .make_new = audioio_audioout_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_audioout_locals_dict, +}; diff --git a/shared-bindings/audioio/AudioOut.h b/shared-bindings/audioio/AudioOut.h new file mode 100644 index 000000000..522792117 --- /dev/null +++ b/shared-bindings/audioio/AudioOut.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H__ +#define __MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H__ + +#include "common-hal/audioio/AudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs_fat_file.h" + +extern const mp_obj_type_t audioio_audioout_type; + +void common_hal_audioio_audioout_construct_from_buffer(audioio_audioout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t* buffer, uint32_t len); +void common_hal_audioio_audioout_construct_from_file(audioio_audioout_obj_t* self, const mcu_pin_obj_t* pin, pyb_file_obj_t* file); + +void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self); +void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, bool loop); +void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self); +bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self); +uint32_t common_hal_audioio_audioout_get_frequency(audioio_audioout_obj_t* self); +void common_hal_audioio_audioout_set_frequency(audioio_audioout_obj_t* self, uint32_t frequency); + +#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H__ diff --git a/shared-bindings/audioio/__init__.c b/shared-bindings/audioio/__init__.c new file mode 100644 index 000000000..42d6aa1c3 --- /dev/null +++ b/shared-bindings/audioio/__init__.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audioio/__init__.h" +#include "shared-bindings/audioio/AudioOut.h" + +//| :mod:`audioio` --- Support for audio input and output +//| ================================================= +//| +//| .. module:: audioio +//| :synopsis: Support for audio input and output +//| :platform: SAMD21 +//| +//| The `audioio` module contains classes to provide access to audio IO. +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| AudioOut +//| +//| All libraries change hardware state and should be deinitialized when they +//| are no longer needed. To do so, either call :py:meth:`!deinit` or use a +//| context manager. +//| + +STATIC const mp_rom_map_elem_t audioio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audioio) }, + { MP_ROM_QSTR(MP_QSTR_AudioOut), MP_ROM_PTR(&audioio_audioout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audioio_module_globals, audioio_module_globals_table); + +const mp_obj_module_t audioio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&audioio_module_globals, +}; diff --git a/shared-bindings/audioio/__init__.h b/shared-bindings/audioio/__init__.h new file mode 100644 index 000000000..32d1f2456 --- /dev/null +++ b/shared-bindings/audioio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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_SHARED_BINDINGS_AUDIOIO___INIT___H__ +#define __MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H__ + +#include "py/obj.h" + +// Nothing now. + +#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H__ -- cgit v1.2.3