blob: 476793b319095e3b74fa3faa3b6317cbd2c59f63 [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 AFEC_module Working with AFE
* \ingroup peripherals_module
* The AFE driver provides the interface to configure and use the AFE peripheral.
* \n
*
* It converts the analog input to digital format. The converted result could be
* 12bit or 10bit. The AFE supports up to 16 analog lines.
*
* To Enable a AFE conversion,the user has to follow these few steps:
* <ul>
* <li> Select an appropriate reference voltage on ADVREF </li>
* <li> Configure the AFE according to its requirements and special needs,which
* could be broken down into several parts:
* -# Select the resolution by setting or clearing AFEC_MR_LOWRES bit in
* AFEC_MR (Mode Register)
* -# Set AFE clock by setting AFEC_MR_PRESCAL bits in AFEC_MR, the clock is
* calculated with AFEClock = MCK / ( (PRESCAL+1) * 2 )
* -# Set Startup Time,Tracking Clock cycles and Transfer Clock respectively
* in AFEC_MR.
</li>
* <li> Start conversion by setting AFEC_CR_START in AFEC_CR. </li>
* </ul>
*
* For more accurate information, please look at the AFE section of the
* Datasheet.
*
* Related files :\n
* \ref afec.c\n
* \ref afec.h\n
* \ref afe_dma.c\n
* \ref afe_dma.h\n
*/
/*@{*/
/*@}*/
/**
* \file
*
* Implementation of Analog-to-Digital Converter (AFE).
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "chip.h"
/*----------------------------------------------------------------------------
* Local variables
*----------------------------------------------------------------------------*/
/** Current working clock */
static uint32_t dwAFEClock = 0;
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
/**
* \brief Initialize the AFE controller
*
* \param pAFE Pointer to an AFE instance.
* \param dwID AFE Index
*/
extern void AFEC_Initialize( Afec* pAFE, uint32_t dwID )
{
/* Enable peripheral clock*/
PMC_EnablePeripheral(dwID);
/* Reset the controller */
pAFE->AFEC_CR = AFEC_CR_SWRST;
/* Reset Mode Register */
pAFE->AFEC_MR = 0;
}
/**
* \brief Set AFE clock.
*
* \param pAFE Pointer to an AFE instance.
* \param dwPres prescale value
* \param dwMck Board MCK (Hz)
*
* \return AFE clock
*/
extern uint32_t AFEC_SetClock( Afec* pAFE, uint32_t dwClk, uint32_t dwMck )
{
uint32_t dwPres, dwMr;
/* Formula for PRESCAL is:
PRESCAL = peripheral clock/ fAFE Clock - 1 */
dwPres = (dwMck) / (dwClk ) - 1;
dwMr = AFEC_MR_PRESCAL(dwPres);
if (dwMr == 0) return 0;
dwMr |= (pAFE->AFEC_MR & ~AFEC_MR_PRESCAL_Msk);
pAFE->AFEC_MR = dwMr;
dwAFEClock = dwMck / (dwPres + 1);
return dwAFEClock;
}
/**
* \brief Set AFE timing.
*
* \param pAFE Pointer to an AFE instance.
* \param dwStartup startup value
* \param dwTracking tracking value
* \param dwSettling settling value
*/
extern void AFEC_SetTiming( Afec* pAFE, uint32_t dwStartup, uint32_t dwTracking,
uint32_t dwSettling )
{
uint32_t dwMr;
dwMr = pAFE->AFEC_MR;
dwMr &= (~AFEC_MR_STARTUP_Msk) & (~AFEC_MR_TRACKTIM_Msk) & (~AFEC_MR_SETTLING_Msk);
/* Formula:
* Startup Time = startup value / AFEClock
* Transfer Time = (TRANSFER * 2 + 3) / AFEClock
* Tracking Time = (TRACKTIM + 1) / AFEClock
* Settling Time = settling value / AFEClock
*/
dwMr |= dwStartup | dwTracking | dwSettling;
pAFE->AFEC_MR |= dwMr;
}
/**
* \brief Set AFE trigger.
*
* \param pAFE Pointer to an AFE instance.
* \param dwTrgSel Trigger selection
*/
extern void AFEC_SetTrigger( Afec* pAFE, uint32_t dwTrgSel )
{
uint32_t dwMr;
dwMr = pAFE->AFEC_MR;
dwMr &= ~AFEC_MR_TRGSEL_Msk;
dwMr |= dwTrgSel;
pAFE->AFEC_MR |= dwMr;
}
/**
* \brief Enable/Disable sleep mode.
*
* \param pAFE Pointer to an AFE instance.
* \param bEnDis Enable/Disable sleep mode.
*/
extern void AFEC_SetSleepMode( Afec *pAFE, uint8_t bEnDis )
{
if ( bEnDis ) {
pAFE->AFEC_MR |= AFEC_MR_SLEEP;
} else {
pAFE->AFEC_MR &= ~AFEC_MR_SLEEP;
}
}
/**
* \brief Enable/Disable fast wake up.
*
* \param pAFE Pointer to an AFE instance.
* \param bEnDis Enable/Disable fast wake up in sleep mode.
*/
extern void AFEC_SetFastWakeup( Afec *pAFE, uint8_t bEnDis )
{
if ( bEnDis ) {
pAFE->AFEC_MR |= AFEC_MR_FWUP;
} else {
pAFE->AFEC_MR &= ~AFEC_MR_FWUP;
}
}
/**
* \brief Enable/Disable sequence mode.
*
* \param pAFE Pointer to an AFE instance.
* \param bEnDis Enable/Disable sequence mode.
*/
extern void AFEC_SetSequenceMode( Afec *pAFE, uint8_t bEnDis )
{
if ( bEnDis ) {
/* User Sequence Mode: The sequence respects what is defined in
AFEC_SEQR1 and AFEC_SEQR2 */
pAFE->AFEC_MR |= AFEC_MR_USEQ;
} else {
/* Normal Mode: The controller converts channels in a simple
numeric order. */
pAFE->AFEC_MR &= ~AFEC_MR_USEQ;
}
}
/**
* \brief Set channel sequence.
*
* \param pAFE Pointer to an AFE instance.
* \param dwSEQ1 Sequence 1 ~ 8 channel number.
* \param dwSEQ2 Sequence 9 ~ 16 channel number.
*/
extern void AFEC_SetSequence( Afec *pAFE, uint32_t dwSEQ1, uint32_t dwSEQ2 )
{
pAFE->AFEC_SEQ1R = dwSEQ1;
pAFE->AFEC_SEQ2R = dwSEQ2;
}
/**
* \brief Set channel sequence by given channel list.
*
* \param pAFE Pointer to an AFE instance.
* \param ucChList Channel list.
* \param ucNumCh Number of channels in list.
*/
extern void AFEC_SetSequenceByList( Afec *pAFE, uint8_t ucChList[], uint8_t ucNumCh )
{
uint8_t i;
uint8_t ucShift;
pAFE->AFEC_SEQ1R = 0;
for (i = 0, ucShift = 0; i < 8; i ++, ucShift += 4) {
if (i >= ucNumCh) return;
pAFE->AFEC_SEQ1R |= ucChList[i] << ucShift;
}
pAFE->AFEC_SEQ2R = 0;
for (ucShift = 0; i < 16; i ++, ucShift += 4) {
if (i >= ucNumCh) return;
pAFE->AFEC_SEQ2R |= ucChList[i] << ucShift;
}
}
/**
* \brief Set analog change.
* IF enabled, it allows different analog settings for each channel,
* otherwise, DIFF0, GAIN0 and OFF0 are used for all channels.
*
* \param pAFE Pointer to an AFE instance.
* \param bEnDis Enable/Disable.
*/
extern void AFEC_SetAnalogChange( Afec* pAFE, uint8_t bEnDis )
{
if ( bEnDis ) {
pAFE->AFEC_MR |= AFEC_MR_ONE;
} else {
pAFE->AFEC_MR &= ~AFEC_MR_ONE;
}
}
/**
* \brief Set "TAG" mode, show channel number in last data or not.
*
* \param pAFE Pointer to an AFE instance.
* \param bEnDis Enable/Disable TAG value.
*/
extern void AFEC_SetTagEnable( Afec *pAFE, uint8_t bEnDis )
{
if ( bEnDis ) {
pAFE->AFEC_EMR |= AFEC_EMR_TAG;
} else {
pAFE->AFEC_EMR &= ~AFEC_EMR_TAG;
}
}
/**
* \brief Set compare channel.
*
* \param pAFE Pointer to an AFE instance.
* \param dwChannel channel number to be set,16 for all channels
*/
extern void AFEC_SetCompareChannel( Afec* pAFE, uint32_t dwChannel )
{
assert( dwChannel <= 16 ) ;
if ( dwChannel < 16 ) {
pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPALL);
pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPSEL_Msk);
pAFE->AFEC_EMR |= (dwChannel << AFEC_EMR_CMPSEL_Pos);
} else {
pAFE->AFEC_EMR |= AFEC_EMR_CMPALL;
}
}
/**
* \brief Set compare mode.
*
* \param pAFE Pointer to an AFE instance.
* \param dwMode compare mode
*/
extern void AFEC_SetCompareMode( Afec* pAFE, uint32_t dwMode )
{
pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPMODE_Msk);
pAFE->AFEC_EMR |= (dwMode & AFEC_EMR_CMPMODE_Msk);
}
/**
* \brief Set comparison window.
*
* \param pAFE Pointer to an AFE instance.
* \param dwHi_Lo Comparison Window
*/
extern void AFEC_SetComparisonWindow( Afec* pAFE, uint32_t dwHi_Lo )
{
pAFE->AFEC_CWR = dwHi_Lo ;
}
/**
* \brief Return the Channel Converted Data
*
* \param pAFE Pointer to an AFE instance.
* \param dwChannel channel to get converted value
*/
extern uint32_t AFEC_GetConvertedData( Afec* pAFE, uint32_t dwChannel )
{
uint32_t dwData = 0;
assert( dwChannel < 12 ) ;
pAFE->AFEC_CSELR = dwChannel;
dwData = pAFE->AFEC_CDR;
return dwData ;
}
/**
* Sets the AFE startup time.
* \param pAFE Pointer to an AFE instance.
* \param dwUs Startup time in uS.
*/
void AFEC_SetStartupTime( Afec *pAFE, uint32_t dwUs )
{
uint32_t dwStart;
uint32_t dwMr;
if (dwAFEClock == 0) return;
/* Formula for STARTUP is:
STARTUP = (time x AFECLK) / (1000000) - 1
Division multiplied by 10 for higher precision */
dwStart = (dwUs * dwAFEClock) / (100000);
if (dwStart % 10) dwStart /= 10;
else
{
dwStart /= 10;
if (dwStart) dwStart --;
}
if (dwStart > 896) dwMr = AFEC_MR_STARTUP_SUT960;
else if (dwStart > 832) dwMr = AFEC_MR_STARTUP_SUT896;
else if (dwStart > 768) dwMr = AFEC_MR_STARTUP_SUT832;
else if (dwStart > 704) dwMr = AFEC_MR_STARTUP_SUT768;
else if (dwStart > 640) dwMr = AFEC_MR_STARTUP_SUT704;
else if (dwStart > 576) dwMr = AFEC_MR_STARTUP_SUT640;
else if (dwStart > 512) dwMr = AFEC_MR_STARTUP_SUT576;
else if (dwStart > 112) dwMr = AFEC_MR_STARTUP_SUT512;
else if (dwStart > 96) dwMr = AFEC_MR_STARTUP_SUT112;
else if (dwStart > 80) dwMr = AFEC_MR_STARTUP_SUT96;
else if (dwStart > 64) dwMr = AFEC_MR_STARTUP_SUT80;
else if (dwStart > 24) dwMr = AFEC_MR_STARTUP_SUT64;
else if (dwStart > 16) dwMr = AFEC_MR_STARTUP_SUT24;
else if (dwStart > 8) dwMr = AFEC_MR_STARTUP_SUT16;
else if (dwStart > 0) dwMr = AFEC_MR_STARTUP_SUT8;
else dwMr = AFEC_MR_STARTUP_SUT0;
dwMr |= pAFE->AFEC_MR & ~AFEC_MR_STARTUP_Msk;
pAFE->AFEC_MR = dwMr;
}
/**
* Set AFE tracking time
* \param pAFE Pointer to an AFE instance.
* \param dwNs Tracking time in nS.
*/
void AFEC_SetTrackingTime( Afec *pAFE, uint32_t dwNs )
{
uint32_t dwShtim;
uint32_t dwMr;
if (dwAFEClock == 0) return;
/* Formula for SHTIM is:
SHTIM = (time x AFECLK) / (1000000000) - 1
Since 1 billion is close to the maximum value for an integer, we first
divide AFECLK by 1000 to avoid an overflow */
dwShtim = (dwNs * (dwAFEClock / 1000)) / 100000;
if (dwShtim % 10) dwShtim /= 10;
else
{
dwShtim /= 10;
if (dwShtim) dwShtim --;
}
dwMr = AFEC_MR_TRACKTIM(dwShtim);
dwMr |= pAFE->AFEC_MR & ~AFEC_MR_TRACKTIM_Msk;
pAFE->AFEC_MR = dwMr;
}
/**
* \brief Set analog offset to be used for channel CSEL.
*
* \param afec Base address of the AFEC.
* \param dwChannel AFEC channel number.
* \param aoffset Analog offset value.
*/
void AFEC_SetAnalogOffset( Afec *pAFE, uint32_t dwChannel,uint32_t aoffset )
{
assert( dwChannel < 12 ) ;
pAFE->AFEC_CSELR = dwChannel;
pAFE->AFEC_COCR = (aoffset & AFEC_COCR_AOFF_Msk);;
}
/**
* \brief Set analog offset to be used for channel CSEL.
*
* \param afec Base address of the AFEC.
* \param control Analog control value.
*/
void AFEC_SetAnalogControl( Afec *pAFE, uint32_t control)
{
pAFE->AFEC_ACR = control;
}