summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Shawcroft <scott.shawcroft@gmail.com>2017-04-21 14:43:03 -0700
committerScott Shawcroft <scott.shawcroft@gmail.com>2017-05-01 13:10:03 -0700
commit58b9789d0c824c665db744ef3653d6304e908bc0 (patch)
treecffd27607f558314246640be45b245f512c8c939
parent30b8091df016bab1fce1f3aa5a813fc8e279cb16 (diff)
atmel-samd: Introduce audio sample playback via audioio.AudioOut.
-rw-r--r--atmel-samd/Makefile12
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/dma.c654
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/dma.h882
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/dma_crc.h230
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/dma_sam_b.c557
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/dma_sam_b.h650
-rw-r--r--atmel-samd/asf/sam0/drivers/dma/module_config/conf_dma.h51
-rw-r--r--atmel-samd/asf/sam0/drivers/events/events.h729
-rw-r--r--atmel-samd/asf/sam0/drivers/events/events_common.h71
-rw-r--r--atmel-samd/asf/sam0/drivers/events/events_hooks.c235
-rw-r--r--atmel-samd/asf/sam0/drivers/events/events_hooks.h182
-rw-r--r--atmel-samd/asf/sam0/drivers/events/events_sam_d_r/events.c328
-rw-r--r--atmel-samd/asf_conf/conf_dma.h51
-rw-r--r--atmel-samd/boards/circuitplayground_express/conf_dma.h51
-rw-r--r--atmel-samd/common-hal/audioio/AudioOut.c350
-rw-r--r--atmel-samd/common-hal/audioio/AudioOut.h47
-rw-r--r--atmel-samd/common-hal/audioio/__init__.c1
-rw-r--r--atmel-samd/common-hal/microcontroller/Pin.c1
-rw-r--r--atmel-samd/common-hal/pulseio/PulseOut.c32
-rw-r--r--atmel-samd/main.c5
-rw-r--r--atmel-samd/mpconfigport.h2
-rw-r--r--atmel-samd/shared_dma.c54
-rw-r--r--atmel-samd/shared_dma.h37
-rw-r--r--extmod/vfs_fat_file.c17
-rw-r--r--extmod/vfs_fat_file.h26
-rw-r--r--shared-bindings/analogio/AnalogIn.h4
-rw-r--r--shared-bindings/audioio/AudioOut.c222
-rw-r--r--shared-bindings/audioio/AudioOut.h46
-rw-r--r--shared-bindings/audioio/__init__.c67
-rw-r--r--shared-bindings/audioio/__init__.h34
30 files changed, 5596 insertions, 32 deletions
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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#include <string.h>
+#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<<DMA_CALLBACK_TRANSFER_ERROR)) &&
+ (resource->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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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&reg; | 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.
+ *
+ * <table>
+ * <tr>
+ * <th>Device</th>
+ * <th>Dma channel number</th>
+ * </tr>
+ * <tr>
+ * <td>SAM D21/R21/C20/C21</td>
+ * <td>12</td>
+ * </tr>
+ * <tr>
+ * <td>SAM D09/D10/D11</td>
+ * <td>6</td>
+ * </tr>
+ * <tr>
+ * <td>SAM L21,SAMR30</td>
+ * <td>16</td>
+ * </tr>
+ * </table>
+ * 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
+ * <table>
+ * <tr>
+ * <th>Driver Feature Macro</th>
+ * <th>Supported devices</th>
+ * </tr>
+ * <tr>
+ * <td>FEATURE_DMA_CHANNEL_STANDBY</td>
+ * <td>SAM L21/L22/C20/C21/R30</td>
+ * </tr>
+ * </table>
+ * \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
+ *
+ * <table border="0" cellborder="1" cellspacing="0" >
+ * <tr>
+ * <th> Name </th> <th> Description </th>
+ * </tr>
+ * <tr>
+ * <td > Beat </td>
+ * <td > It is a single bus access by the DMAC.
+ * Configurable as 8-bit, 16-bit, or 32-bit.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td > Burst </td>
+ * <td> 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.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td > Block transfer </td>
+ * <td> A single block transfer is a configurable number of (1 to 64k)
+ * beat transfers
+ * </td>
+ * </tr>
+ * </table>
+ *
+ * \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.
+ * <table border="0" cellborder="1" cellspacing="0" >
+ * <tr>
+ * <th> Field name </th> <th> Field width </th>
+ * </tr>
+ * <tr>
+ * <td > Descriptor Next Address </td> <td > 32 bits </td>
+ * </tr>
+ * <tr>
+ * <td > Destination Address </td> <td> 32 bits </td>
+ * </tr>
+ * <tr>
+ * <td > Source Address </td> <td> 32 bits </td>
+ * </tr>
+ * <tr>
+ * <td > Block Transfer Counter </td> <td> 16 bits </td>
+ * </tr>
+ * <tr>
+ * <td > Block Transfer Control </td> <td> 16 bits </td>
+ * </tr>
+ * </table>
+ *
+ * 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 <compiler.h>
+#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.
+ *
+ * <table>
+ * <tr>
+ * <th>Acronym</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>DMA</td>
+ * <td>Direct Memory Access</td>
+ * </tr>
+ * <tr>
+ * <td>DMAC</td>
+ * <td>Direct Memory Access Controller </td>
+ * </tr>
+ * <tr>
+ * <td>CPU</td>
+ * <td>Central Processing Unit</td>
+ * </tr>
+ * </table>
+ *
+ *
+ * \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.
+ *
+ * <table>
+ * <tr>
+ * <th>Changelog</th>
+ * </tr>
+ * <tr>
+ * <td>Add SAM C21 support</td>
+ * </tr>
+ * <tr>
+ * <td>Add SAM L21 support</td>
+ * </tr>
+ * <tr>
+ * <td>Add SAM R30 support</td>
+ * </tr>
+ * <tr>
+ * <td>Initial Release</td>
+ * </tr>
+ * </table>
+ */
+
+ /**
+ * \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/I<SUP>2</SUP>C 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
+ *
+ * <table>
+ * <tr>
+ * <th>Doc. Rev.</th>
+ * <th>Date</th>
+ * <th>Comments</th>
+ * </tr>
+ * <tr>
+ * <td>42257C</td>
+ * <td>12/2015</td>
+ * <td>Added suppport for SAM L21/L22, SAM C21, SAM D09, SAMR30 and SAM DA1</td>
+ * </tr>
+ * <tr>
+ * <td>42257B</td>
+ * <td>12/2014</td>
+ * <td>Added support for SAM R21 and SAM D10/D11</td>
+ * </tr>
+ * <tr>
+ * <td>42257A</td>
+ * <td>02/2014</td>
+ * <td>Initial release</td>
+ * </tr>
+ * </table>
+ */
+
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#ifndef DMA_CRC_H_INCLUDED
+#define DMA_CRC_H_INCLUDED
+
+#include <compiler.h>
+
+#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; counter<total_beat_size; counter++) {
+ if (DMAC->CRCCTRL.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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#include <string.h>
+#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<<isr_flag);
+ /* Execute the callback function */
+ if ((resource->callback_enable & (1<<isr_flag)) &&
+ (resource->callback[isr_flag])) {
+ resource->callback[isr_flag](resource);
+ }
+ }
+ isr &= ~(1<<isr_flag);
+ } while (isr);
+
+ NVIC_ClearPendingIRQ(PROV_DMA_CTRL0_IRQn);
+}
+
+/**
+ * \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;
+
+ if (!_dma_inst._dma_init) {
+ /* Perform a reset before enable DMA controller */
+ system_peripheral_reset(PERIPHERAL_DMA);
+ /* Select Mux 15 as PROV_DMA_CTRL0 interrupt source */
+ LPMCU_MISC_REGS0->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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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&reg; | 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.
+ *
+ * <table>
+ * <tr>
+ * <th>Device</th>
+ * <th>Dma channel number</th>
+ * </tr>
+ * <tr>
+ * <td>SAMB11</td>
+ * <td>4</td>
+ * </tr>
+ * </table>
+ * 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.
+ *
+ * <table>
+ * <tr>
+ * <th>Field name</th>
+ * <th>Field width</th>
+ * </tr>
+ * <tr>
+ * <td>Source Address</td>
+ * <td>32 bits</td>
+ * </tr>
+ * <tr>
+ * <td>Destination Address</td>
+ * <td>32 bits</td>
+ * </tr>
+ * <tr>
+ * <td>Block Transfer Counter</td>
+ * <td>32 bits</td>
+ * </tr>
+ * <tr>
+ * <td>Descriptor Next Address</td>
+ * <td>30 bits</td>
+ * </tr>
+ * <tr>
+ * <td>Block Transfer Interrupt</td>
+ * <td>1 bit</td>
+ * </tr>
+ * <tr>
+ * <td>Block Transfer Stop Control</td>
+ * <td>1 bit</td>
+ * </tr>
+ * </table>
+ *
+ * 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 <compiler.h>
+#include <system_sam_b.h>
+#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.
+ *
+ * <table>
+ * <tr>
+ * <th>Acronym</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>DMA</td>
+ * <td>Direct Memory Access</td>
+ * </tr>
+ * <tr>
+ * <td>DMAC</td>
+ * <td>Direct Memory Access Controller </td>
+ * </tr>
+ * <tr>
+ * <td>CPU</td>
+ * <td>Central Processing Unit</td>
+ * </tr>
+ * </table>
+ *
+ *
+ * \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.
+ *
+ * <table>
+ * <tr>
+ * <th>Changelog</th>
+ * </tr>
+ * <tr>
+ * <td>Initial Release</td>
+ * </tr>
+ * </table>
+ */
+
+ /**
+ * \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
+ *
+ * <table>
+ * <tr>
+ * <th>Doc. Rev.</td>
+ * <th>Date</td>
+ * <th>Comments</td>
+ * </tr>
+ * <tr>
+ * <td>A</td>
+ * <td>09/2015</td>
+ * <td>Initial release</td>
+ * </tr>
+ * </table>
+ */
+
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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&reg; | SMART ARM&reg;-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="<f0> EVSYS | <f1> 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="<f0> EVSYS | <f1> 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="<f0> EVSYS | <f1> 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="<f0> EVSYS | <f1> Event Channels" fillcolor=white shape=record] events_chan;
+ * node [label="<f0> EVSYS | <f1> 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 <compiler.h>
+#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.
+ *
+ * <table>
+ * <tr>
+ * <th>Acronym</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>CPU</td>
+ * <td>Central Processing Unit</td>
+ * </tr>
+ * <tr>
+ * <td>MUX</td>
+ * <td>Multiplexer</td>
+ * </tr>
+ * </table>
+ *
+ *
+ * \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.
+ *
+ * <table>
+ * <tr>
+ * <th>Changelog</th>
+ * </tr>
+ * <tr>
+ * <td>Fix a bug in internal function _events_find_bit_position()</td>
+ * </tr>
+ * <tr>
+ * <td>Rewrite of events driver</td>
+ * </tr>
+ * <tr>
+ * <td>Initial Release</td>
+ * </tr>
+ * </table>
+ */
+
+ /**
+ * \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
+ *
+ * <table>
+ * <tr>
+ * <th>Doc. Rev.</td>
+ * <th>Date</td>
+ * <th>Comments</td>
+ * </tr>
+ * <tr>
+ * <td>42108G</td>
+ * <td>12/2015</td>
+ * <td>Added support for SAM D09 and SAM L22</td>
+ * </tr>
+ * <tr>
+ * <td>42108F</td>
+ * <td>08/2015</td>
+ * <td>Added support for SAM L21, SAM DA1, SAMR30 and SAM C20/C21</td>
+ * </tr>
+ * <tr>
+ * <td>42108E</td>
+ * <td>12/2014</td>
+ * <td>Added support for interrupt hook mode.
+ * Added support for SAM R21 and SAM D10/D11.</td>
+ * </tr>
+ * <tr>
+ * <td>42108D</td>
+ * <td>01/2014</td>
+ * <td>Update to support SAM D21 and corrected documentation typos</td>
+ * </tr>
+ * <tr>
+ * <td>42108C</td>
+ * <td>11/2013</td>
+ * <td>Fixed incorrect documentation for the event signal paths. Added
+ * configuration steps overview to the documentation.</td>
+ * </tr>
+ * <tr>
+ * <td>42108B</td>
+ * <td>06/2013</td>
+ * <td>Corrected documentation typos</td>
+ * </tr>
+ * <tr>
+ * <td>42108A</td>
+ * <td>06/2013</td>
+ * <td>Initial release</td>
+ * </tr>
+ * </table>
+ */
+
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#include <events.h>
+#include <system.h>
+#include <system_interrupt.h>
+#include <status_codes.h>
+
+#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<<resource->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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#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 <stdint.h>
+
+#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 <board.h>
#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, "<io.%s %p>", 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 <stdint.h>
+
+#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 <https://docs.python.org/3/glossary.html#term-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 <stdint.h>
+
+#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__