/* ---------------------------------------------------------------------------- */ /* Atmel Microcontroller Software Support */ /* SAM Software Package License */ /* ---------------------------------------------------------------------------- */ /* Copyright (c) 2015, Atmel Corporation */ /* */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following condition is 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: * * * 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; }