atmel-samd: Introduce audio sample playback via audioio.AudioOut.

This commit is contained in:
Scott Shawcroft 2017-04-21 14:43:03 -07:00
parent 30b8091df0
commit 58b9789d0c
30 changed files with 5596 additions and 32 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
// No audioio module functions.

View File

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

View File

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

View File

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

View File

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

54
atmel-samd/shared_dma.c Normal file
View File

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

37
atmel-samd/shared_dma.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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