blob: c664f7746162e2ef828e83b6cb731251774cfde1 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: 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
* 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.
* ----------------------------------------------------------------------------
*/
/** \addtogroup dacc_module Working with DACC
* \ingroup peripherals_module
* The DACC driver provides the interface to configure and use the DACC
* peripheral.\n
*
* The DACC(Digital-to-Analog Converter Controller) converts digital code to
* analog output.
* The data to be converted are sent in a common register for all channels.
* It offers up to 2 analog outputs.The output voltage ranges from (1/6)ADVREF
* to (5/6)ADVREF.
*
* To Enable a DACC conversion,the user has to follow these few steps:
* <ul>
* <li> Select an appropriate reference voltage on ADVREF </li>
* <li> Configure the DACC according to its requirements and special needs,
* which could be broken down into several parts:
* -# Enable DACC in free running mode by clearing TRGEN in DACC_MR;
* -# Configure Refresh Period through setting REFRESH fields
* in DACC_MR; The refresh mechanism is used to protect the output analog
* value from
* decreasing.
* -# Enable channels and write digital code to DACC_CDR,in free running mode,
* the conversion is started right after at least one channel is enabled and
* data is written .
</li>
* </ul>
*
* For more accurate information, please look at the DACC section of the
* Datasheet.
*
* Related files :\n
* \ref dac_dma.c\n
* \ref dac_dma.h\n
*/
/*@{*/
/*@}*/
/**
* \file
*
* Implementation of Digital-to-Analog Converter Controller (DACC).
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "chip.h"
#include <stdint.h>
#include <assert.h>
/* DMA driver instance */
static uint32_t dacDmaTxChannel;
static LinkedListDescriporView1 dmaWriteLinkList[256];
/*----------------------------------------------------------------------------
* Local functions
*----------------------------------------------------------------------------*/
/**
* \brief Configure the DMA Channels: 0 RX.
* Channels are disabled after configure.
* \returns 0 if the dma channel configuration successfully; otherwise returns
* DAC_ERROR_XXX.
*/
static uint8_t _DacConfigureDmaChannels( DacDma* pDacd )
{
/* Driver initialize */
XDMAD_Initialize( pDacd->pXdmad, 0 );
XDMAD_FreeChannel( pDacd->pXdmad, dacDmaTxChannel);
/* Allocate a DMA channel for DAC0/1 TX. */
dacDmaTxChannel =
XDMAD_AllocateChannel( pDacd->pXdmad, XDMAD_TRANSFER_MEMORY, ID_DACC);
if ( dacDmaTxChannel == XDMAD_ALLOC_FAILED ) {
return DAC_ERROR;
}
if ( XDMAD_PrepareChannel( pDacd->pXdmad, dacDmaTxChannel ))
return DAC_ERROR;
return DAC_OK;
}
/**
* \brief Configure the DMA source and destination with Linker List mode.
*
* \param pBuffer Pointer to dac buffer
* \param size length of buffer
*/
static uint8_t _Dac_configureLinkList(Dacc *pDacHw, void *pXdmad, DacCmd *pCommand)
{
uint32_t xdmaCndc;
sXdmadCfg xdmadCfg;
uint32_t * pBuffer;
/* Setup TX Link List */
uint8_t i;
pBuffer = (uint32_t *)pCommand->pTxBuff;
for(i = 0; i < pCommand->TxSize; i++){
dmaWriteLinkList[i].mbr_ubc = XDMA_UBC_NVIEW_NDV1
| XDMA_UBC_NDE_FETCH_EN
| XDMA_UBC_NSEN_UPDATED
| XDMAC_CUBC_UBLEN(4);
dmaWriteLinkList[i].mbr_sa = (uint32_t)pBuffer;
dmaWriteLinkList[i].mbr_da =
(uint32_t)&(pDacHw->DACC_CDR[pCommand->dacChannel]);
if ( i == (pCommand->TxSize - 1 )) {
if (pCommand->loopback) {
dmaWriteLinkList[i].mbr_nda = (uint32_t)&dmaWriteLinkList[0];
} else {
dmaWriteLinkList[i].mbr_nda = 0;
}
} else {
dmaWriteLinkList[i].mbr_nda = (uint32_t)&dmaWriteLinkList[i+1];
}
pBuffer++;
}
xdmadCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN
| XDMAC_CC_MBSIZE_SINGLE
| XDMAC_CC_DSYNC_MEM2PER
| XDMAC_CC_CSIZE_CHK_1
| XDMAC_CC_DWIDTH_WORD
| XDMAC_CC_SIF_AHB_IF0
| XDMAC_CC_DIF_AHB_IF1
| XDMAC_CC_SAM_INCREMENTED_AM
| XDMAC_CC_DAM_FIXED_AM
| XDMAC_CC_PERID(
XDMAIF_Get_ChannelNumber(ID_DACC, XDMAD_TRANSFER_TX ));
xdmaCndc = XDMAC_CNDC_NDVIEW_NDV1
| XDMAC_CNDC_NDE_DSCR_FETCH_EN
| XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED
| XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED ;
XDMAD_ConfigureTransfer( pXdmad, dacDmaTxChannel, &xdmadCfg, xdmaCndc,
(uint32_t)&dmaWriteLinkList[0], XDMAC_CIE_LIE);
return DAC_OK;
}
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
/**
* \brief Initializes the DacDma structure and the corresponding DAC & DMA .
* hardware select value.
* The driver will uses DMA channel 0 for RX .
* The DMA channels are freed automatically when no DMA command processing.
*
* \param pDacd Pointer to a DacDma instance.
* \param pDacHw Associated Dac peripheral.
* \param DacId Dac peripheral identifier.
* \param pDmad Pointer to a Dmad instance.
*/
uint32_t Dac_ConfigureDma( DacDma *pDacd ,
Dacc *pDacHw ,
uint8_t DacId,
sXdmad *pXdmad )
{
/* Initialize the Dac structure */
pDacd->pDacHw = pDacHw;
pDacd->dacId = DacId;
pDacd->semaphore = 1;
pDacd->pCurrentCommand = 0;
pDacd->pXdmad = pXdmad;
return 0;
}
/**
* \brief Starts a DAC transfer. This is a non blocking function. It will
* return as soon as the transfer is started.
*
* \param pDacd Pointer to a DacDma instance.
* \param pCommand Pointer to the Dac command to execute.
* \returns 0 if the transfer has been started successfully; otherwise returns
* DAC_ERROR_LOCK is the driver is in use, or DAC_ERROR if the command is not
* valid.
*/
uint32_t Dac_SendData( DacDma *pDacd, DacCmd *pCommand)
{
Dacc *pDacHw = pDacd->pDacHw;
/* Try to get the dataflash semaphore */
if (pDacd->semaphore == 0) {
return DAC_ERROR_LOCK;
}
pDacd->semaphore--;
// Initialize the callback
pDacd->pCurrentCommand = pCommand;
/* Initialize DMA controller using channel 0 for RX. */
if (_DacConfigureDmaChannels(pDacd) )
return DAC_ERROR_LOCK;
if (_Dac_configureLinkList(pDacHw, pDacd->pXdmad, pCommand))
return DAC_ERROR_LOCK;
SCB_CleanDCache();
/* Start DMA TX */
if (XDMAD_StartTransfer( pDacd->pXdmad, dacDmaTxChannel ))
return DAC_ERROR_LOCK;
return DAC_OK;;
}