/** * @file ad5940.c * @brief AD5940 library. This file contains all AD5940 library functions. * @author ADI * @date March 2019 * @par Revision History: * * Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved. * * This software is proprietary to Analog Devices, Inc. and its licensors. * By using this software you agree to the terms of the associated * Analog Devices Software License Agreement. **/ #include "ad5940.h" /*! \mainpage AD5940 Library Introduction * * ![AD5940 EVAL Board](https://www.analog.com/-/media/analog/en/evaluation-board-images/images/eval-ad5940elcztop-web.gif?h=500&thn=1&hash=1F38F7CC1002894616F74D316365C0A2631C432B "ADI logo") * * # Introduction * * The documentation is for AD594x library and examples. * * # Manual Structure * * @ref AD5940_Library * - @ref AD5940_Functions * - @ref TypeDefinitions * @ref AD5940_Standard_Examples * @ref AD5940_System_Examples * * # How to Use It * We provide examples that can directly run out of box. * The files can generally be separated to three parts: * - AD5940 Library files. ad5940.c and ad5940.h specifically. These two files are shared among all examples. * - AD5940 System Examples. The system examples mean system level application like measuring impedance. * - Standard examples. These include basic block level examples like ADC. It shows how to setup and use one specific block. * * ## Requirements to run these examples * ### Hardware * - Use EVAL_AD5940 or EVAL_AD5941. The default MCU board we used is ADICUP3029. We also provide project for ST NUCLEO board. * - Or use EVAL_ADuCM355 * ### Software * - Pull all the source file from [GitHub](https://github.com/analogdevicesinc/ad5940-examples.git) * - CMSIS pack that related to specific MCU. This normally is done by IDE you use. * * ## Materials * Please use this library together with following materials. * - [AD5940 Data Sheet](https://www.analog.com/media/en/technical-documentation/data-sheets/AD5940.pdf) * - [AD5940 Eval Board](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/EVAL-AD5940.html) * */ /* Remove below variables after AD594x is released. */ static BoolFlag bIsS2silicon = bFALSE; /* Declare of SPI functions used to read/write registers */ #ifndef CHIPSEL_M355 static uint32_t AD5940_SPIReadReg(uint16_t RegAddr); static void AD5940_SPIWriteReg(uint16_t RegAddr, uint32_t RegData); #else static uint32_t AD5940_D2DReadReg(uint16_t RegAddr); static void AD5940_D2DWriteReg(uint16_t RegAddr, uint32_t RegData); #endif /** * @addtogroup AD5940_Library * The library functions, structures and constants. * @{ * @defgroup AD5940_Functions * @{ * @defgroup Function_Helpers * @brief The functions with no hardware access. They are helpers. * @{ * @defgroup Sequencer_Generator_Functions * @brief The set of function used to track all register read and write once it's enabled. It can translate register write operation to sequencer commands. * @{ */ #define SEQUENCE_GENERATOR /*!< Build sequence generator part in to lib. Comment this line to remove this feature */ #ifdef SEQUENCE_GENERATOR /** * Structure used to store register information(address and its data) * */ typedef struct { uint32_t RegAddr :8; /**< 8bit address is enough for sequencer */ uint32_t RegValue :24; /**< Reg data is limited to 24bit by sequencer */ }SEQGenRegInfo_Type; /** * Sequencer generator data base. */ struct { BoolFlag EngineStart; /**< Flag to mark start of the generator */ uint32_t BufferSize; /**< Total buffer size */ uint32_t *pSeqBuff; /**< The buffer for sequence generator(both sequences and RegInfo) */ uint32_t SeqLen; /**< Generated sequence length till now */ SEQGenRegInfo_Type *pRegInfo; /**< Pointer to buffer where stores register info */ uint32_t RegCount; /**< The count of register info available in buffer *pRegInfo. */ AD5940Err LastError; /**< The last error message. */ }SeqGenDB; /* Data base of Seq Generator */ /** * @brief Manually input a command to sequencer generator. * @param CmdWord: The 32-bit width sequencer command word. @ref Sequencer_Helper can be used to generate commands. * @return None; */ void AD5940_SEQGenInsert(uint32_t CmdWord) { uint32_t temp; temp = SeqGenDB.RegCount + SeqGenDB.SeqLen; /* Generate Sequence command */ if(temp < SeqGenDB.BufferSize) { SeqGenDB.pSeqBuff[SeqGenDB.SeqLen] = CmdWord; SeqGenDB.SeqLen ++; } else /* There is no buffer */ SeqGenDB.LastError = AD5940ERR_BUFF; } /** * @brief Search data-base to get current register value. * @param RegAddr: The register address. * @param pIndex: Pointer to a variable that used to store index of found register-info. * @return Return AD5940ERR_OK if register found in data-base. Otherwise return AD5940ERR_SEQREG. */ static AD5940Err AD5940_SEQGenSearchReg(uint32_t RegAddr, uint32_t *pIndex) { uint32_t i; RegAddr = (RegAddr>>2)&0xff; for(i=0;i>2)&0xff; SeqGenDB.pRegInfo[0].RegValue = RegData&0x00ffffff; SeqGenDB.RegCount ++; } else /* There is no more buffer */ { SeqGenDB.LastError = AD5940ERR_BUFF; } } /** * @brief Get current register value. If we have record in data-base, read it. Otherwise, return the register default value. * @param RegAddr: The register address. * @return Return register value. */ static uint32_t AD5940_SEQReadReg(uint16_t RegAddr) { uint32_t RegIndex, RegData; if(AD5940_SEQGenSearchReg(RegAddr, &RegIndex) != AD5940ERR_OK) { /* There is no record in data-base, read the default value. */ AD5940_SEQGenGetRegDefault(RegAddr, &RegData); AD5940_SEQRegInfoInsert(RegAddr, RegData); } else { /* return the current register value stored in data-base */ RegData = SeqGenDB.pRegInfo[RegIndex].RegValue; } return RegData; } /** * @brief Generate a sequencer command to write register. If the register address is out of range, it won't generate a command. * This function will also update the register-info in data-base to record current register value. * @param RegAddr: The register address. * @param RegData: The register value. * @return Return None. */ static void AD5940_SEQWriteReg(uint16_t RegAddr, uint32_t RegData) { uint32_t RegIndex; if(RegAddr > 0x21ff) { SeqGenDB.LastError = AD5940ERR_ADDROR; /* address out of range */ return; } if(AD5940_SEQGenSearchReg(RegAddr, &RegIndex) == AD5940ERR_OK) { /* Store register value */ SeqGenDB.pRegInfo[RegIndex].RegValue = RegData; /* Generate Sequence command */ AD5940_SEQGenInsert(SEQ_WR(RegAddr, RegData)); } else { AD5940_SEQRegInfoInsert(RegAddr, RegData); /* Generate Sequence command */ AD5940_SEQGenInsert(SEQ_WR(RegAddr, RegData)); } } /** * @brief Initialize sequencer generator with specified buffer. * The buffer is used to store sequencer generated and record register value changes. * The command is stored from start address of buffer while register value is stored from end of buffer. * Buffer[0] : First sequencer command; * Buffer[1] : Second Sequencer command; * ... * Buffer[Last-1]: The second register value record. * Buffer[Last]: The first register value record. * @param pBuffer: Pointer to the buffer. * @param BufferSize: The buffer length. * @return Return None. */ void AD5940_SEQGenInit(uint32_t *pBuffer, uint32_t BufferSize) { if(BufferSize < 2) return; SeqGenDB.BufferSize = BufferSize; SeqGenDB.pSeqBuff = pBuffer; SeqGenDB.pRegInfo = (SEQGenRegInfo_Type*)pBuffer + BufferSize - 1; /* Point to the last element in buffer */ SeqGenDB.SeqLen = 0; SeqGenDB.RegCount = 0; SeqGenDB.LastError = AD5940ERR_OK; SeqGenDB.EngineStart = bFALSE; } /** * @brief Get sequencer command generated. * @param ppSeqCmd: Pointer to a variable(pointer) used to store the pointer to generated sequencer command. * @param pSeqLen: Pointer to a variable that used to store how many commands available in buffer. * @return Return lasterror. */ AD5940Err AD5940_SEQGenFetchSeq(const uint32_t **ppSeqCmd, uint32_t *pSeqLen) { AD5940Err lasterror; if(ppSeqCmd) *ppSeqCmd = SeqGenDB.pSeqBuff; if(pSeqLen) *pSeqLen = SeqGenDB.SeqLen; //SeqGenDB.SeqLen = 0; /* Start a new sequence */ lasterror = SeqGenDB.LastError; //SeqGenDB.LastError = AD5940ERR_OK; /* Clear error message */ return lasterror; } /** * @brief Start or stop the sequencer generator. Once started, the register write will be recorded to sequencer generator. * Once it's disabled, the register write is written to AD5940 directly by SPI bus. * @param bFlag: Enable or disable sequencer generator. * @return Return None. */ void AD5940_SEQGenCtrl(BoolFlag bFlag) { if(bFlag == bFALSE) /* Disable sequence generator */ { SeqGenDB.EngineStart = bFALSE; } else { SeqGenDB.SeqLen = 0; SeqGenDB.LastError = AD5940ERR_OK; /* Clear error message */ SeqGenDB.EngineStart = bTRUE; } } /** * @brief Calculate the number of cycles in the sequence * @return Return Number of ACLK Cycles that a generated sequence will take. */ uint32_t AD5940_SEQCycleTime(void) { uint32_t i, Cycles, Cmd; Cycles = 0; for(i=0;i> 30) & 0x3; if (Cmd & 0x2) { /* A write command */ Cycles += 1; } else { if (Cmd & 0x1) { /* Timeout Command */ Cycles += 1; } else { /* Wait command */ Cycles += SeqGenDB.pSeqBuff[i] & 0x3FFFFFFF; } } } return Cycles; } #endif /** * @} Sequencer_Generator_Functions */ /** * Check if an uint8_t value exist in table. */ static int32_t _is_value_in_table(uint8_t value, const uint8_t *table, uint8_t len, uint8_t *index) { for(int i=0; iADCRate == ADCRATE_800KHZ && pFilterInfo->ADCSinc3Osr == ADCSINC3OSR_2)||\ (pFilterInfo->ADCRate == ADCRATE_1P6MHZ && pFilterInfo->ADCSinc3Osr != ADCSINC3OSR_2)) { //this combination suits for filter: //SINC3 OSR2, for 800kSPS //and SINC3 OSR4 and OSR5 for 1.6MSPS, const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_533, ADCSINC2OSR_667,ADCSINC2OSR_800, ADCSINC2OSR_889, ADCSINC2OSR_1333}; const uint8_t dl_50Hz[] = {15,12,10,9,6}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_50Hz[index]; return bTRUE; } } else if(pFilterInfo->ADCRate == ADCRATE_1P6MHZ && pFilterInfo->ADCSinc3Osr == ADCSINC3OSR_2) { //this combination suits for filter: //SINC3 OSR2 for 1.6MSPS const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_889, ADCSINC2OSR_1067, ADCSINC2OSR_1333}; const uint8_t dl_50Hz[] = {18,15,12}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_50Hz[index]; return bTRUE; } } else if(pFilterInfo->ADCRate == ADCRATE_800KHZ && pFilterInfo->ADCSinc3Osr != ADCSINC3OSR_2) { //this combination suits for filter: //SINC3 OSR4 and OSR5 for 800kSPS, const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_178, ADCSINC2OSR_267, ADCSINC2OSR_533, ADCSINC2OSR_640,\ ADCSINC2OSR_800, ADCSINC2OSR_1067}; const uint8_t dl_50Hz[] = {18,12,6,5,4,3}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_50Hz[index]; return bTRUE; } } *dl = 0; return bFALSE; } /** * @brief return if the SINC3/SINC2 combination is available for notch 60Hz filter. * If it's not availabe, hardware automatically bypass Notch even if it's enabled. * @param pFilterInfo the filter configuration, need sinc2/sinc3 osr and adc data rate information. * @return return bTRUE if notch 60Hz filter is available. */ BoolFlag AD5940_Notch60HzAvailable(ADCFilterCfg_Type *pFilterInfo, uint8_t *dl) { if((pFilterInfo->ADCRate == ADCRATE_800KHZ && pFilterInfo->ADCSinc3Osr == ADCSINC3OSR_2)||\ (pFilterInfo->ADCRate == ADCRATE_1P6MHZ && pFilterInfo->ADCSinc3Osr != ADCSINC3OSR_2)) { //this combination suits for filter: //SINC3 OSR2, for 800kSPS //and SINC3 OSR4 and OSR5 for 1.6MSPS, const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_667, ADCSINC2OSR_1333}; const uint8_t dl_60Hz[] = {10,5}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_60Hz[index]; return bTRUE; } } else if(pFilterInfo->ADCRate == ADCRATE_1P6MHZ && pFilterInfo->ADCSinc3Osr == ADCSINC3OSR_2) { //this combination suits for filter: //SINC3 OSR2 for 1.6MSPS const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_889, ADCSINC2OSR_1333}; const uint8_t dl_60Hz[] = {15,10}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_60Hz[index]; return bTRUE; } } else if(pFilterInfo->ADCRate == ADCRATE_800KHZ && pFilterInfo->ADCSinc3Osr != ADCSINC3OSR_2) { //this combination suits for filter: //SINC3 OSR4 and OSR5 for 800kSPS, const uint8_t available_sinc2_osr[] = {ADCSINC2OSR_178, ADCSINC2OSR_267, ADCSINC2OSR_533, ADCSINC2OSR_667,\ ADCSINC2OSR_889, ADCSINC2OSR_1333}; const uint8_t dl_60Hz[] = {15,10,5,4,3,2}; uint8_t index; if(_is_value_in_table(pFilterInfo->ADCSinc2Osr, available_sinc2_osr, sizeof(available_sinc2_osr), &index)) { *dl = dl_60Hz[index]; return bTRUE; } } *dl = 0; return bFALSE; } /** * @brief Calculate how many clocks are needed in sequencer wait command to generate required number of data from filter output. * @note When measurement is done, it's recommend to disable blocks like ADCPWR, ADCCNV, SINC2, DFT etc. If blocks remain powered up, * they may need less clocks to generate required number of output. Use function @ref AD5940_AFECtrlS to control these blocks. * @param pFilterInfo: Pointer to configuration structure. * @param pClocks: pointer used to store results. * @return return none. */ void AD5940_ClksCalculate(ClksCalInfo_Type *pFilterInfo, uint32_t *pClocks) { uint32_t temp = 0; const uint32_t sinc2osr_table[] = {22,44,89,178,267,533,640,667,800,889,1067,1333,0}; const uint32_t sinc3osr_table[] = {5,4,2,0}; *pClocks = 0; if(pFilterInfo == NULL) return; if(pClocks == NULL) return; if(pFilterInfo->ADCSinc2Osr > ADCSINC2OSR_1333) return; if(pFilterInfo->ADCSinc3Osr > 2) return; /* 0: OSR5, 1:OSR4, 2:OSR2 */ if(pFilterInfo->ADCAvgNum > ADCAVGNUM_16) return; /* Average number index:0,1,2,3 */ switch(pFilterInfo->DataType) { case DATATYPE_ADCRAW: temp = (uint32_t)(20*pFilterInfo->DataCount*pFilterInfo->RatioSys2AdcClk); break; case DATATYPE_SINC3: temp = (uint32_t)(((pFilterInfo->DataCount+2)*sinc3osr_table[pFilterInfo->ADCSinc3Osr]+1)*20*pFilterInfo->RatioSys2AdcClk + 0.5f); break; case DATATYPE_SINC2: temp = (pFilterInfo->DataCount+1)*sinc2osr_table[pFilterInfo->ADCSinc2Osr] + 1; pFilterInfo->DataType = DATATYPE_SINC3; pFilterInfo->DataCount = temp; AD5940_ClksCalculate(pFilterInfo, &temp); pFilterInfo->DataType = DATATYPE_SINC2; temp += 15; /* Need extra 15 clocks for FIFO etc. Just to be safe. */ break; case DATATYPE_NOTCH: { ADCFilterCfg_Type filter; filter.ADCRate = pFilterInfo->ADCRate; filter.ADCSinc3Osr = pFilterInfo->ADCSinc3Osr; filter.ADCSinc2Osr = pFilterInfo->ADCSinc2Osr; uint8_t dl=0, dl_50, dl_60; if(AD5940_Notch50HzAvailable(&filter, &dl_50)){ dl += dl_50 - 1; } if(AD5940_Notch60HzAvailable(&filter, &dl_60)){ dl += dl_60 - 1; } pFilterInfo->DataType = DATATYPE_SINC2; pFilterInfo->DataCount += dl; //DL is the extra data input needed for filter to output first data. AD5940_ClksCalculate(pFilterInfo,&temp); //restore the filter info. pFilterInfo->DataType = DATATYPE_NOTCH; pFilterInfo->DataCount -= dl; break; } case DATATYPE_DFT: switch(pFilterInfo->DftSrc) { case DFTSRC_ADCRAW: pFilterInfo->DataType = DATATYPE_ADCRAW; AD5940_ClksCalculate(pFilterInfo, &temp); break; case DFTSRC_SINC3: pFilterInfo->DataType = DATATYPE_SINC3; AD5940_ClksCalculate(pFilterInfo, &temp); break; case DFTSRC_SINC2NOTCH: if(pFilterInfo->BpNotch) pFilterInfo->DataType = DATATYPE_SINC2; else pFilterInfo->DataType = DATATYPE_NOTCH; AD5940_ClksCalculate(pFilterInfo, &temp); break; case DFTSRC_AVG: pFilterInfo->DataType = DATATYPE_SINC3; pFilterInfo->DataCount *= 1L<<(pFilterInfo->ADCAvgNum+1); /* 0: average2, 1: average4, 2: average8, 3: average16 */ AD5940_ClksCalculate(pFilterInfo, &temp); break; default: break; } pFilterInfo->DataType = DATATYPE_DFT; temp += 25; /* add margin */ break; default: break; } *pClocks = temp; } /** @brief void AD5940_SweepNext(SoftSweepCfg_Type *pSweepCfg, float *pNextFreq) For sweep function, calculate next frequency point according to pSweepCfg info. @return Return next frequency point in Hz. */ void AD5940_SweepNext(SoftSweepCfg_Type *pSweepCfg, float *pNextFreq) { float frequency; if(pSweepCfg->SweepLog)/* Log step */ { if(pSweepCfg->SweepStartSweepStop) /* Normal */ { if(++pSweepCfg->SweepIndex == pSweepCfg->SweepPoints) pSweepCfg->SweepIndex = 0; frequency = pSweepCfg->SweepStart*pow(10,pSweepCfg->SweepIndex*log10(pSweepCfg->SweepStop/pSweepCfg->SweepStart)/(pSweepCfg->SweepPoints-1)); } else { pSweepCfg->SweepIndex --; if(pSweepCfg->SweepIndex >= pSweepCfg->SweepPoints) pSweepCfg->SweepIndex = pSweepCfg->SweepPoints-1; frequency = pSweepCfg->SweepStop*pow(10,pSweepCfg->SweepIndex* (log10(pSweepCfg->SweepStart/pSweepCfg->SweepStop)/(pSweepCfg->SweepPoints-1))); } } else/* Linear step */ { if(pSweepCfg->SweepStartSweepStop) /* Normal */ { if(++pSweepCfg->SweepIndex == pSweepCfg->SweepPoints) pSweepCfg->SweepIndex = 0; frequency = pSweepCfg->SweepStart + pSweepCfg->SweepIndex*(double)(pSweepCfg->SweepStop-pSweepCfg->SweepStart)/(pSweepCfg->SweepPoints-1); } else { pSweepCfg->SweepIndex --; if(pSweepCfg->SweepIndex >= pSweepCfg->SweepPoints) pSweepCfg->SweepIndex = pSweepCfg->SweepPoints-1; frequency = pSweepCfg->SweepStop + pSweepCfg->SweepIndex*(double)(pSweepCfg->SweepStart - pSweepCfg->SweepStop)/(pSweepCfg->SweepPoints-1); } } *pNextFreq = frequency; } /** @brief Initialize Structure members to zero @param pStruct: Pointer to the structure. @param StructSize: The structure size in Byte. @return Return None. **/ void AD5940_StructInit(void *pStruct, uint32_t StructSize) { memset(pStruct, 0, StructSize); } /** @brief Convert ADC Code to voltage. @param ADCPga: The ADC PGA used for this result. @param code: ADC code. @param VRef1p82: the actual 1.82V reference voltage. @return Voltage in volt. **/ float AD5940_ADCCode2Volt(uint32_t code, uint32_t ADCPga, float VRef1p82) { float kFactor = 1.835/1.82; float fVolt = 0.0; float tmp = 0; tmp = (int32_t)code - 32768; switch(ADCPga) { case ADCPGA_1: break; case ADCPGA_1P5: tmp /= 1.5f; break; case ADCPGA_2: tmp /= 2.0f; break; case ADCPGA_4: tmp /= 4.0f; break; case ADCPGA_9: tmp /= 9.0f; break; default:break; } fVolt = tmp*VRef1p82/32768*kFactor; return fVolt; } /** * @brief Do complex number division. * @param a: The dividend. * @param b: The divisor. * @return Return result. **/ fImpCar_Type AD5940_ComplexDivFloat(fImpCar_Type *a, fImpCar_Type *b) { fImpCar_Type res; float temp; temp = b->Real*b->Real + b->Image*b->Image; res.Real = a->Real*b->Real + a->Image*b->Image; res.Real /= temp; res.Image = a->Image*b->Real - a->Real*b->Image; res.Image /= temp; return res; } /** * @brief Do complex number multiplication. * @param a: The multiplicand. * @param b: The multiplier . * @return Return result. **/ fImpCar_Type AD5940_ComplexMulFloat(fImpCar_Type *a, fImpCar_Type *b) { fImpCar_Type res; res.Real = a->Real*b->Real - a->Image*b->Image; res.Image = a->Image*b->Real + a->Real*b->Image; return res; } /** * @brief Do complex number addition. * @param a: The addend. * @param b: The addend . * @return Return result. **/ fImpCar_Type AD5940_ComplexAddFloat(fImpCar_Type *a, fImpCar_Type *b) { fImpCar_Type res; res.Real = a->Real + b->Real; res.Image = a->Image + b->Image; return res; } /** * @brief Do complex number subtraction. * @param a: The minuend. * @param b: The subtrahend . * @return Return result. **/ fImpCar_Type AD5940_ComplexSubFloat(fImpCar_Type *a, fImpCar_Type *b) { fImpCar_Type res; res.Real = a->Real - b->Real; res.Image = a->Image - b->Image; return res; } /** * @brief Do complex number division. * @param a: The dividend. * @param b: The divisor. * @return Return result. **/ fImpCar_Type AD5940_ComplexDivInt(iImpCar_Type *a, iImpCar_Type *b) { fImpCar_Type res; float temp; temp = (float)b->Real*b->Real + (float)b->Image*b->Image; res.Real = (float)a->Real*b->Real + (float)a->Image*b->Image; res.Real /= temp; res.Image = (float)a->Image*b->Real - (float)a->Real*b->Image; res.Image /= temp; return res; } /** * @brief Do complex number multiplication. * @param a: The multiplicand. * @param b: The multiplier . * @return Return result. **/ fImpCar_Type AD5940_ComplexMulInt(iImpCar_Type *a, iImpCar_Type *b) { fImpCar_Type res; res.Real = (float)a->Real*b->Real - (float)a->Image*b->Image; res.Image = (float)a->Image*b->Real + (float)a->Real*b->Image; return res; } /** * @brief Calculate the complex number magnitude. * @param a: The complex number. * @return Return magnitude. **/ float AD5940_ComplexMag(fImpCar_Type *a) { return sqrt(a->Real*a->Real + a->Image*a->Image); } /** * @brief Calculate the complex number phase. * @param a: The complex number. * @return Return phase. **/ float AD5940_ComplexPhase(fImpCar_Type *a) { return atan2(a->Image, a->Real); } /** * @brief Calculate the optimum filter settings based on signal frequency. * @param freq: Frequency of signalr. * @return Return FreqParams. **/ FreqParams_Type AD5940_GetFreqParameters(float freq) { const uint32_t dft_table[] = {4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}; const uint32_t sinc2osr_table[] = {1, 22,44,89,178,267,533,640,667,800,889,1067,1333}; const uint32_t sinc3osr_table[] = {2, 4, 5}; float AdcRate = 800000; uint32_t n1 = 0; // Sample rate after ADC filters uint32_t n2 = 0; // Sample rate after DFT block uint32_t iCycle = 0; FreqParams_Type freq_params; /* High power mode */ if(freq >= 20000) { freq_params. DftSrc = DFTSRC_SINC3; freq_params.ADCSinc2Osr = 0; freq_params.ADCSinc3Osr = 2; freq_params.DftNum = DFTNUM_8192; freq_params.NumClks = 0; freq_params.HighPwrMode = bTRUE; return freq_params; } if(freq < 0.51) { freq_params. DftSrc = DFTSRC_SINC2NOTCH; freq_params.ADCSinc2Osr = 6; freq_params.ADCSinc3Osr = 1; freq_params.DftNum = DFTNUM_8192; freq_params.NumClks = 0; freq_params.HighPwrMode = bTRUE; return freq_params; } /* Start with SINC2 setting */ for(uint8_t i = 0; i=0x1000)&&(RegAddr<=0x3014))) /* 32bit register */ *(volatile uint32_t *)(RegAddr+0x400c0000) = RegData; else /* 16bit register */ *(volatile uint16_t *)(RegAddr+0x400c0000) = RegData; } static uint32_t AD5940_D2DReadReg(uint16_t RegAddr) { if(((RegAddr>=0x1000)&&(RegAddr<=0x3014))) /* 32bit register */ return *(volatile uint32_t *)(RegAddr+0x400c0000); else /* 16bit register */ return *(volatile uint16_t *)(RegAddr+0x400c0000); } void AD5940_FIFORd(uint32_t *pBuffer, uint32_t uiReadCount) { while(uiReadCount--) *pBuffer++ = *(volatile uint32_t *)(0x400c206C); } #else /** * @defgroup SPI_Block * @brief Functions to communicate with AD5940 registers following AD5940 SPI protocols * @{ * * @defgroup SPI_Block_Functions * @brief The basic SPI protocols. All functions are basic on AD5940_ReadWriteNBytes which * provided by user. * * ##SPI basic protocol * All SPI protocol starts with one-byte command word. Following are data(16B or 32B) * There are four SPI commands available @ref SPI_Block_Const. * @{ */ /** @brief Using SPI to transmit one byte and return the received byte. @param data: The 8-bit data SPI will transmit. @return received data. **/ static unsigned char AD5940_ReadWrite8B(unsigned char data) { uint8_t tx[1], rx[1]; tx[0] = data; AD5940_ReadWriteNBytes(tx,rx,1); return rx[0]; } /** @brief Using SPI to transmit two bytes and return the received bytes. @param data: The 16-bit data SPI will transmit. @return received data. **/ static uint16_t AD5940_ReadWrite16B(uint16_t data) { uint8_t SendBuffer[2]; uint8_t RecvBuffer[2]; SendBuffer[0] = data>>8; SendBuffer[1] = data&0xff; AD5940_ReadWriteNBytes(SendBuffer,RecvBuffer,2); return (((uint16_t)RecvBuffer[0])<<8)|RecvBuffer[1]; } /** * @brief Using SPI to transmit four bytes and return the received bytes. * @param data: The 32-bit data SPI will transmit. * @return received data. **/ static uint32_t AD5940_ReadWrite32B(uint32_t data) { uint8_t SendBuffer[4]; uint8_t RecvBuffer[4]; SendBuffer[0] = (data>>24)&0xff; SendBuffer[1] = (data>>16)&0xff; SendBuffer[2] = (data>> 8)&0xff; SendBuffer[3] = (data )&0xff; AD5940_ReadWriteNBytes(SendBuffer,RecvBuffer,4); return (((uint32_t)RecvBuffer[0])<<24)|(((uint32_t)RecvBuffer[1])<<16)|(((uint32_t)RecvBuffer[2])<<8)|RecvBuffer[3]; } /** * @brief Write register through SPI. * @param RegAddr: The register address. * @param RegData: The register data. * @return Return None. **/ static void AD5940_SPIWriteReg(uint16_t RegAddr, uint32_t RegData) { /* Set register address */ AD5940_CsClr(); AD5940_ReadWrite8B(SPICMD_SETADDR); AD5940_ReadWrite16B(RegAddr); AD5940_CsSet(); /* Add delay here to meet the SPI timing. */ AD5940_CsClr(); AD5940_ReadWrite8B(SPICMD_WRITEREG); if(((RegAddr>=0x1000)&&(RegAddr<=0x3014))) AD5940_ReadWrite32B(RegData); else AD5940_ReadWrite16B(RegData); AD5940_CsSet(); } /** * @brief Read register through SPI. * @param RegAddr: The register address. * @return Return register data. **/ static uint32_t AD5940_SPIReadReg(uint16_t RegAddr) { uint32_t Data = 0; /* Set register address that we want to read */ AD5940_CsClr(); AD5940_ReadWrite8B(SPICMD_SETADDR); AD5940_ReadWrite16B(RegAddr); AD5940_CsSet(); /* Read it */ AD5940_CsClr(); AD5940_ReadWrite8B(SPICMD_READREG); AD5940_ReadWrite8B(0); //Dummy read /* The real data is coming */ if((RegAddr>=0x1000)&&(RegAddr<=0x3014)) Data = AD5940_ReadWrite32B(0); else Data = AD5940_ReadWrite16B(0); AD5940_CsSet(); return Data; } /** @brief Read specific number of data from FIFO with optimized SPI access. @param pBuffer: Pointer to a buffer that used to store data read back. @param uiReadCount: How much data to be read. @return none. **/ void AD5940_FIFORd(uint32_t *pBuffer, uint32_t uiReadCount) { /* Use function AD5940_SPIReadReg to read REG_AFE_DATAFIFORD is also one method. */ uint32_t i; if(uiReadCount < 3) { /* This method is more efficient when readcount < 3 */ uint32_t i; AD5940_CsClr(); AD5940_ReadWrite8B(SPICMD_SETADDR); AD5940_ReadWrite16B(REG_AFE_DATAFIFORD); AD5940_CsSet(); for(i=0;iHpBandgapEn == bFALSE) tempreg |= BITM_AFE_AFECON_HPREFDIS; AD5940_WriteReg(REG_AFE_AFECON, tempreg); /* Reference buffer configure */ tempreg = AD5940_ReadReg(REG_AFE_BUFSENCON); if(pBufCfg->Hp1V8BuffEn == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P8HPADCEN; if(pBufCfg->Hp1V1BuffEn == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P1HPADCEN; if(pBufCfg->Lp1V8BuffEn == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P8LPADCEN; if(pBufCfg->Lp1V1BuffEn == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P1LPADCEN; if(pBufCfg->Hp1V8ThemBuff == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P8THERMSTEN; if(pBufCfg->Hp1V8Ilimit == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P8HPADCILIMITEN; if(pBufCfg->Disc1V8Cap == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P8HPADCCHGDIS; if(pBufCfg->Disc1V1Cap == bTRUE) tempreg |= BITM_AFE_BUFSENCON_V1P1LPADCCHGDIS; AD5940_WriteReg(REG_AFE_BUFSENCON, tempreg); /* LPREFBUFCON */ tempreg = 0; if(pBufCfg->LpRefBufEn == bFALSE) tempreg |= BITM_AFE_LPREFBUFCON_LPBUF2P5DIS; if(pBufCfg->LpBandgapEn == bFALSE) tempreg |= BITM_AFE_LPREFBUFCON_LPREFDIS; if(pBufCfg->LpRefBoostEn == bTRUE) tempreg |= BITM_AFE_LPREFBUFCON_BOOSTCURRENT; AD5940_WriteReg(REG_AFE_LPREFBUFCON, tempreg); } /** * @} End of AFE_Control_Functions * @} End of AFE_Control * */ /** * @defgroup High_Speed_Loop * @brief The high speed loop * @{ * @defgroup High_Speed_Loop_Functions * @{ */ /** @brief Configure High speed loop(high bandwidth loop or called excitation loop). This configuration includes HSDAC, HSTIA and Switch matrix. @param pHsLoopCfg : Pointer to configure structure; @return return none. */ void AD5940_HSLoopCfgS(HSLoopCfg_Type *pHsLoopCfg) { AD5940_HSDacCfgS(&pHsLoopCfg->HsDacCfg); AD5940_HSTIACfgS(&pHsLoopCfg->HsTiaCfg); AD5940_SWMatrixCfgS(&pHsLoopCfg->SWMatCfg); AD5940_WGCfgS(&pHsLoopCfg->WgCfg); } /** @brief Initialize switch matrix @param pSwMatrix: Pointer to configuration structure @return return none. */ void AD5940_SWMatrixCfgS(SWMatrixCfg_Type *pSwMatrix) { AD5940_WriteReg(REG_AFE_DSWFULLCON, pSwMatrix->Dswitch); AD5940_WriteReg(REG_AFE_PSWFULLCON, pSwMatrix->Pswitch); AD5940_WriteReg(REG_AFE_NSWFULLCON, pSwMatrix->Nswitch); AD5940_WriteReg(REG_AFE_TSWFULLCON, pSwMatrix->Tswitch); AD5940_WriteReg(REG_AFE_SWCON, BITM_AFE_SWCON_SWSOURCESEL); /* Update switch configuration */ } /** @brief Initialize HSDAC @param pHsDacCfg: Pointer to configuration structure @return return none. */ void AD5940_HSDacCfgS(HSDACCfg_Type *pHsDacCfg) { uint32_t tempreg; //Check parameters tempreg = 0; if(pHsDacCfg->ExcitBufGain == EXCITBUFGAIN_0P25) tempreg |= BITM_AFE_HSDACCON_INAMPGNMDE; /* Enable attenuator */ if(pHsDacCfg->HsDacGain == HSDACGAIN_0P2) tempreg |= BITM_AFE_HSDACCON_ATTENEN; /* Enable attenuator */ tempreg |= (pHsDacCfg->HsDacUpdateRate&0xff)<= HSTIADERTIA_OPEN) tempreg = 0x1f << 3; /* bit field HPTIRES03CON[7:3] */ else if(DeRtia >= HSTIADERTIA_1K) { tempreg = (DeRtia - 3 + 11) << 3; } else /* DERTIA 50/100/200Ohm */ { const uint8_t DeRtiaTable[3][5] = { //Rload 0 10 30 50 100 {0x00, 0x01, 0x02, 0x03, 0x06}, /* RTIA 50Ohm */ {0x03, 0x04, 0x05, 0x06, 0x07}, /* RTIA 100Ohm */ {0x07, 0x07, 0x09, 0x09, 0x0a}, /* RTIA 200Ohm */ }; if(DeRload < HSTIADERLOAD_OPEN) tempreg = (uint32_t)(DeRtiaTable[DeRtia][DeRload])<<3; else tempreg = (0x1f)<<3; /* Set it to HSTIADERTIA_OPEN. This setting is illegal */ } /* deal with HSTIA Rload */ tempreg |= DeRload; if(DExPin) //DE1 AD5940_WriteReg(REG_AFE_DE1RESCON, tempreg); else //DE0 AD5940_WriteReg(REG_AFE_DE0RESCON, tempreg); } /** @brief Initialize High speed TIA amplifier @param pHsTiaCfg: Pointer to configuration structure @return return none. */ AD5940Err AD5940_HSTIACfgS(HSTIACfg_Type *pHsTiaCfg) { uint32_t tempreg; //Check parameters if(pHsTiaCfg == NULL) return AD5940ERR_NULLP; /* Available parameter is 1k, 5k,...,160k, short, OPEN */ if(pHsTiaCfg->HstiaDeRtia < HSTIADERTIA_1K) return AD5940ERR_PARA; if(pHsTiaCfg->HstiaDeRtia > HSTIADERTIA_OPEN) return AD5940ERR_PARA; /* Parameter is invalid */ if(pHsTiaCfg->HstiaDeRload > HSTIADERLOAD_OPEN) return AD5940ERR_PARA; /* Available parameter is OPEN, 0R,..., 100R */ tempreg = 0; tempreg |= pHsTiaCfg->HstiaBias; AD5940_WriteReg(REG_AFE_HSTIACON, tempreg); /* HSRTIACON */ /* Calculate CTIA value */ tempreg = pHsTiaCfg->HstiaCtia << BITP_AFE_HSRTIACON_CTIACON; tempreg |= pHsTiaCfg->HstiaRtiaSel; if(pHsTiaCfg->DiodeClose == bTRUE) tempreg |= BITM_AFE_HSRTIACON_TIASW6CON; /* Close switch 6 */ AD5940_WriteReg(REG_AFE_HSRTIACON, tempreg); /* DExRESCON */ __AD5940_SetDExRTIA(0, pHsTiaCfg->HstiaDeRtia, pHsTiaCfg->HstiaDeRload); #ifdef CHIPSEL_M355 __AD5940_SetDExRTIA(1, pHsTiaCfg->HstiaDe1Rtia, pHsTiaCfg->HstiaDe1Rload); #endif /* Done */ return AD5940ERR_OK; } /** * @brief Configure HSTIA RTIA resistor and keep other parameters unchanged. * @param HSTIARtia: The RTIA setting, select it from @ref HSTIARTIA_Const * @return return none. */ void AD5940_HSRTIACfgS(uint32_t HSTIARtia) { uint32_t tempreg; tempreg = AD5940_ReadReg(REG_AFE_HSRTIACON); tempreg &= ~BITM_AFE_HSRTIACON_RTIACON; HSTIARtia &= BITM_AFE_HSRTIACON_RTIACON; tempreg |= HSTIARtia<WgType == WGTYPE_SIN) { /* Configure Sine wave Generator */ AD5940_WriteReg(REG_AFE_WGFCW, pWGInit->SinCfg.SinFreqWord); AD5940_WriteReg(REG_AFE_WGAMPLITUDE, pWGInit->SinCfg.SinAmplitudeWord); AD5940_WriteReg(REG_AFE_WGOFFSET, pWGInit->SinCfg.SinOffsetWord); AD5940_WriteReg(REG_AFE_WGPHASE, pWGInit->SinCfg.SinPhaseWord); } else if(pWGInit->WgType == WGTYPE_TRAPZ) { /* Configure Trapezoid Generator */ AD5940_WriteReg(REG_AFE_WGDCLEVEL1, pWGInit->TrapzCfg.WGTrapzDCLevel1); AD5940_WriteReg(REG_AFE_WGDCLEVEL2, pWGInit->TrapzCfg.WGTrapzDCLevel2); AD5940_WriteReg(REG_AFE_WGDELAY1, pWGInit->TrapzCfg.WGTrapzDelay1); AD5940_WriteReg(REG_AFE_WGDELAY2, pWGInit->TrapzCfg.WGTrapzDelay2); AD5940_WriteReg(REG_AFE_WGSLOPE1, pWGInit->TrapzCfg.WGTrapzSlope1); AD5940_WriteReg(REG_AFE_WGSLOPE2, pWGInit->TrapzCfg.WGTrapzSlope2); } else { /* Write DAC data. It's only have effect when WgType set to WGTYPE_MMR */ AD5940_WriteReg(REG_AFE_HSDACDAT, pWGInit->WgCode); } tempreg = 0; if(pWGInit->GainCalEn == bTRUE) tempreg |= BITM_AFE_WGCON_DACGAINCAL; if(pWGInit->OffsetCalEn == bTRUE) tempreg |= BITM_AFE_WGCON_DACOFFSETCAL; tempreg |= (pWGInit->WgType) << BITP_AFE_WGCON_TYPESEL; AD5940_WriteReg(REG_AFE_WGCON, tempreg); } /** * @brief Write HSDAC code directly when WG configured to MMR type * @param code: The 12-bit HSDAC code. * @return return none. */ AD5940Err AD5940_WGDACCodeS(uint32_t code) { code &= 0xfff; AD5940_WriteReg(REG_AFE_HSDACDAT, code); return AD5940ERR_OK; } /** * @brief Update WG SIN wave frequency in Hz. * @param SinFreqHz: The desired frequency in Hz. * @param WGClock: The clock for WG. It's same as system clock and the default value is internal 16MHz HSOSC. * @return return none. */ void AD5940_WGFreqCtrlS(float SinFreqHz, float WGClock) { uint32_t freq_word; freq_word = AD5940_WGFreqWordCal(SinFreqHz, WGClock); AD5940_WriteReg(REG_AFE_WGFCW, freq_word); } /** @brief Calculate sine wave generator frequency word. The maxim frequency is 250kHz-1LSB @param SinFreqHz : Target frequency in Hz unit. @param WGClock: Waveform generator clock frequency in Hz unit. The clock is sourced from system clock, default value is 16MHz HFOSC. @return return none. */ uint32_t AD5940_WGFreqWordCal(float SinFreqHz, float WGClock) { uint32_t temp; uint32_t __BITWIDTH_WGFCW = 26; if(bIsS2silicon == bTRUE) __BITWIDTH_WGFCW = 30; if(WGClock == 0) return 0; temp = (uint32_t)(SinFreqHz*(1LL<<__BITWIDTH_WGFCW)/WGClock + 0.5f); if(temp > ((__BITWIDTH_WGFCW == 26)?0xfffff:0xffffff)) temp = (__BITWIDTH_WGFCW == 26)?0xfffff:0xffffff; return temp; } /** * @} Waveform_Generator_Functions * @} High_Speed_Loop_Functions * @} High_Speed_Loop */ /** * @defgroup Low_Power_Loop * @brief The low power loop. * @{ * @defgroup Low_Power_Loop_Functions * @{ */ /** @brief Configure low power loop include LPDAC LPAmp(PA and TIA) @param pLpLoopCfg: Pointer to configure structure; @return return none. */ void AD5940_LPLoopCfgS(LPLoopCfg_Type *pLpLoopCfg) { AD5940_LPDACCfgS(&pLpLoopCfg->LpDacCfg); AD5940_LPAMPCfgS(&pLpLoopCfg->LpAmpCfg); } /** @brief Initialize LPDAC @param pLpDacCfg: Pointer to configuration structure @return return none. */ void AD5940_LPDACCfgS(LPDACCfg_Type *pLpDacCfg) { uint32_t tempreg; tempreg = 0; tempreg = (pLpDacCfg->LpDacSrc)<LpDacVzeroMux)<LpDacVbiasMux)<LpDacRef)<DataRst == bFALSE) tempreg |= BITM_AFE_LPDACCON0_RSTEN; if(pLpDacCfg->PowerEn == bFALSE) tempreg |= BITM_AFE_LPDACCON0_PWDEN; if(pLpDacCfg->LpdacSel == LPDAC0) { AD5940_WriteReg(REG_AFE_LPDACCON0, tempreg); AD5940_LPDAC0WriteS(pLpDacCfg->DacData12Bit, pLpDacCfg->DacData6Bit); AD5940_WriteReg(REG_AFE_LPDACSW0, pLpDacCfg->LpDacSW|BITM_AFE_LPDACSW0_LPMODEDIS); /* Overwrite LPDACSW settings. On Si1, this register is not accessible. */ } else { AD5940_WriteReg(REG_AFE_LPDACCON1, tempreg); AD5940_LPDAC1WriteS(pLpDacCfg->DacData12Bit, pLpDacCfg->DacData6Bit); AD5940_WriteReg(REG_AFE_LPDACSW1, pLpDacCfg->LpDacSW|BITM_AFE_LPDACSW0_LPMODEDIS); /* Overwrite LPDACSW settings. On Si1, this register is not accessible. */ } } /** @brief Write LPDAC data @param Data12Bit: 12Bit DAC data @param Data6Bit: 6Bit DAC data @return return none. */ void AD5940_LPDACWriteS(uint16_t Data12Bit, uint8_t Data6Bit) { /* Check parameter */ Data6Bit &= 0x3f; Data12Bit &= 0xfff; AD5940_WriteReg(REG_AFE_LPDACDAT0, ((uint32_t)Data6Bit<<12)|Data12Bit); } /** @brief Write LPDAC0 data @param Data12Bit: 12Bit DAC data @param Data6Bit: 6Bit DAC data @return return none. */ void AD5940_LPDAC0WriteS(uint16_t Data12Bit, uint8_t Data6Bit) { /* Check parameter */ Data6Bit &= 0x3f; Data12Bit &= 0xfff; AD5940_WriteReg(REG_AFE_LPDACDAT0, ((uint32_t)Data6Bit<<12)|Data12Bit); } /** @brief Write LPDAC1 data @param Data12Bit: 12Bit DAC data @param Data6Bit: 6Bit DAC data @return return none. */ void AD5940_LPDAC1WriteS(uint16_t Data12Bit, uint8_t Data6Bit) { /* Check parameter */ Data6Bit &= 0x3f; Data12Bit &= 0xfff; AD5940_WriteReg(REG_AFE_LPDACDAT1, ((uint32_t)Data6Bit<<12)|Data12Bit); } /** @brief Initialize LP TIA and PA @param pLpAmpCfg: Pointer to configuration structure @return return none. */ void AD5940_LPAMPCfgS(LPAmpCfg_Type *pLpAmpCfg) { //check parameters uint32_t tempreg; tempreg = 0; if(pLpAmpCfg->LpPaPwrEn == bFALSE) tempreg |= BITM_AFE_LPTIACON0_PAPDEN; if(pLpAmpCfg->LpTiaPwrEn == bFALSE) tempreg |= BITM_AFE_LPTIACON0_TIAPDEN; if(pLpAmpCfg->LpAmpPwrMod == LPAMPPWR_HALF) tempreg |= BITM_AFE_LPTIACON0_HALFPWR; else { tempreg |= pLpAmpCfg->LpAmpPwrMod<LpTiaRtia<LpTiaRload<LpTiaRf<LpAmpSel == LPAMP0) { AD5940_WriteReg(REG_AFE_LPTIACON0, tempreg); AD5940_WriteReg(REG_AFE_LPTIASW0, pLpAmpCfg->LpTiaSW); } else { AD5940_WriteReg(REG_AFE_LPTIACON1, tempreg); AD5940_WriteReg(REG_AFE_LPTIASW1, pLpAmpCfg->LpTiaSW); } } /** * @} Low_Power_Loop_Functions * @} Low_Power_Loop */ /** * @defgroup DSP_Block * @brief DSP block includes ADC, filters, DFT and statistic functions. * @{ * @defgroup DSP_Block_Functions * @{ * */ /** @brief Configure low power loop include LPDAC LPAmp(PA and TIA) @param pDSPCfg: Pointer to configure structure; @return return none. */ void AD5940_DSPCfgS(DSPCfg_Type *pDSPCfg) { AD5940_ADCBaseCfgS(&pDSPCfg->ADCBaseCfg); AD5940_ADCFilterCfgS(&pDSPCfg->ADCFilterCfg); AD5940_ADCDigCompCfgS(&pDSPCfg->ADCDigCompCfg); AD5940_DFTCfgS(&pDSPCfg->DftCfg); AD5940_StatisticCfgS(&pDSPCfg->StatCfg); } /** @brief Read AD5940 generated data like ADC and DFT etc. @param AfeResultSel: available parameters are @ref AFERESULT_Const - AFERESULT_SINC3: Read SINC3 filter data result - AFERESULT_SINC2: Read SINC2+NOTCH filter result, when Notch filter is bypassed, the result is SINC2 - AFERESULT_STATSVAR: Statistic variance result @return return data read back. */ uint32_t AD5940_ReadAfeResult(uint32_t AfeResultSel) { uint32_t rd = 0; //PARA_CHECK((AfeResultSel)); switch (AfeResultSel) { case AFERESULT_SINC3: rd = AD5940_ReadReg(REG_AFE_ADCDAT); break; case AFERESULT_SINC2: rd = AD5940_ReadReg(REG_AFE_SINC2DAT); break; case AFERESULT_TEMPSENSOR: rd = AD5940_ReadReg(REG_AFE_TEMPSENSDAT); break; case AFERESULT_DFTREAL: rd = AD5940_ReadReg(REG_AFE_DFTREAL); break; case AFERESULT_DFTIMAGE: rd = AD5940_ReadReg(REG_AFE_DFTIMAG); break; case AFERESULT_STATSMEAN: rd = AD5940_ReadReg(REG_AFE_STATSMEAN); break; case AFERESULT_STATSVAR: rd = AD5940_ReadReg(REG_AFE_STATSVAR); break; } return rd; } /** * @defgroup ADC_Block_Functions * @{ */ /** @brief Initializes ADC peripheral according to the specified parameters in the pADCInit. @param pADCInit: Pointer to ADC initialize structure. @return return none. */ void AD5940_ADCBaseCfgS(ADCBaseCfg_Type *pADCInit) { uint32_t tempreg = 0; //PARA_CHECK(IS_ADCMUXP(pADCInit->ADCMuxP)); //PARA_CHECK(IS_ADCMUXN(pADCInit->ADCMuxN)); PARA_CHECK(IS_ADCPGA(pADCInit->ADCPga)); PARA_CHECK(IS_ADCAAF(pADCInit->ADCAAF)); tempreg = pADCInit->ADCMuxP; tempreg |= (uint32_t)(pADCInit->ADCMuxN)<OffCancEnable == bTRUE) // tempreg |= BITM_AFE_ADCCON_GNOFSELPGA; tempreg |= (uint32_t)(pADCInit->ADCPga)<ADCSinc3Osr)); PARA_CHECK(IS_ADCSINC2OSR(pFiltCfg->ADCSinc2Osr)); PARA_CHECK(IS_ADCAVGNUM(pFiltCfg->ADCAvgNum)); PARA_CHECK(IS_ADCRATE(pFiltCfg->ADCRate)); tempreg = AD5940_ReadReg(REG_AFE_ADCFILTERCON); tempreg &= BITM_AFE_ADCFILTERCON_AVRGEN; /* Keep this bit setting. */ tempreg |= pFiltCfg->ADCRate; if(pFiltCfg->BpNotch == bTRUE) tempreg |= BITM_AFE_ADCFILTERCON_LPFBYPEN; if(pFiltCfg->BpSinc3 == bTRUE) tempreg |= BITM_AFE_ADCFILTERCON_SINC3BYP; /** * Average filter is enabled when DFT source is @ref DFTSRC_AVG in function @ref AD5940_DFTCfgS. * Once average function is enabled, it's automatically set as DFT source, register DFTCON.DFTINSEL is ignored. */ //if(pFiltCfg->AverageEnable == bTRUE) // tempreg |= BITM_AFE_ADCFILTERCON_AVRGEN; tempreg |= (uint32_t)(pFiltCfg->ADCSinc2Osr)<ADCSinc3Osr)<ADCAvgNum)<Sinc2NotchEnable) { AD5940_AFECtrlS(AFECTRL_SINC2NOTCH,bTRUE); } } /** @brief Power up or power down ADC block(including ADC PGA and FRONTBUF). @param State : {bTRUE, bFALSE} - bTRUE: Power up ADC - bFALSE: Power down ADC @return return none. */ void AD5940_ADCPowerCtrlS(BoolFlag State) { uint32_t tempreg; tempreg = AD5940_ReadReg(REG_AFE_AFECON); if(State == bTRUE) { tempreg |= BITM_AFE_AFECON_ADCEN; } else { tempreg &= ~BITM_AFE_AFECON_ADCEN; } AD5940_WriteReg(REG_AFE_AFECON,tempreg); } /** @brief Start or stop ADC convert. @param State : {bTRUE, bFALSE} - bTRUE: Start ADC convert - bFALSE: Stop ADC convert @return return none. */ void AD5940_ADCConvtCtrlS(BoolFlag State) { uint32_t tempreg; tempreg = AD5940_ReadReg(REG_AFE_AFECON); if(State == bTRUE) { tempreg |= BITM_AFE_AFECON_ADCCONVEN; } else { tempreg &= ~BITM_AFE_AFECON_ADCCONVEN; } AD5940_WriteReg(REG_AFE_AFECON,tempreg); } /** @brief Configure ADC input MUX @param ADCMuxP : {ADCMUXP_FLOAT, ADCMUXP_HSTIA_P, ,,, ,ADCMUXP_P_NODE} - ADCMUXP_FLOAT: float ADC MUX positive input - ADCMUXP_HSTIA_P: High speed TIA output sense terminal - ADCMUXP_P_NODE: Excitation loop P node @param ADCMuxN : {ADCMUXP_FLOAT, ADCMUXP_HSTIA_P, ,,, ,ADCMUXP_P_NODE} - ADCMUXP_FLOAT: float ADC MUX positive input - ADCMUXP_HSTIA_P: High speed TIA output sense terminal - ADCMUXP_P_NODE: Excitation loop P node @return return none. */ void AD5940_ADCMuxCfgS(uint32_t ADCMuxP, uint32_t ADCMuxN) { uint32_t tempreg; //PARA_CHECK(IS_ADCMUXP(ADCMuxP)); //PARA_CHECK(IS_ADCMUXN(ADCMuxN)); tempreg = AD5940_ReadReg(REG_AFE_ADCCON); tempreg &= ~(BITM_AFE_ADCCON_MUXSELN|BITM_AFE_ADCCON_MUXSELP); tempreg |= ADCMuxP<ADCMin); AD5940_WriteReg(REG_AFE_ADCMINSM, pCompCfg->ADCMinHys); AD5940_WriteReg(REG_AFE_ADCMAX, pCompCfg->ADCMax); AD5940_WriteReg(REG_AFE_ADCMAXSMEN, pCompCfg->ADCMaxHys); } /** @} ADC_Block_Functions */ /** @brief Configure statistic functions @param pStatCfg: Pointer to configuration structure @return return none. */ void AD5940_StatisticCfgS(StatCfg_Type *pStatCfg) { uint32_t tempreg; //check parameters tempreg = 0; if(pStatCfg->StatEnable == bTRUE) tempreg |= BITM_AFE_STATSCON_STATSEN; tempreg |= (pStatCfg->StatSample) << BITP_AFE_STATSCON_SAMPLENUM; tempreg |= (pStatCfg->StatDev) << BITP_AFE_STATSCON_STDDEV; AD5940_WriteReg(REG_AFE_STATSCON, tempreg); } /** * @brief Set ADC Repeat convert function number. Turn off ADC automatically after Number samples of ADC raw data are ready * @param Number: Specify after how much ADC raw data need to sample before shutdown ADC * @return return none. */ void AD5940_ADCRepeatCfgS(uint32_t Number) { //check parameter if(number<255) AD5940_WriteReg(REG_AFE_REPEATADCCNV, Number<DftSrc == DFTSRC_AVG) { reg_adcfilter |= BITM_AFE_ADCFILTERCON_AVRGEN; AD5940_WriteReg(REG_AFE_ADCFILTERCON, reg_adcfilter); } else { /* Disable Average function and set correct DFT source */ reg_adcfilter &= ~BITM_AFE_ADCFILTERCON_AVRGEN; AD5940_WriteReg(REG_AFE_ADCFILTERCON, reg_adcfilter); /* Set new DFT source */ reg_dftcon |= (pDftCfg->DftSrc) << BITP_AFE_DFTCON_DFTINSEL; } /* Set DFT number */ reg_dftcon |= (pDftCfg->DftNum) << BITP_AFE_DFTCON_DFTNUM; if(pDftCfg->HanWinEn == bTRUE) reg_dftcon |= BITM_AFE_DFTCON_HANNINGEN; AD5940_WriteReg(REG_AFE_DFTCON, reg_dftcon); } /** * @} DSP_Block_Functions * @} DSP_Block */ /** * @defgroup Sequencer_FIFO * @brief Sequencer and FIFO. * @{ * @defgroup Sequencer_FIFO_Functions * @{ */ /** @brief Configure AD5940 FIFO @param pFifoCfg: Pointer to configuration structure. @return return none. */ void AD5940_FIFOCfg(FIFOCfg_Type *pFifoCfg) { uint32_t tempreg; //check parameters AD5940_WriteReg(REG_AFE_FIFOCON, 0); /* Disable FIFO firstly! */ /* CMDDATACON register. Configure this firstly */ tempreg = AD5940_ReadReg(REG_AFE_CMDDATACON); tempreg &= BITM_AFE_CMDDATACON_CMD_MEM_SEL|BITM_AFE_CMDDATACON_CMDMEMMDE; /* Keep sequencer memory settings */ tempreg |= pFifoCfg->FIFOMode << BITP_AFE_CMDDATACON_DATAMEMMDE; /* Data FIFO mode: stream or FIFO */ tempreg |= pFifoCfg->FIFOSize << BITP_AFE_CMDDATACON_DATA_MEM_SEL; /* Data FIFO memory size */ /* The reset memory can be used for sequencer, configure it by function AD5940_SEQCfg() */ AD5940_WriteReg(REG_AFE_CMDDATACON, tempreg); /* FIFO Threshold */ AD5940_WriteReg(REG_AFE_DATAFIFOTHRES, pFifoCfg->FIFOThresh << BITP_AFE_DATAFIFOTHRES_HIGHTHRES); /* FIFOCON register. Final step is to enable FIFO */ tempreg = 0; if(pFifoCfg->FIFOEn == bTRUE) tempreg |= BITM_AFE_FIFOCON_DATAFIFOEN; /* Enable FIFO after everything set. */ tempreg |= pFifoCfg->FIFOSrc << BITP_AFE_FIFOCON_DATAFIFOSRCSEL; AD5940_WriteReg(REG_AFE_FIFOCON, tempreg); } /** @brief Read current FIFO configuration. @param pFifoCfg: Pointer to a buffer that used to store FIFO configuration. @return return AD5940ERR_OK if succeed. */ AD5940Err AD5940_FIFOGetCfg(FIFOCfg_Type *pFifoCfg) { uint32_t tempreg; //check parameters if(pFifoCfg == NULL) return AD5940ERR_NULLP; /* CMDDATACON register. */ tempreg = AD5940_ReadReg(REG_AFE_CMDDATACON); pFifoCfg->FIFOMode = (tempreg&BITM_AFE_CMDDATACON_DATAMEMMDE)>>BITP_AFE_CMDDATACON_DATAMEMMDE; pFifoCfg->FIFOSize = (tempreg&BITM_AFE_CMDDATACON_DATA_MEM_SEL)>>BITP_AFE_CMDDATACON_DATA_MEM_SEL; /* FIFO Threshold */ tempreg = AD5940_ReadReg(REG_AFE_DATAFIFOTHRES); pFifoCfg->FIFOThresh = (tempreg&BITM_AFE_DATAFIFOTHRES_HIGHTHRES)>>BITP_AFE_DATAFIFOTHRES_HIGHTHRES; /* FIFOCON register. */ tempreg = AD5940_ReadReg(REG_AFE_FIFOCON); pFifoCfg->FIFOEn = (tempreg&BITM_AFE_FIFOCON_DATAFIFOEN)?bTRUE:bFALSE; pFifoCfg->FIFOSrc = (tempreg&BITM_AFE_FIFOCON_DATAFIFOSRCSEL)>>BITP_AFE_FIFOCON_DATAFIFOSRCSEL; return AD5940ERR_OK; } /** * @brief Configure AD5940 FIFO Source and enable or disable FIFO. * @param FifoSrc : available choices are @ref FIFOSRC_Const * - FIFOSRC_SINC3 SINC3 data * - FIFOSRC_DFT DFT real and imaginary part * - FIFOSRC_SINC2NOTCH SINC2+NOTCH block. Notch can be bypassed, so SINC2 data can be feed to FIFO * - FIFOSRC_VAR Statistic variance output * - FIFOSRC_MEAN Statistic mean output * @param FifoEn: enable or disable the FIFO. * @return return none. */ void AD5940_FIFOCtrlS(uint32_t FifoSrc, BoolFlag FifoEn) { uint32_t tempreg; tempreg = 0; if(FifoEn == bTRUE) tempreg |= BITM_AFE_FIFOCON_DATAFIFOEN; tempreg |= FifoSrc << BITP_AFE_FIFOCON_DATAFIFOSRCSEL; AD5940_WriteReg(REG_AFE_FIFOCON, tempreg); } /** * @brief Configure AD5940 Data FIFO threshold value @param FIFOThresh: FIFO threshold value @return return none. */ void AD5940_FIFOThrshSet(uint32_t FIFOThresh) { /* FIFO Threshold */ AD5940_WriteReg(REG_AFE_DATAFIFOTHRES, FIFOThresh << BITP_AFE_DATAFIFOTHRES_HIGHTHRES); } /** * @brief Get Data count in FIFO * @return return none. */ uint32_t AD5940_FIFOGetCnt(void) { return AD5940_ReadReg(REG_AFE_FIFOCNTSTA) >> BITP_AFE_FIFOCNTSTA_DATAFIFOCNTSTA; } /* Sequencer */ /** * @brief Initialize Sequencer * @param pSeqCfg: Pointer to configuration structure @return return none. */ void AD5940_SEQCfg(SEQCfg_Type *pSeqCfg) { /* check parameters */ uint32_t tempreg, fifocon; fifocon = AD5940_ReadReg(REG_AFE_FIFOCON); AD5940_WriteReg(REG_AFE_FIFOCON, 0); /* Disable FIFO before changing memory configuration */ /* Configure CMDDATACON register */ tempreg = AD5940_ReadReg(REG_AFE_CMDDATACON); tempreg &= ~(BITM_AFE_CMDDATACON_CMDMEMMDE|BITM_AFE_CMDDATACON_CMD_MEM_SEL); /* Clear settings for sequencer memory */ tempreg |= (1L) << BITP_AFE_CMDDATACON_CMDMEMMDE; /* Sequencer is always in memory mode */ tempreg |= (pSeqCfg->SeqMemSize) << BITP_AFE_CMDDATACON_CMD_MEM_SEL; AD5940_WriteReg(REG_AFE_CMDDATACON, tempreg); if(pSeqCfg->SeqCntCRCClr) { AD5940_WriteReg(REG_AFE_SEQCON, 0); /* Disable sequencer firstly */ AD5940_WriteReg(REG_AFE_SEQCNT, 0); /* When sequencer is disabled, any write to SEQCNT will clear CNT and CRC register */ } tempreg = 0; if(pSeqCfg->SeqEnable == bTRUE) tempreg |= BITM_AFE_SEQCON_SEQEN; tempreg |= (pSeqCfg->SeqWrTimer) << BITP_AFE_SEQCON_SEQWRTMR; AD5940_WriteReg(REG_AFE_SEQCON, tempreg); AD5940_WriteReg(REG_AFE_FIFOCON, fifocon); /* restore FIFO configuration */ // tempreg = 0; // if(pSeqCfg->SeqBreakEn) // tempreg |= 0x01; // add register definition? bitm_afe_ // if(pSeqCfg->SeqIgnoreEn) // tempreg |= 0x02; // AD5940_WriteReg(0x21dc, tempreg); } /** * @brief Read back current sequencer configuration and store it to pSeqCfg * @param pSeqCfg: Pointer to structure * @return return AD5940ERR_OK if succeed. */ AD5940Err AD5940_SEQGetCfg(SEQCfg_Type *pSeqCfg) { /* check parameters */ uint32_t tempreg; if(pSeqCfg == NULL) return AD5940ERR_NULLP; /* Read CMDDATACON register */ tempreg = AD5940_ReadReg(REG_AFE_CMDDATACON); pSeqCfg->SeqMemSize = (tempreg&BITM_AFE_CMDDATACON_CMD_MEM_SEL) >> BITP_AFE_CMDDATACON_CMD_MEM_SEL; pSeqCfg->SeqCntCRCClr = bFALSE; /* Has no meaning */ /* SEQCON register */ tempreg = AD5940_ReadReg(REG_AFE_SEQCON); pSeqCfg->SeqEnable = (tempreg&BITM_AFE_SEQCON_SEQEN)?bTRUE:bFALSE; pSeqCfg->SeqWrTimer = (tempreg&BITM_AFE_SEQCON_SEQWRTMR) >> BITP_AFE_SEQCON_SEQWRTMR; return AD5940ERR_OK; } /** * @brief Enable or Disable sequencer. * @note Only after valid trigger signal, sequencer can run. * @return return none. */ void AD5940_SEQCtrlS(BoolFlag SeqEn) { uint32_t tempreg = AD5940_ReadReg(REG_AFE_SEQCON); if(SeqEn == bTRUE) tempreg |= BITM_AFE_SEQCON_SEQEN; else tempreg &= ~BITM_AFE_SEQCON_SEQEN; AD5940_WriteReg(REG_AFE_SEQCON, tempreg); } /** * @brief Halt sequencer immediately. Use this to debug. In normal application, there is no situation that can use this function. * @return return none. */ void AD5940_SEQHaltS(void) { AD5940_WriteReg(REG_AFE_SEQCON, BITM_AFE_SEQCON_SEQHALT|BITM_AFE_SEQCON_SEQEN); } /** * @brief Trigger sequencer by register write. * @return return none. **/ void AD5940_SEQMmrTrig(uint32_t SeqId) { if(SeqId > SEQID_3) return; AD5940_WriteReg(REG_AFECON_TRIGSEQ, 1L<SeqId) { case SEQID_0: /* Configure SEQINFO register */ AD5940_WriteReg(REG_AFE_SEQ0INFO, (pSeq->SeqLen<< 16) | pSeq->SeqRamAddr); break; case SEQID_1: AD5940_WriteReg(REG_AFE_SEQ1INFO, (pSeq->SeqLen<< 16) | pSeq->SeqRamAddr); break; case SEQID_2: AD5940_WriteReg(REG_AFE_SEQ2INFO, (pSeq->SeqLen<< 16) | pSeq->SeqRamAddr); break; case SEQID_3: AD5940_WriteReg(REG_AFE_SEQ3INFO, (pSeq->SeqLen<< 16) | pSeq->SeqRamAddr); break; default: break; } if(pSeq->WriteSRAM == bTRUE) { AD5940_SEQCmdWrite(pSeq->SeqRamAddr, pSeq->pSeqCmd, pSeq->SeqLen); } } /** * @brief Get sequence info: start address and sequence length. * @param SeqId: Select from {SEQID_0, SEQID_1, SEQID_2, SEQID_3} - Select which sequence we want to get the information. @param pSeqInfo: Pointer to sequence info structure. @return return AD5940ERR_OK when succeed. */ AD5940Err AD5940_SEQInfoGet(uint32_t SeqId, SEQInfo_Type *pSeqInfo) { uint32_t tempreg; if(pSeqInfo == NULL) return AD5940ERR_NULLP; switch(SeqId) { case SEQID_0: tempreg = AD5940_ReadReg(REG_AFE_SEQ0INFO); break; case SEQID_1: tempreg = AD5940_ReadReg(REG_AFE_SEQ1INFO); break; case SEQID_2: tempreg = AD5940_ReadReg(REG_AFE_SEQ2INFO); break; case SEQID_3: tempreg = AD5940_ReadReg(REG_AFE_SEQ3INFO); break; default: return AD5940ERR_PARA; } pSeqInfo->pSeqCmd = 0; /* We don't know where you store the sequence in MCU SRAM */ pSeqInfo->SeqId = SeqId; pSeqInfo->SeqLen = (tempreg>>16)&0x7ff; pSeqInfo->SeqRamAddr = tempreg&0x7ff; pSeqInfo->WriteSRAM = bFALSE; /* Don't care */ return AD5940ERR_OK; } /** @brief Control GPIO with register SYNCEXTDEVICE. Because sequencer have no ability to access register GPIOOUT, so we use this register for sequencer. @param Gpio : Select from {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} - The combination of GPIO pins. The selected pins will be set to High. Others will be pulled low. @return return None. **/ void AD5940_SEQGpioCtrlS(uint32_t Gpio) { AD5940_WriteReg(REG_AFE_SYNCEXTDEVICE, Gpio); } /** * @brief Read back current count down timer value for Sequencer Timer Out command. * @return return register value of Sequencer Timer out value. **/ uint32_t AD5940_SEQTimeOutRd(void) { return AD5940_ReadReg(REG_AFE_SEQTIMEOUT); } /** * @brief Configure GPIO to allow it to trigger corresponding sequence(SEQ0/1/2/3). * @details There are four sequences. We can use GPIO to trigger each sequence. For example, * GP0 or GP4 can be used to trigger sequence0 and GP3 or GP7 to trigger sequence3. * There are five mode available to detect pin action: Rising edge, falling edge, both rising and falling * edge, low level or high level. * Be careful to use level detection. The trigger signal is always available if the pin level is matched. * Once the sequence is done, it will immediately run again if the pin level is still matched. * @return return AD5940ERR_OK if succeed. **/ AD5940Err AD5940_SEQGpioTrigCfg(SeqGpioTrig_Cfg *pSeqGpioTrigCfg) { uint32_t reg_ei0con, reg_ei1con; uint32_t pin_count, pin_mask; uint32_t mode, en; if(pSeqGpioTrigCfg == NULL) return AD5940ERR_NULLP; reg_ei0con = AD5940_ReadReg(REG_ALLON_EI0CON); reg_ei1con = AD5940_ReadReg(REG_ALLON_EI1CON); pin_count = 0; /* Start from pin0 */ pin_mask = 0x01; /* start from pin0, mask 0x01 */ pSeqGpioTrigCfg->SeqPinTrigMode &= 0x07; /* 3bit width */ mode = pSeqGpioTrigCfg->SeqPinTrigMode; en = pSeqGpioTrigCfg->bEnable?1:0; for(;;) { uint32_t bit_position; if(pSeqGpioTrigCfg->PinSel&pin_mask) { if(pin_count < 4) /* EI0CON register */ { bit_position = pin_count*4; reg_ei1con &= ~(0xfL<SeqxWakeupTime[0] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ0WUPH, (pWuptCfg->SeqxWakeupTime[0] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ0SLEEPL, (pWuptCfg->SeqxSleepTime[0] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ0SLEEPH, (pWuptCfg->SeqxSleepTime[0] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ1WUPL, (pWuptCfg->SeqxWakeupTime[1] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ1WUPH, (pWuptCfg->SeqxWakeupTime[1] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ1SLEEPL, (pWuptCfg->SeqxSleepTime[1] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ1SLEEPH, (pWuptCfg->SeqxSleepTime[1] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ2WUPL, (pWuptCfg->SeqxWakeupTime[2] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ2WUPH, (pWuptCfg->SeqxWakeupTime[2] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ2SLEEPL, (pWuptCfg->SeqxSleepTime[2] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ2SLEEPH, (pWuptCfg->SeqxSleepTime[2] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ3WUPL, (pWuptCfg->SeqxWakeupTime[3] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ3WUPH, (pWuptCfg->SeqxWakeupTime[3] & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ3SLEEPL, (pWuptCfg->SeqxSleepTime[3] & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ3SLEEPH, (pWuptCfg->SeqxSleepTime[3] & 0xF0000)>>16); /* TMRCON register */ //if(pWuptCfg->WakeupEn == bTRUE) /* enable use Wupt to wakeup AFE */ /* We always allow Wupt to wakeup AFE automatically. */ AD5940_WriteReg(REG_ALLON_TMRCON, BITM_ALLON_TMRCON_TMRINTEN); /* Wupt order */ tempreg = 0; tempreg |= (pWuptCfg->WuptOrder[0]&0x03) << BITP_WUPTMR_SEQORDER_SEQA; /* position A */ tempreg |= (pWuptCfg->WuptOrder[1]&0x03) << BITP_WUPTMR_SEQORDER_SEQB; /* position B */ tempreg |= (pWuptCfg->WuptOrder[2]&0x03) << BITP_WUPTMR_SEQORDER_SEQC; /* position C */ tempreg |= (pWuptCfg->WuptOrder[3]&0x03) << BITP_WUPTMR_SEQORDER_SEQD; /* position D */ tempreg |= (pWuptCfg->WuptOrder[4]&0x03) << BITP_WUPTMR_SEQORDER_SEQE; /* position E */ tempreg |= (pWuptCfg->WuptOrder[5]&0x03) << BITP_WUPTMR_SEQORDER_SEQF; /* position F */ tempreg |= (pWuptCfg->WuptOrder[6]&0x03) << BITP_WUPTMR_SEQORDER_SEQG; /* position G */ tempreg |= (pWuptCfg->WuptOrder[7]&0x03) << BITP_WUPTMR_SEQORDER_SEQH; /* position H */ AD5940_WriteReg(REG_WUPTMR_SEQORDER, tempreg); tempreg = 0; if(pWuptCfg->WuptEn == bTRUE) tempreg |= BITM_WUPTMR_CON_EN; /* We always allow Wupt to trigger sequencer */ tempreg |= pWuptCfg->WuptEndSeq << BITP_WUPTMR_CON_ENDSEQ; //tempreg |= 1L<<4; AD5940_WriteReg(REG_WUPTMR_CON, tempreg); } /** * @brief Enable or disable wakeup timer * @param Enable : {bTRUE, bFALSE} * - bTRUE: enable wakeup timer * - bFALSE: Disable wakeup timer * @return return none. */ void AD5940_WUPTCtrl(BoolFlag Enable) { uint16_t tempreg; tempreg = AD5940_ReadReg(REG_WUPTMR_CON); tempreg &= ~BITM_WUPTMR_CON_EN; if(Enable == bTRUE) tempreg |= BITM_WUPTMR_CON_EN; AD5940_WriteReg(REG_WUPTMR_CON, tempreg); } /** * @brief Configure WakeupTimer. * @param SeqId: Select from SEQID_0/1/2/3. The wakeup timer will load corresponding value from four sets of registers. * @param SleepTime: After how much time, AFE will try to enter hibernate. We disabled this feature in AD59840_Initialize. After this timer expired, nothing will happen. * @param WakeupTime: After how much time, AFE will wakeup and trigger corresponding sequencer. * @note By SleepTime and WakeupTime, the sequencer is triggered periodically and period is (SleepTime+WakeupTime) * @return return none. */ AD5940Err AD5940_WUPTTime(uint32_t SeqId, uint32_t SleepTime, uint32_t WakeupTime) { switch (SeqId) { case SEQID_0: { AD5940_WriteReg(REG_WUPTMR_SEQ0WUPL, (WakeupTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ0WUPH, (WakeupTime & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ0SLEEPL, (SleepTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ0SLEEPH, (SleepTime & 0xF0000)>>16); break; } case SEQID_1: { AD5940_WriteReg(REG_WUPTMR_SEQ1WUPL, (WakeupTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ1WUPH, (WakeupTime & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ1SLEEPL, (SleepTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ1SLEEPH, (SleepTime & 0xF0000)>>16); break; } case SEQID_2: { AD5940_WriteReg(REG_WUPTMR_SEQ2WUPL, (WakeupTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ2WUPH, (WakeupTime & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ2SLEEPL, (SleepTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ2SLEEPH, (SleepTime & 0xF0000)>>16); break; } case SEQID_3: { AD5940_WriteReg(REG_WUPTMR_SEQ3WUPL, (WakeupTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ3WUPH, (WakeupTime & 0xF0000)>>16); AD5940_WriteReg(REG_WUPTMR_SEQ3SLEEPL, (SleepTime & 0xFFFF)); AD5940_WriteReg(REG_WUPTMR_SEQ3SLEEPH, (SleepTime & 0xF0000)>>16); break; } default: return AD5940ERR_PARA; } return AD5940ERR_OK; } /** * @} end-of Sequencer_FIFO_Functions * @} end-of Sequencer_FIFO */ /** * @defgroup MISC_Block * @brief Other functions not included in above blocks. Clock, GPIO, INTC etc. * @{ * @defgroup MISC_Block_Functions * @{ */ /** * @brief Configure AD5940 clock * @param pClkCfg: Pointer to configuration structure. * @return return none. */ void AD5940_CLKCfg(CLKCfg_Type *pClkCfg) { uint32_t tempreg, reg_osccon; reg_osccon = AD5940_ReadReg(REG_ALLON_OSCCON); /* Enable clocks */ if(pClkCfg->HFXTALEn == bTRUE) { reg_osccon |= BITM_ALLON_OSCCON_HFXTALEN; AD5940_WriteReg(REG_ALLON_OSCKEY,KEY_OSCCON); /* Write Key */ AD5940_WriteReg(REG_ALLON_OSCCON, reg_osccon); /* Enable HFXTAL */ while((AD5940_ReadReg(REG_ALLON_OSCCON)&BITM_ALLON_OSCCON_HFXTALOK) == 0); /* Wait for clock ready */ } if(pClkCfg->HFOSCEn == bTRUE) { reg_osccon |= BITM_ALLON_OSCCON_HFOSCEN; AD5940_WriteReg(REG_ALLON_OSCKEY,KEY_OSCCON); /* Write Key */ AD5940_WriteReg(REG_ALLON_OSCCON, reg_osccon); /* Enable HFOSC */ while((AD5940_ReadReg(REG_ALLON_OSCCON)&BITM_ALLON_OSCCON_HFOSCOK) == 0); /* Wait for clock ready */ /* Configure HFOSC mode if it's enabled. */ if(pClkCfg->HfOSC32MHzMode == bTRUE) AD5940_HFOSC32MHzCtrl(bTRUE); else AD5940_HFOSC32MHzCtrl(bFALSE); } if(pClkCfg->LFOSCEn == bTRUE) { reg_osccon |= BITM_ALLON_OSCCON_LFOSCEN; AD5940_WriteReg(REG_ALLON_OSCKEY,KEY_OSCCON); /* Write Key */ AD5940_WriteReg(REG_ALLON_OSCCON, reg_osccon); /* Enable LFOSC */ while((AD5940_ReadReg(REG_ALLON_OSCCON)&BITM_ALLON_OSCCON_LFOSCOK) == 0); /* Wait for clock ready */ } /* Switch clocks */ /* step1. Set clock divider */ tempreg = pClkCfg->SysClkDiv&0x3f; tempreg |= (pClkCfg->SysClkDiv&0x3f) << BITP_AFECON_CLKCON0_SYSCLKDIV; tempreg |= (pClkCfg->ADCClkDiv&0xf) << BITP_AFECON_CLKCON0_ADCCLKDIV; AD5940_WriteReg(REG_AFECON_CLKCON0, tempreg); AD5940_Delay10us(10); /* Step2. set clock source */ tempreg = pClkCfg->SysClkSrc; tempreg |= pClkCfg->ADCCLkSrc << BITP_AFECON_CLKSEL_ADCCLKSEL; AD5940_WriteReg(REG_AFECON_CLKSEL, tempreg); /* Disable clocks */ if(pClkCfg->HFXTALEn == bFALSE) reg_osccon &= ~BITM_ALLON_OSCCON_HFXTALEN; if(pClkCfg->HFOSCEn == bFALSE) reg_osccon &= ~BITM_ALLON_OSCCON_HFOSCEN; if(pClkCfg->LFOSCEn == bFALSE) reg_osccon &= ~BITM_ALLON_OSCCON_LFOSCEN; AD5940_WriteReg(REG_ALLON_OSCKEY, KEY_OSCCON); /* Write Key */ AD5940_WriteReg(REG_ALLON_OSCCON, reg_osccon); } /** * @brief Configure Internal HFOSC to output 32MHz or 16MHz. * @param Mode32MHz : {bTRUE, bFALSE} * - bTRUE: HFOSC 32MHz mode. * - bFALSE: HFOSC 16MHz mode. * @return return none. */ void AD5940_HFOSC32MHzCtrl(BoolFlag Mode32MHz) { uint32_t RdCLKEN1; uint32_t RdHPOSCCON; uint32_t bit8,bit9; RdCLKEN1 = AD5940_ReadReg(REG_AFECON_CLKEN1); bit8 = (RdCLKEN1>>9)&0x01; bit9 = (RdCLKEN1>>8)&0x01; /* Fix bug in silicon, bit8 and bit9 is swapped when read back. */ RdCLKEN1 = RdCLKEN1&0xff; RdCLKEN1 |= (bit8<<8)|(bit9<<9); AD5940_WriteReg(REG_AFECON_CLKEN1,RdCLKEN1|BITM_AFECON_CLKEN1_ACLKDIS); /* Disable ACLK during clock changing */ RdHPOSCCON = AD5940_ReadReg(REG_AFE_HPOSCCON); if(Mode32MHz == bTRUE) { AD5940_WriteReg(REG_AFE_HPOSCCON,RdHPOSCCON&(~BITM_AFE_HPOSCCON_CLK32MHZEN)); /* Enable 32MHz output(bit definition-0: 32MHz, 1: 16MHz) */ while((AD5940_ReadReg(REG_ALLON_OSCCON)&BITM_ALLON_OSCCON_HFOSCOK) == 0); /* Wait for clock ready */ } else { AD5940_WriteReg(REG_AFE_HPOSCCON,RdHPOSCCON|BITM_AFE_HPOSCCON_CLK32MHZEN); /* Enable 16MHz output(bit definition-0: 32MHz, 1: 16MHz) */ while((AD5940_ReadReg(REG_ALLON_OSCCON)&BITM_ALLON_OSCCON_HFOSCOK) == 0); /* Wait for clock ready */ } AD5940_WriteReg(REG_AFECON_CLKEN1,RdCLKEN1&(~BITM_AFECON_CLKEN1_ACLKDIS)); /* Enable ACLK */ } /** * @brief Enable high power mode for high frequency EIS * @param Mode32MHz : {bTRUE, bFALSE} * - bTRUE: HFOSC 32MHz mode. * - bFALSE: HFOSC 16MHz mode. * @return return none. */ void AD5940_HPModeEn(BoolFlag Enable) { CLKCfg_Type clk_cfg; uint32_t temp_reg = 0; /* Check what the system clock is */ temp_reg = AD5940_ReadReg(REG_AFECON_CLKSEL); clk_cfg.ADCCLkSrc = (temp_reg>>2)&0x3; clk_cfg.SysClkSrc = temp_reg & 0x3; if(Enable == bTRUE) { clk_cfg.SysClkDiv = SYSCLKDIV_2; clk_cfg.HfOSC32MHzMode = bTRUE; AD5940_AFEPwrBW(AFEPWR_HP, AFEBW_250KHZ); } else { clk_cfg.SysClkDiv = SYSCLKDIV_1; clk_cfg.HfOSC32MHzMode = bFALSE; AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_100KHZ); } clk_cfg.ADCClkDiv = ADCCLKDIV_1; clk_cfg.HFOSCEn = (temp_reg & 0x3) == 0x1? bFALSE : bTRUE;; clk_cfg.HFXTALEn = (temp_reg & 0x3) == 0x1? bTRUE : bFALSE; clk_cfg.LFOSCEn = bTRUE; AD5940_CLKCfg(&clk_cfg); } /** * @defgroup Interrupt_Controller_Functions * @{ */ /* AFE Interrupt Controller */ /** * @brief Enable or Disable selected interrupt source(s) * @param AfeIntcSel : {AFEINTC_0, AFEINTC_1} * - AFEINTC_0: Configure Interrupt Controller 0 * - AFEINTC_1: Configure Interrupt Controller 1 * @param AFEIntSrc: select from @ref AFEINTC_SRC_Const * - AFEINTSRC_ADCRDY : Bit0 ADC Result Ready Status * - AFEINTSRC_DFTRDY : Bit1 DFT Result Ready Status * - AFEINTSRC_SUPPLYFILTRDY : Bit2 Low Pass Filter Result Status * - AFEINTSRC_TEMPRDY : Bit3, Temp Sensor Result Ready * - AFEINTSRC_ADCMINERR : Bit4, ADC Minimum Value * - AFEINTSRC_ADCMAXERR : Bit5, ADC Maximum Value * - AFEINTSRC_ADCDIFFERR : Bit6, ADC Delta Ready * - AFEINTSRC_MEANRDY : Bit7, Mean Result Ready * - AFEINTSRC_VARRDY : Bit8, Variance Result Ready * - AFEINTSRC_DLYCMDDONE : Bit9, User controlled interrupt by writing AFEGENINTSTA. Provides an Early Indication for the End of the Test _Block. * - AFEINTSRC_HWSETUPDONE : Bit10, User controlled interrupt by writing AFEGENINTSTA. Indicates the MMR Setup for the Measurement Step Finished * - AFEINTSRC_BRKSEQ : Bit11, User controlled interrupt by writing AFEGENINTSTA. * - AFEINTSRC_CUSTOMINS : Bit12, User controlled interrupt by writing AFEGENINTSTA. General Purpose Custom Interrupt. * - AFEINTSRC_BOOTLDDONE : Bit13, OTP Boot Loading Done * - AFEINTSRC_WAKEUP : Bit14, AFE Woken up * - AFEINTSRC_ENDSEQ : Bit15, End of Sequence Interrupt. * - AFEINTSRC_SEQTIMEOUT : Bit16, Sequencer Timeout Command Finished. * - AFEINTSRC_SEQTIMEOUTERR : Bit17, Sequencer Timeout Command Error. * - AFEINTSRC_CMDFIFOFULL : Bit18, Command FIFO Full Interrupt. * - AFEINTSRC_CMDFIFOEMPTY : Bit19, Command FIFO Empty * - AFEINTSRC_CMDFIFOTHRESH: Bit20, Command FIFO Threshold Interrupt. * - AFEINTSRC_CMDFIFOOF : Bit21, Command FIFO Overflow Interrupt. * - AFEINTSRC_CMDFIFOUF : Bit22, Command FIFO Underflow Interrupt. * - AFEINTSRC_DATAFIFOFULL : Bit23, Data FIFO Full Interrupt. * - AFEINTSRC_DATAFIFOEMPTY: Bit24, Data FIFO Empty * - AFEINTSRC_DATAFIFOTHRESH: Bit25, Data FIFO Threshold Interrupt. * - AFEINTSRC_DATAFIFOOF : Bit26, Data FIFO Overflow Interrupt. * - AFEINTSRC_DATAFIFOUF : Bit27, Data FIFO Underflow Interrupt. * - AFEINTSRC_WDTIRQ : Bit28, WDT Timeout Interrupt. * - AFEINTSRC_CRC_OUTLIER : Bit29, CRC interrupt for M355, Outliers Int for AD5940 * - AFEINTSRC_GPT0INT_SLPWUT: Bit30, General Purpose Timer0 IRQ for M355. Sleep or Wakeup Timer timeout for AD5940 * - AFEINTSRC_GPT1INT_TRYBRK: Bit31, General Purpose Timer1 IRQ for M355. Tried to Break IRQ for AD5940 * - AFE_INTC_ALLINT : All interrupts * @param State : {bTRUE, bFALSE} * - bTRUE: Enable these interrupt source(s) * - bFALSE: Disable interrupt source(s) * @return return none. */ void AD5940_INTCCfg(uint32_t AfeIntcSel, uint32_t AFEIntSrc, BoolFlag State) { uint32_t tempreg; uint32_t regaddr = REG_INTC_INTCSEL0; if(AfeIntcSel == AFEINTC_1) regaddr = REG_INTC_INTCSEL1; tempreg = AD5940_ReadReg(regaddr); if(State == bTRUE) tempreg |= AFEIntSrc; /* Enable this interrupt */ else tempreg &= ~(AFEIntSrc); /* Disable this interrupt */ AD5940_WriteReg(regaddr,tempreg); } /** * @brief Check if current interrupt configuration. * @param AfeIntcSel : {AFEINTC_0, AFEINTC_1} * - AFEINTC_0: Configure Interrupt Controller 0 * - AFEINTC_1: Configure Interrupt Controller 1 */ uint32_t AD5940_INTCGetCfg(uint32_t AfeIntcSel) { uint32_t tempreg; if(AfeIntcSel == AFEINTC_0) tempreg = AD5940_ReadReg(REG_INTC_INTCSEL0); else tempreg = AD5940_ReadReg(REG_INTC_INTCSEL1); return tempreg; } /** * @brief Clear selected interrupt(s) flag(INTC0Flag and INTC1Flag are both cleared). * @param AfeIntSrcSel: Select from @ref AFEINTC_SRC_Const * @return return none. **/ void AD5940_INTCClrFlag(uint32_t AfeIntSrcSel) { AD5940_WriteReg(REG_INTC_INTCCLR,AfeIntSrcSel); } /** * @brief Test if selected interrupt source(s) is(are) bTRUE. * @param AfeIntcSel : {AFEINTC_0, AFEINTC_1} * - AFEINTC_0: Read Interrupt Controller 0 flag * - AFEINTC_1: Read Interrupt Controller 1 flag * @param AfeIntSrcSel: Select from @ref AFEINTC_SRC_Const * @return If selected interrupt source(s) are all cleared, return bFALSE. Otherwise return bTRUE. **/ BoolFlag AD5940_INTCTestFlag(uint32_t AfeIntcSel, uint32_t AfeIntSrcSel) { uint32_t tempreg; uint32_t regaddr = (AfeIntcSel == AFEINTC_0)? REG_INTC_INTCFLAG0: REG_INTC_INTCFLAG1; tempreg = AD5940_ReadReg(regaddr); if(tempreg & AfeIntSrcSel) return bTRUE; else return bFALSE; } /** * @brief return register value of REG_INTC_INTCFLAGx * @param AfeIntcSel : {AFEINTC_0, AFEINTC_1} * - AFEINTC_0: Read Interrupt Controller 0 flag * - AFEINTC_1: Read Interrupt Controller 1 flag * @return register value of REG_INTC_INTCFLAGx. **/ uint32_t AD5940_INTCGetFlag(uint32_t AfeIntcSel) { uint32_t tempreg; uint32_t regaddr = (AfeIntcSel == AFEINTC_0)? REG_INTC_INTCFLAG0: REG_INTC_INTCFLAG1; tempreg = AD5940_ReadReg(regaddr); return tempreg; } /** * @} Interrupt_Controller_Functions */ /** * @defgroup GPIO_Block_Functions * @{ */ /** * @brief Initialize AFE GPIO * @param pAgpioCfg: Pointer to configuration structure * @return return none. */ void AD5940_AGPIOCfg(AGPIOCfg_Type *pAgpioCfg) { AD5940_AGPIOFuncCfg(pAgpioCfg->FuncSet); AD5940_AGPIOOen(pAgpioCfg->OutputEnSet); AD5940_AGPIOIen(pAgpioCfg->InputEnSet); AD5940_AGPIOPen(pAgpioCfg->PullEnSet); AD5940_WriteReg(REG_AGPIO_GP0OUT, pAgpioCfg->OutVal); } /** * @brief Configure the function of GP0 to GP7. * @param uiCfgSet :{GP0_INT,GP0_TRIG,GP0_SYNC,GP0_GPIO| * GP1_GPIO,GP1_TRIG,GP1_SYNC,GP1_SLEEP| * GP2_PORB,GP2_TRIG,GP2_SYNC,GP2_EXTCLK| * GP3_GPIO,GP3_TRIG,GP3_SYNC,GP3_INT0|\ * GP4_GPIO,GP4_TRIG,GP4_SYNC,GP4_INT1| * GP5_GPIO,GP5_TRIG,GP5_SYNC,GP5_EXTCLK| * GP6_GPIO,GP6_TRIG,GP6_SYNC,GP6_INT0| * GP7_GPIO,GP7_TRIG,GP7_SYNC,GP7_INT} * @return return none. **/ void AD5940_AGPIOFuncCfg(uint32_t uiCfgSet) { AD5940_WriteReg(REG_AGPIO_GP0CON,uiCfgSet); } /** * @brief Enable GPIO output mode on selected pins. Disable output on non-selected pins. * @param uiPinSet :Select from {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOOen(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0OEN,uiPinSet); } /** * @brief Enable input on selected pins while disable others. * @param uiPinSet: Select from {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOIen(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0IEN,uiPinSet); } /** * @brief Read the GPIO status. * @return return GP0IN register which is the GPIO status. **/ uint32_t AD5940_AGPIOIn(void) { return AD5940_ReadReg(REG_AGPIO_GP0IN); } /** * @brief Enable pull-up or down on selected pins while disable other pins. * @param uiPinSet: Select from: {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOPen(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0PE,uiPinSet); } /** * @brief Put selected GPIOs to high level. * @param uiPinSet: Select from: {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOSet(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0SET,uiPinSet); } /** * @brief Put selected GPIOs to low level. * @param uiPinSet: Select from: {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOClr(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0CLR,uiPinSet); } /** * @brief Toggle selected GPIOs. * @param uiPinSet: Select from: {AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin2|AGPIO_Pin3|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6|AGPIO_Pin7} * @return return none **/ void AD5940_AGPIOToggle(uint32_t uiPinSet) { AD5940_WriteReg(REG_AGPIO_GP0TGL,uiPinSet); } /** * @} GPIO_Block_Functions */ /** * @defgroup LPMode_Block_Functions * @{ */ /** * @brief Enter or leave LPMODE. * @details Once enter this mode, some registers are collected together to a new register so we can * Control most blocks with in one register. The so called LPMODE has nothing to do with AD5940 power. * @return return AD5940ERR_OK **/ AD5940Err AD5940_LPModeEnS(BoolFlag LPModeEn) { if(LPModeEn == bTRUE) AD5940_WriteReg(REG_AFE_LPMODEKEY, KEY_LPMODEKEY); /* Enter LP mode by right key. */ else AD5940_WriteReg(REG_AFE_LPMODEKEY, 0); /* Write wrong key to exit LP mode */ return AD5940ERR_OK; } /** * @brief Select system clock source for LPMODE. * @note Only in LP Mode, this operation takes effect. Enter LPMODE by function @ref AD5940_LPModeEnS. * @param LPModeClk: Select from @ref LPMODECLK_Const * - LPMODECLK_LFOSC: Select LFOSC 32kHz for system clock * - LPMODECLK_HFOSC: Select HFOSC 16MHz/32MHz for system clock * @return none. */ void AD5940_LPModeClkS(uint32_t LPModeClk) { AD5940_WriteReg(REG_AFE_LPMODECLKSEL, LPModeClk); } /** * @} LPMode_Block_Functions */ /** * @brief Enter sleep mode key to unlock it or enter incorrect key to lock it. \ * Once key is unlocked, it will always be effect until manually lock it * @param SlpKey : {SLPKEY_UNLOCK, SLPKEY_LOCK} - SLPKEY_UNLOCK Unlock Key so we can enter sleep(or called hibernate) mode. - SLPKEY_LOCK Lock key so AD5940 is prohibited to enter sleep mode. @return return none. */ void AD5940_SleepKeyCtrlS(uint32_t SlpKey) { AD5940_WriteReg(REG_AFE_SEQSLPLOCK, SlpKey); } /** * @brief Put AFE to hibernate. * @details This will only take effect when SLP_KEY has been unlocked. Use function @ref AD5940_SleepKeyCtrlS to enter correct key. * @return return none. */ void AD5940_EnterSleepS(void) { AD5940_WriteReg(REG_AFE_SEQTRGSLP, 0); AD5940_WriteReg(REG_AFE_SEQTRGSLP, 1); } /** * @brief Turn off LP-Loop and put AFE to hibernate mode; * @details By function @ref AD5940_EnterSleepS, we can put most blocks to hibernate mode except LP block. * This function will shut down LP block and then enter sleep mode. * @return return none. */ void AD5940_ShutDownS(void) { /* Turn off LPloop related blocks which are not controlled automatically by hibernate operation */ AFERefCfg_Type aferef_cfg; LPLoopCfg_Type lp_loop; /* Turn off LP-loop manually because it's not affected by sleep/hibernate mode */ AD5940_StructInit(&aferef_cfg, sizeof(aferef_cfg)); AD5940_StructInit(&lp_loop, sizeof(lp_loop)); AD5940_REFCfgS(&aferef_cfg); AD5940_LPLoopCfgS(&lp_loop); AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); /* Unlock the key */ AD5940_EnterSleepS(); /* Enter Hibernate */ } /** * @brief Try to wakeup AD5940 by read register. * @details Any SPI operation can wakeup AD5940. AD5940_Initialize must be called to enable this function. * @param TryCount Specify how many times we will read register. Zero or negative number means always waiting here. * @return How many times register is read. If returned value is bigger than TryCount, it means wakeup failed. */ uint32_t AD5940_WakeUp(int32_t TryCount) { uint32_t count = 0; while(1) { count++; if(AD5940_ReadReg(REG_AFECON_ADIID) == AD5940_ADIID) break; /* Succeed */ if(TryCount<=0) continue; /* Always try to wakeup AFE */ if(count > TryCount) break; /* Failed */ } return count; } /** * @brief Read ADIID register, the value for current version is @ref AD5940_ADIID * @return return none. */ uint32_t AD5940_GetADIID(void) { return AD5940_ReadReg(REG_AFECON_ADIID); } /** * @brief Read CHIPID register, the value for current version is 0x5501. * @return return none. */ uint32_t AD5940_GetChipID(void) { return AD5940_ReadReg(REG_AFECON_CHIPID); } /** * @brief Reset AD5940 by register. * @note AD5940 must be in active state so we can access registers. * If AD5940 system clock is too low, we consider to use hardware reset, or * we need to make sure register write is successfully. * @return return none. */ AD5940Err AD5940_SoftRst(void) { AD5940_WriteReg(REG_AFECON_SWRSTCON, AD5940_SWRST); AD5940_Delay10us(20); /* AD5940 need some time to exit reset status. 200us looks good. */ /* We can check RSTSTA register to make sure software reset happened. */ return AD5940ERR_OK; } /** * @brief Reset AD5940 with RESET pin. * @note This will call function AD5940_RstClr which locates in file XXXPort.C * @return return none. */ void AD5940_HWReset(void) { #ifndef CHIPSEL_M355 AD5940_RstClr(); AD5940_Delay10us(200); /* Delay some time */ AD5940_RstSet(); AD5940_Delay10us(500); /* AD5940 need some time to exit reset status. 200us looks good. */ #else //There is no method to reset AFE only for M355. #endif } /** * @} MISC_Block_Functions * @} MISC_Block */ /** * @defgroup Calibration_Block * @brief The non-factory calibration routines. * @{ * @defgroup Calibration_Functions * @{ * * */ /** * @brief Turn on High power 1.8V/1.1V reference and 2.5V LP reference. * @return return none. */ static void __AD5940_ReferenceON(void) { AFERefCfg_Type ref_cfg; /* Turn ON ADC/DAC and LPDAC reference */ ref_cfg.Hp1V1BuffEn = bTRUE; ref_cfg.Hp1V8BuffEn = bTRUE; ref_cfg.HpBandgapEn = bTRUE; ref_cfg.HSDACRefEn = bTRUE; ref_cfg.LpBandgapEn = bTRUE; ref_cfg.LpRefBufEn = bTRUE; ref_cfg.Disc1V1Cap = bFALSE; ref_cfg.Disc1V8Cap = bFALSE; ref_cfg.Hp1V8Ilimit = bFALSE; ref_cfg.Hp1V8ThemBuff = bFALSE; ref_cfg.Lp1V1BuffEn = bFALSE; ref_cfg.Lp1V8BuffEn = bFALSE; ref_cfg.LpRefBoostEn = bFALSE; AD5940_REFCfgS(&ref_cfg); } /** * @brief Turn on ADC to sample one SINC2 data. * @return return ADCCode. */ static uint32_t __AD5940_TakeMeasurement(int32_t *time_out) { uint32_t ADCCode = 0; AD5940_INTCClrFlag(AFEINTSRC_SINC2RDY); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_SINC2NOTCH, bTRUE);/* Start conversion */ do { AD5940_Delay10us(1); /* Delay 10us */ if(AD5940_INTCTestFlag(AFEINTC_1,AFEINTSRC_SINC2RDY)) { ADCCode = AD5940_ReadAfeResult(AFERESULT_SINC2); break; } if(*time_out != -1) (*time_out)--; }while(*time_out != 0); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_SINC2NOTCH, bFALSE);/* Stop conversion */ return ADCCode; } /** @brief Calibrate ADC PGA @param pADCPGACal: PGA calibration parameters include filter setup and PGA gain. @return AD5940ERR_OK. **/ AD5940Err AD5940_ADCPGACal(ADCPGACal_Type *pADCPGACal) { const float kFactor = 1.835f/1.82f; ADCBaseCfg_Type adc_base; int32_t time_out; uint32_t INTCCfg; int32_t ADCCode; BoolFlag bADCClk32MHzMode; uint32_t regaddr_gain, regaddr_offset; if(pADCPGACal == NULL) return AD5940ERR_NULLP; if(pADCPGACal->ADCPga > ADCPGA_9) return AD5940ERR_PARA; /* Parameter Error */ if(pADCPGACal->AdcClkFreq > (32000000*0.8)) bADCClk32MHzMode = bTRUE; /** * Determine Gain calibration method according to different gain value... * and calibration register * */ static const struct _cal_registers { uint16_t gain_reg; uint16_t offset_reg; }cal_registers[] = { {REG_AFE_ADCGAINGN1,REG_AFE_ADCOFFSETGN1}, {REG_AFE_ADCGAINGN1P5,REG_AFE_ADCOFFSETGN1P5}, {REG_AFE_ADCGAINGN2,REG_AFE_ADCOFFSETGN2}, {REG_AFE_ADCGAINGN4,REG_AFE_ADCOFFSETGN4}, {REG_AFE_ADCGAINGN9,REG_AFE_ADCOFFSETGN9}, }; regaddr_gain = cal_registers[pADCPGACal->ADCPga].gain_reg; regaddr_offset = cal_registers[pADCPGACal->ADCPga].offset_reg; /* Do initialization */ __AD5940_ReferenceON(); ADCFilterCfg_Type adc_filter; /* Initialize ADC filters ADCRawData-->SINC3-->SINC2+NOTCH. Use SIN2 data for calibration-->Lower noise */ adc_filter.ADCSinc3Osr = pADCPGACal->ADCSinc3Osr; adc_filter.ADCSinc2Osr = pADCPGACal->ADCSinc2Osr; /* 800KSPS/4/1333 = 150SPS */ adc_filter.ADCAvgNum = ADCAVGNUM_2; /* Don't care about it. Average function is only used for DFT */ adc_filter.ADCRate = bADCClk32MHzMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; /* If ADC clock is 32MHz, then set it to ADCRATE_1P6MHZ. Default is 16MHz, use ADCRATE_800KHZ. */ adc_filter.BpNotch = bTRUE; /* SINC2+Notch is one block, when bypass notch filter, we can get fresh data from SINC2 filter. */ adc_filter.BpSinc3 = bFALSE; /* We use SINC3 filter. */ adc_filter.Sinc2NotchEnable = bTRUE; /* Enable the SINC2+Notch block. You can also use function AD5940_AFECtrlS */ AD5940_ADCFilterCfgS(&adc_filter); /* Turn ON reference and ADC power, and DAC reference. We use DAC 1.8V reference to calibrate ADC because of the ADC reference bug. */ AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Disable all */ AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_HPREFPWR|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|AFECTRL_SINC2NOTCH, bTRUE); AD5940_Delay10us(25); /* Wait 250us for reference power up */ /* INTC configure and open calibration lock */ INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ AD5940_WriteReg(REG_AFE_CALDATLOCK, KEY_CALDATLOCK); /* Unlock KEY */ /* Do offset calibration. */ if(pADCPGACal->PGACalType != PGACALTYPE_GAIN){ /* Need offset calibration */ int32_t ExpectedCode = 0x8000; /* Ideal ADC output */ AD5940_WriteReg(regaddr_offset, 0); /* Reset offset register */ adc_base.ADCMuxP = ADCMUXP_VSET1P1; adc_base.ADCMuxN = ADCMUXN_VSET1P1; /* Short input with common voltage set to 1.11v */ adc_base.ADCPga = pADCPGACal->ADCPga; /* Set correct Gain value. */ AD5940_ADCBaseCfgS(&adc_base); AD5940_Delay10us(5); /* Wait for sometime */ ADCCode = 0; for(int i=0; i<8; i++) { /* ADC offset calibration register has resolution of 0.25LSB. take full use of it. */ time_out = pADCPGACal->TimeOut10us; /* Reset time out counter */ ADCCode += __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) goto ADCPGACALERROR_TIMEOUT; /* Time out error. */ } /* Calculate and write the result to registers before gain calibration */ ADCCode = (ExpectedCode<<3) - ADCCode; /* We will shift back 1bit below */ /** * AD5940 use formular Output = gain*(input + offset) for calibration. * So, the measured results should be divided by gain to get value for offset register. */ uint32_t gain = AD5940_ReadReg(regaddr_gain); ADCCode = (ADCCode*0x4000)/gain; ADCCode = ((ADCCode+1)>>1)&0x7fff; /* Round 0.5 */ AD5940_WriteReg(regaddr_offset, ADCCode); } /* Do gain calibration */ if(pADCPGACal->PGACalType != PGACALTYPE_OFFSET) /* Need gain calibration */ { int32_t ExpectedGainCode; static const float ideal_pga_gain[]={1,1.5,2,4,9}; AD5940_WriteReg(regaddr_gain, 0x4000); /* Reset gain register */ if(pADCPGACal->ADCPga <= ADCPGA_2) { //gain1,1.5,2 could use reference directly adc_base.ADCMuxP = ADCMUXP_VREF1P8DAC; adc_base.ADCMuxN = ADCMUXN_VSET1P1; ExpectedGainCode = (int32_t)((pADCPGACal->VRef1p82 - pADCPGACal->VRef1p11)*ideal_pga_gain[pADCPGACal->ADCPga]/\ pADCPGACal->VRef1p82*32768/kFactor)\ + 0x8000; } else { //gain4,9 use DAC generated voltage adc_base.ADCMuxP = ADCMUXP_P_NODE; adc_base.ADCMuxN = ADCMUXN_N_NODE; /* Setup HSLOOP to generate voltage for GAIN4/9 calibration. */ AD5940_AFECtrlS(AFECTRL_EXTBUFPWR|AFECTRL_INAMPPWR|AFECTRL_HSTIAPWR|AFECTRL_WG, bTRUE); HSLoopCfg_Type hsloop_cfg; hsloop_cfg.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2; hsloop_cfg.HsDacCfg.HsDacGain = HSDACGAIN_1; hsloop_cfg.HsDacCfg.HsDacUpdateRate = 7; hsloop_cfg.HsTiaCfg.DiodeClose = bFALSE; hsloop_cfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1; hsloop_cfg.HsTiaCfg.HstiaCtia = 31; hsloop_cfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN; hsloop_cfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN; hsloop_cfg.HsTiaCfg.HstiaDe1Rload = HSTIADERLOAD_OPEN; hsloop_cfg.HsTiaCfg.HstiaDe1Rtia = HSTIADERTIA_OPEN; hsloop_cfg.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_200; hsloop_cfg.SWMatCfg.Dswitch = SWD_OPEN; hsloop_cfg.SWMatCfg.Pswitch = SWP_PL; hsloop_cfg.SWMatCfg.Nswitch = SWN_NL; hsloop_cfg.SWMatCfg.Tswitch = SWT_TRTIA; hsloop_cfg.WgCfg.GainCalEn = bTRUE; hsloop_cfg.WgCfg.OffsetCalEn = bTRUE; hsloop_cfg.WgCfg.WgType = WGTYPE_MMR; uint32_t HSDACCode; if(pADCPGACal->ADCPga == ADCPGA_4) HSDACCode = 0x800 + 0x300; /* 0x300--> 0x300/0x1000*0.8*BUFFERGAIN2 = 0.3V. */ else if(pADCPGACal->ADCPga == ADCPGA_9) HSDACCode = 0x800 + 0x155; /* 0x155--> 0x155/0x1000*0.8*BUFFERGAIN2 = 0.133V. */ hsloop_cfg.WgCfg.WgCode = HSDACCode; AD5940_HSLoopCfgS(&hsloop_cfg); //measure expected code adc_base.ADCPga = ADCPGA_1P5; AD5940_ADCBaseCfgS(&adc_base); AD5940_Delay10us(5); time_out = pADCPGACal->TimeOut10us; /* Reset time out counter */ ExpectedGainCode = 0x8000 + (int32_t)((__AD5940_TakeMeasurement(&time_out) - 0x8000)/1.5f\ *ideal_pga_gain[pADCPGACal->ADCPga]); if(time_out == 0) goto ADCPGACALERROR_TIMEOUT; } adc_base.ADCPga = pADCPGACal->ADCPga; /* Set to gain under calibration */ AD5940_ADCBaseCfgS(&adc_base); AD5940_Delay10us(5); time_out = pADCPGACal->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); if(time_out == 0) goto ADCPGACALERROR_TIMEOUT; /* Calculate and write the result to registers */ ADCCode = (ExpectedGainCode - 0x8000)*0x4000/(ADCCode-0x8000); ADCCode &= 0x7fff; AD5940_WriteReg(regaddr_gain, ADCCode); } /* Restore INTC1 SINC2 configure */ if(INTCCfg&AFEINTSRC_SINC2RDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bFALSE); /* Disable SINC2 Interrupt */ AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ /* Done */ return AD5940ERR_OK; ADCPGACALERROR_TIMEOUT: AD5940_ADCConvtCtrlS(bFALSE); /* Stop conversion */ AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ return AD5940ERR_TIMEOUT; } /** * @brief Calibrate LPTIA offset * @param pLPTIAOffsetCal Pointer to LPTIA offset calibration settings. * @return AD5940ERR_OK. **/ AD5940Err AD5940_LPTIAOffsetCal(LPTIAOffsetCal_Type *pLPTIAOffsetCal) { AD5940Err error = AD5940ERR_OK; LPLoopCfg_Type lploop_cfg; ADCBaseCfg_Type adc_base; ADCFilterCfg_Type adc_filter; int32_t time_out; uint32_t INTCCfg; int32_t ADCCode; BoolFlag bADCClk32MHzMode; if(pLPTIAOffsetCal == NULL) return AD5940ERR_NULLP; if(pLPTIAOffsetCal->AdcClkFreq > (32000000*0.8)) bADCClk32MHzMode = bTRUE; /* Step0: Do initialization */ /* Turn on AD5940 references in case it's disabled. */ __AD5940_ReferenceON(); lploop_cfg.LpAmpCfg.LpAmpSel = pLPTIAOffsetCal->LpAmpSel; lploop_cfg.LpAmpCfg.LpAmpPwrMod = pLPTIAOffsetCal->LpAmpPwrMod; /* Power mode will affect amp offset. */ lploop_cfg.LpAmpCfg.LpPaPwrEn = bTRUE; lploop_cfg.LpAmpCfg.LpTiaPwrEn = bTRUE; lploop_cfg.LpAmpCfg.LpTiaRf = LPTIARF_OPEN; lploop_cfg.LpAmpCfg.LpTiaRload = LPTIARLOAD_100R; lploop_cfg.LpAmpCfg.LpTiaRtia = pLPTIAOffsetCal->LpTiaRtia; lploop_cfg.LpAmpCfg.LpTiaSW = pLPTIAOffsetCal->LpTiaSW; /* Disconnect capacitors so it settles quickly */ lploop_cfg.LpDacCfg.LpdacSel = (pLPTIAOffsetCal->LpAmpSel == LPAMP0)?LPDAC0:LPDAC1; lploop_cfg.LpDacCfg.DacData12Bit = pLPTIAOffsetCal->DacData12Bit; lploop_cfg.LpDacCfg.DacData6Bit = pLPTIAOffsetCal->DacData6Bit; lploop_cfg.LpDacCfg.DataRst = bFALSE; lploop_cfg.LpDacCfg.LpDacRef = LPDACREF_2P5; lploop_cfg.LpDacCfg.LpDacSrc = LPDACSRC_MMR; lploop_cfg.LpDacCfg.LpDacVzeroMux = pLPTIAOffsetCal->LpDacVzeroMux; lploop_cfg.LpDacCfg.LpDacSW = LPDACSW_VZERO2LPTIA; lploop_cfg.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT; lploop_cfg.LpDacCfg.PowerEn = bTRUE; AD5940_LPLoopCfgS(&lploop_cfg); /* Initialize ADC filters ADCRawData-->SINC3-->SINC2+NOTCH. Use SIN2 data for calibration-->Lower noise */ adc_filter.ADCSinc3Osr = pLPTIAOffsetCal->ADCSinc3Osr; adc_filter.ADCSinc2Osr = pLPTIAOffsetCal->ADCSinc2Osr; /* 800KSPS/4/1333 = 150SPS */ adc_filter.ADCAvgNum = ADCAVGNUM_2; /* Don't care about it. Average function is only used for DFT */ adc_filter.ADCRate = bADCClk32MHzMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; /* If ADC clock is 32MHz, then set it to ADCRATE_1P6MHZ. Default is 16MHz, use ADCRATE_800KHZ. */ adc_filter.BpNotch = bTRUE; /* SINC2+Notch is one block, when bypass notch filter, we can get fresh data from SINC2 filter. */ adc_filter.BpSinc3 = bFALSE; /* We use SINC3 filter. */ adc_filter.Sinc2NotchEnable = bTRUE; /* Enable the SINC2+Notch block. You can also use function AD5940_AFECtrlS */ AD5940_ADCFilterCfgS(&adc_filter); /* Initialize ADC MUx and PGA */ if(pLPTIAOffsetCal->LpAmpSel == LPAMP0) { adc_base.ADCMuxP = ADCMUXP_LPTIA0_P; adc_base.ADCMuxN = ADCMUXN_LPTIA0_N; } else { adc_base.ADCMuxP = ADCMUXP_LPTIA1_P; adc_base.ADCMuxN = ADCMUXN_LPTIA1_N; } adc_base.ADCPga = pLPTIAOffsetCal->ADCPga; /* Set correct Gain value. */ AD5940_ADCBaseCfgS(&adc_base); /* Turn ON ADC and its reference. And SINC2. */ AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Disable all firstly, we only enable things we use */ AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_HPREFPWR|AFECTRL_SINC2NOTCH, bTRUE); AD5940_Delay10us(25); /* Wait 250us for reference power up */ /* INTC configure and open calibration lock */ INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ AD5940_WriteReg(REG_AFE_CALDATLOCK, KEY_CALDATLOCK); /* Unlock KEY */ /* Do offset calibration. */ { int32_t ExpectedCode = 0x8000; /* Ideal ADC output */ AD5940_WriteReg(REG_AFE_ADCOFFSETLPTIA0, 0); /* Reset offset register */ if(pLPTIAOffsetCal->SettleTime10us > 0) AD5940_Delay10us(pLPTIAOffsetCal->SettleTime10us); /* Delay 10us */ time_out = pLPTIAOffsetCal->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPTIAOFFSETCALERROR; } /* Time out error. */ /* Calculate and write the result to registers before gain calibration */ ADCCode = ((ExpectedCode - ADCCode)<<3); /* We will shift back 1bit below */ ADCCode = ((ADCCode+1)>>1); /* Round 0.5 */ if((ADCCode > 0x3fff) || (ADCCode < -0x4000)) /* The register used for offset calibration is limited to -0x4000 to 0x3fff */ { error = AD5940ERR_CALOR; goto LPTIAOFFSETCALERROR; } ADCCode &= 0x7fff; if(pLPTIAOffsetCal->LpAmpSel == LPAMP0) AD5940_WriteReg(REG_AFE_ADCOFFSETLPTIA0, ADCCode); else AD5940_WriteReg(REG_AFE_ADCOFFSETLPTIA1, ADCCode); } /* Restore INTC1 SINC2 configure */ if(INTCCfg&AFEINTSRC_SINC2RDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bFALSE); /* Disable SINC2 Interrupt */ AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ /* Done */ return AD5940ERR_OK; LPTIAOFFSETCALERROR: AD5940_ADCConvtCtrlS(bFALSE); /* Stop conversion */ AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ return error; } /** * @brief Calibrate HSTIA offset-ongoing. * @param pHSTIAOffsetCal: pointer to configuration. * @return AD5940ERR_OK. **/ AD5940Err AD5940_HSTIAOffsetCal(LPTIAOffsetCal_Type *pHSTIAOffsetCal) { return AD5940ERR_OK; } /** * @brief Measure HSTIA internal RTIA impedance. * @param pCalCfg: pointer to calibration structure. * @param pResult: Pointer to a variable that used to store result. * If bPolarResult in structure is set, then use type fImpPol_Type otherwise use fImpCar_Type. * @return AD5940ERR_OK if succeed. **/ AD5940Err AD5940_HSRtiaCal(HSRTIACal_Type *pCalCfg, void *pResult) { /***** CALIBRATION METHOD ****** 1) Measure the complex voltage V_Rcal across the calibration DUT (Rcal). 2) Measure the complex voltage V_Rtia across Rtia [HSTIA_P (output) - HSTIA_N]. 3) Note Rtia carries the same current as Rcal; I_Rtia = I_exc = I_Rcal 4) Implement the equation: Rtia = V_Rtia / I_Rtia --> Rtia = (V_Rtia / V_Rcal) * Rcal *******************************/ AFERefCfg_Type aferef_cfg; HSLoopCfg_Type hs_loop; DSPCfg_Type dsp_cfg; uint32_t INTCCfg; BoolFlag bADCClk32MHzMode = bFALSE; uint32_t ExcitBuffGain = EXCITBUFGAIN_2; uint32_t HsDacGain = HSDACGAIN_1; float ExcitVolt; /* Excitation voltage, unit is mV */ uint32_t RtiaVal; uint32_t const HpRtiaTable[]={200,1000,5000,10000,20000,40000,80000,160000,0}; uint32_t const HSTIADERLOADTable[]={0,10,30,50,100,999999999999}; uint32_t const HSTIADERTIATable[] = {50,100,200,1000,5000,10000,20000,40000,80000,160000,0,999999999999999}; uint32_t WgAmpWord; iImpCar_Type DftRcalVolt, DftRtiaVolt; if(pCalCfg == NULL) return AD5940ERR_NULLP; if(pCalCfg->fRcal == 0) return AD5940ERR_PARA; //if(pCalCfg->HsTiaCfg.HstiaRtiaSel > HSTIARTIA_160K) // return AD5940ERR_PARA; //if(pCalCfg->HsTiaCfg.HstiaRtiaSel == HSTIARTIA_OPEN) // return AD5940ERR_PARA; /* Do not support calibrating DE0-RTIA */ if(pResult == NULL) return AD5940ERR_NULLP; if(pCalCfg->AdcClkFreq > (32000000*0.8)) bADCClk32MHzMode = bTRUE; /* Calculate the excitation voltage we should use based on RCAL/Rtia */ if(pCalCfg->HsTiaCfg.HstiaRtiaSel == HSTIARTIA_OPEN) { if(pCalCfg->HsTiaCfg.HstiaDeRtia == HSTIADERTIA_TODE) { RtiaVal = pCalCfg->HsTiaCfg.ExtRtia; } else { RtiaVal = pCalCfg->HsTiaCfg.ExtRtia + HSTIADERLOADTable[pCalCfg->HsTiaCfg.HstiaDeRload] + HSTIADERTIATable[pCalCfg->HsTiaCfg.HstiaDeRtia]; } } else RtiaVal = HpRtiaTable[pCalCfg->HsTiaCfg.HstiaRtiaSel]; /* DAC output voltage calculation Note: RCAL value should be similar to RTIA so the accuracy is best. HSTIA output voltage should be limited to 0.2V to AVDD-0.2V, with 1.1V bias. We use 80% of this range for safe. Because the bias voltage is fixed to 1.1V, so for AC signal maximum amplitude is 1.1V-0.2V = 0.9Vp. That's 1.8Vpp. Formula is: ExcitVolt(in mVpp) = (1800mVpp*80% / RTIA) * RCAL ADC input range is +-1.5V which is enough for calibration. */ ExcitVolt = 1800*0.8*pCalCfg->fRcal/RtiaVal; if(ExcitVolt <= 800*0.05) /* Voltage is so small that we can enable the attenuator of DAC(1/5) and Excitation buffer(1/4). 800mVpp is the DAC output voltage */ { ExcitBuffGain = EXCITBUFGAIN_0P25; HsDacGain = HSDACGAIN_0P2; /* Excitation buffer voltage full range is 800mVpp*0.05 = 40mVpp */ WgAmpWord = ((uint32_t)(ExcitVolt/40*2047*2)+1)>>1; /* Assign value with rounding (0.5 LSB error) */ } else if(ExcitVolt <= 800*0.25) /* Enable Excitation buffer attenuator */ { ExcitBuffGain = EXCITBUFGAIN_0P25; HsDacGain = HSDACGAIN_1; /* Excitation buffer voltage full range is 800mVpp*0.25 = 200mVpp */ WgAmpWord = ((uint32_t)(ExcitVolt/200*2047*2)+1)>>1; /* Assign value with rounding (0.5 LSB error) */ } else if(ExcitVolt <= 800*0.4) /* Enable DAC attenuator */ { ExcitBuffGain = EXCITBUFGAIN_2; HsDacGain = HSDACGAIN_0P2; /* Excitation buffer voltage full range is 800mVpp*0.4 = 320mV */ WgAmpWord = ((uint32_t)(ExcitVolt/320*2047*2)+1)>>1; /* Assign value with rounding (0.5 LSB error) */ } else /* No attenuator is needed. This is the best condition which means RTIA is close to RCAL */ { ExcitBuffGain = EXCITBUFGAIN_2; HsDacGain = HSDACGAIN_1; /* Excitation buffer voltage full range is 800mVpp*2=1600mVpp */ WgAmpWord = ((uint32_t)(ExcitVolt/1600*2047*2)+1)>>1; /* Assign value with rounding (0.5 LSB error) */ } if(WgAmpWord > 0x7ff) WgAmpWord = 0x7ff; /*INTC configuration */ INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Init all to disable state */ /* Configure reference system */ aferef_cfg.HpBandgapEn = bTRUE; aferef_cfg.Hp1V1BuffEn = bTRUE; aferef_cfg.Hp1V8BuffEn = bTRUE; aferef_cfg.Disc1V1Cap = bFALSE; aferef_cfg.Disc1V8Cap = bFALSE; aferef_cfg.Hp1V8ThemBuff = bFALSE; aferef_cfg.Hp1V8Ilimit = bFALSE; aferef_cfg.Lp1V1BuffEn = bFALSE; aferef_cfg.Lp1V8BuffEn = bFALSE; aferef_cfg.LpBandgapEn = bFALSE; aferef_cfg.LpRefBufEn = bFALSE; aferef_cfg.LpRefBoostEn = bFALSE; AD5940_REFCfgS(&aferef_cfg); /* Configure HP Loop */ hs_loop.HsDacCfg.ExcitBufGain = ExcitBuffGain; hs_loop.HsDacCfg.HsDacGain = HsDacGain; hs_loop.HsDacCfg.HsDacUpdateRate = 7; /* Set it to highest update rate */ memcpy(&hs_loop.HsTiaCfg, &pCalCfg->HsTiaCfg, sizeof(pCalCfg->HsTiaCfg)); hs_loop.SWMatCfg.Dswitch = SWD_RCAL0; hs_loop.SWMatCfg.Pswitch = SWP_RCAL0; hs_loop.SWMatCfg.Nswitch = SWN_RCAL1; hs_loop.SWMatCfg.Tswitch = SWT_RCAL1|SWT_TRTIA|SWT_AIN1; hs_loop.WgCfg.WgType = WGTYPE_SIN; hs_loop.WgCfg.GainCalEn = bTRUE; hs_loop.WgCfg.OffsetCalEn = bTRUE; hs_loop.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(pCalCfg->fFreq, pCalCfg->SysClkFreq); hs_loop.WgCfg.SinCfg.SinAmplitudeWord = WgAmpWord; hs_loop.WgCfg.SinCfg.SinOffsetWord = 0; hs_loop.WgCfg.SinCfg.SinPhaseWord = 0; AD5940_HSLoopCfgS(&hs_loop); /* Configure DSP */ dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_N_NODE; dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_P_NODE; dsp_cfg.ADCBaseCfg.ADCPga = ADCPGA_1P5; AD5940_StructInit(&dsp_cfg.ADCDigCompCfg, sizeof(dsp_cfg.ADCDigCompCfg)); dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16; /* Don't care because it's disabled */ dsp_cfg.ADCFilterCfg.ADCRate = bADCClk32MHzMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; dsp_cfg.ADCFilterCfg.ADCSinc2Osr = pCalCfg->ADCSinc2Osr; dsp_cfg.ADCFilterCfg.ADCSinc3Osr = pCalCfg->ADCSinc3Osr; dsp_cfg.ADCFilterCfg.BpNotch = bTRUE; dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE; dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE; memcpy(&dsp_cfg.DftCfg, &pCalCfg->DftCfg, sizeof(pCalCfg->DftCfg)); memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg)); AD5940_DSPCfgS(&dsp_cfg); /* Enable all of them. They are automatically turned off during hibernate mode to save power */ AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\ /*AFECTRL_WG|*/AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\ AFECTRL_SINC2NOTCH, bTRUE); /***** MEASURE VOLTAGE ACROSS RCAL *****/ AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE); /* Enable Waveform generator, ADC power */ //wait for sometime. AD5940_Delay10us(25); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */ /* Wait until DFT ready */ while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */ AD5940_INTCClrFlag(AFEINTSRC_DFTRDY); DftRcalVolt.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL); DftRcalVolt.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE); /***** MEASURE VOLTAGE ACROSS RTIA *****/ AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N); AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE); /* Enable Waveform generator, ADC power */ //wait for sometime. AD5940_Delay10us(25); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Start ADC convert and DFT */ /* Wait until DFT ready */ while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */ AD5940_INTCClrFlag(AFEINTSRC_DFTRDY); DftRtiaVolt.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL); DftRtiaVolt.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE); if(DftRcalVolt.Real&(1L<<17)) DftRcalVolt.Real |= 0xfffc0000; if(DftRcalVolt.Image&(1L<<17)) DftRcalVolt.Image |= 0xfffc0000; if(DftRtiaVolt.Real&(1L<<17)) DftRtiaVolt.Real |= 0xfffc0000; if(DftRtiaVolt.Image&(1L<<17)) DftRtiaVolt.Image |= 0xfffc0000; /* ADC MUX is set to HSTIA_P and HSTIA_N. While the current flow through RCAL and then into RTIA, the current direction should be from HSTIA_N to HSTIA_P if we measure the voltage across RCAL by MUXSELP_P_NODE and MUXSELN_N_NODE. So here, we add a negative sign to results */ DftRtiaVolt.Image = -DftRtiaVolt.Image; DftRtiaVolt.Real = -DftRtiaVolt.Real; /* Current is measured by MUX HSTIA_P-HSTIA_N. It should be */ /* The impedance engine inside of AD594x give us Real part and Imaginary part of DFT. Due to technology used, the Imaginary part in register is the opposite number. So we add a negative sign on the Imaginary part of results. */ DftRtiaVolt.Image = -DftRtiaVolt.Image; DftRcalVolt.Image = -DftRcalVolt.Image; /***** Implement RTIA = (V_Rtia / V_Rcal) * Rcal ******/ fImpCar_Type temp; temp = AD5940_ComplexDivInt(&DftRtiaVolt, &DftRcalVolt); temp.Real *= pCalCfg->fRcal; temp.Image *= pCalCfg->fRcal; if(pCalCfg->bPolarResult == bFALSE) { *(fImpCar_Type*)pResult = temp; } else { ((fImpPol_Type*)pResult)->Magnitude = AD5940_ComplexMag(&temp); ((fImpPol_Type*)pResult)->Phase = AD5940_ComplexPhase(&temp); } /* Restore INTC1 DFT configure */ if(INTCCfg&AFEINTSRC_DFTRDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bFALSE); /* Disable DFT Interrupt */ return AD5940ERR_OK; } /** * @brief Measure LPTIA internal RTIA impedance with HSTIA. This is the recommended method for LPTIA RTIA calibration. * @param pCalCfg: pointer to calibration structure. * @param pResult: Pointer to a variable that used to store result. * If bPolarResult in structure is set, then use type fImpPol_Type otherwise use fImpCar_Type. * @return AD5940ERR_OK if succeed. **/ AD5940Err AD5940_LPRtiaCal(LPRTIACal_Type *pCalCfg, void *pResult) { HSLoopCfg_Type hs_loop; LPLoopCfg_Type lp_loop; DSPCfg_Type dsp_cfg; ADCBaseCfg_Type *pADCBaseCfg; SWMatrixCfg_Type *pSWCfg; uint32_t INTCCfg, reg_afecon; BoolFlag bADCClk32MHzMode = bFALSE; BoolFlag bDCMode = bFALSE; /* Indicate if frequency is 0, which means we calibrate at DC. */ float ExcitVolt; /* Excitation voltage, unit is mV */ uint32_t RtiaVal; /* RTIA value table when RLOAD set to 100Ohm */ uint32_t const LpRtiaTable[]={0,110,1000,2000,3000,4000,6000,8000,10000,12000,16000,20000,24000,30000,32000,40000,48000,64000,85000,96000,100000,120000,128000,160000,196000,256000,512000}; float const ADCPGAGainTable[] = {1, 1.5, 2, 4, 9}; uint32_t WgAmpWord; uint32_t ADCPgaGainRtia, ADCPgaGainRcal; float GainRatio; iImpCar_Type DftRcal, DftRtia; if(pCalCfg == NULL) return AD5940ERR_NULLP; /* Parameters illegal */ if(pCalCfg->fRcal == 0) return AD5940ERR_PARA; if(pCalCfg->LpTiaRtia > LPTIARTIA_512K) return AD5940ERR_PARA; if(pCalCfg->LpTiaRtia == LPTIARTIA_OPEN) return AD5940ERR_PARA; /* Not supported now. By setting RTIA to open and set corresponding switches can calibrate external RTIA */ if(pResult == NULL) return AD5940ERR_NULLP; if(pCalCfg->AdcClkFreq > (32000000*0.8)) bADCClk32MHzMode = bTRUE; /* Clock frequency is high. */ if(pCalCfg->fFreq == 0.0f) /* Frequency is zero means we calibrate RTIA at DC. */ bDCMode = bTRUE; /* Init two pointers */ pSWCfg = &hs_loop.SWMatCfg; pADCBaseCfg = &dsp_cfg.ADCBaseCfg; /* Calculate the excitation voltage we should use based on RCAL/Rtia */ RtiaVal = LpRtiaTable[pCalCfg->LpTiaRtia]; /* * DAC output voltage calculation * Note: RCAL value should be similar to RTIA so the accuracy is best. * LPTIA output voltage should be limited to 0.3V to AVDD-0.4V, with 1.3V bias. We use 80% of this range for safe. * That's 2.0Vpp*80%@2.7V AVDD * Formula is: ExcitVolt(in mVpp) = (2000mVpp*80% / RTIA) * RCAL * ADC input range is +-1.5V which is enough for calibration. * Limitations: * Note: HSTIA output range is AVDD-0.4V to AGND+0.2V * HSTIA input common voltage range is 0.3V to AVDD-0.7V; * When AVDD is 2.7V, the input range is 0.3V to 2.0V; * If we set Vbias to 1.3V, then maximum AC signal is 0.7Vp*2 = 1.4Vpp. * Maximum AC signal is further limited by HSTIA RTIA=200Ohm, when RCAL is 200Ohm(for ADuCM355). The maximum output of HSTIA is limited to 2.3V. * Maximum Vzero voltage is 1.9V when Rcal is 200Ohm and Switch On resistance is 50Ohm*2. Vzero_max = 1.3V + (2.3V-1.3V)/(200+200+50*2)*300. * Maximum AC signal is (1.9-1.3)*2 = 1.2Vpp(for ADuCM355, RCAl=200Ohm). */ /** @cond */ #define MAXVOLT_P2P 1400 /* Maximum peak to peak voltage 1200mV for ADuCM355. */ /* Maximum peak2peak voltage for AD5940 10kOhm RCAL is 1400mV */ #define __MAXVOLT_AMP_CODE (MAXVOLT_P2P*2047L/2200) /** @endcond */ ExcitVolt = 2000*0.8*pCalCfg->fRcal/RtiaVal; WgAmpWord = ((uint32_t)(ExcitVolt/2200*2047*2)+1)>>1; /* Assign value with rounding (0.5 LSB error) */ if(WgAmpWord > __MAXVOLT_AMP_CODE) WgAmpWord = __MAXVOLT_AMP_CODE; /** * Determine the best ADC PGA gain for both RCAL and RTIA voltage measurement. */ { float RtiaVolt, RcalVolt, temp; ExcitVolt = WgAmpWord*2000.0f/2047; /* 2000mVpp -->ExcitVolt in Peak to Peak unit */ RtiaVolt = ExcitVolt/(pCalCfg->fRcal + 100)*RtiaVal; RcalVolt = RtiaVolt/RtiaVal*pCalCfg->fRcal; /* The input range of ADC is 1.5Vp, we calculate how much gain we need */ temp = 3000.0f/RcalVolt; if(temp >= 9.0f) ADCPgaGainRcal = ADCPGA_9; else if(temp >= 4.0f) ADCPgaGainRcal = ADCPGA_4; else if(temp >= 2.0f) ADCPgaGainRcal = ADCPGA_2; else if(temp >= 1.5f) ADCPgaGainRcal = ADCPGA_1P5; else ADCPgaGainRcal = ADCPGA_1; temp = 3000.0f/RtiaVolt; if(temp >= 9.0f) ADCPgaGainRtia = ADCPGA_9; else if(temp >= 4.0f) ADCPgaGainRtia = ADCPGA_4; else if(temp >= 2.0f) ADCPgaGainRtia = ADCPGA_2; else if(temp >= 1.5f) ADCPgaGainRtia = ADCPGA_1P5; else ADCPgaGainRtia = ADCPGA_1; GainRatio = ADCPGAGainTable[ADCPgaGainRtia]/ADCPGAGainTable[ADCPgaGainRcal]; } reg_afecon = AD5940_ReadReg(REG_AFE_AFECON); /* INTC configuration */ INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY|AFEINTSRC_SINC2RDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ AD5940_INTCClrFlag(AFEINTSRC_ALLINT); AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Init all to disable state */ /* Configure reference system */ __AD5940_ReferenceON(); /* Configure DSP */ AD5940_StructInit(&dsp_cfg, sizeof(dsp_cfg)); dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16; /* Don't care because it's disabled */ dsp_cfg.ADCFilterCfg.ADCRate = bADCClk32MHzMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; dsp_cfg.ADCFilterCfg.ADCSinc2Osr = pCalCfg->ADCSinc2Osr; dsp_cfg.ADCFilterCfg.ADCSinc3Osr = pCalCfg->ADCSinc3Osr; dsp_cfg.ADCFilterCfg.BpNotch = bTRUE; dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE; dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE; memcpy(&dsp_cfg.DftCfg, &pCalCfg->DftCfg, sizeof(pCalCfg->DftCfg)); AD5940_DSPCfgS(&dsp_cfg); /* Configure LP Loop */ AD5940_StructInit(&lp_loop, sizeof(lp_loop)); /* Configure LP Amplifies(LPPA and LPTIA). We won't use LP-PA */ lp_loop.LpDacCfg.LpdacSel = (pCalCfg->LpAmpSel == LPAMP0)?LPDAC0:LPDAC1; lp_loop.LpDacCfg.DacData12Bit = 0x800; /* Controlled by WG */ lp_loop.LpDacCfg.DacData6Bit = 32; /* middle scale value */ lp_loop.LpDacCfg.DataRst =bFALSE; /* Do not keep DATA registers at reset status */ lp_loop.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VZERO2HSTIA; lp_loop.LpDacCfg.LpDacRef = LPDACREF_2P5; /* Select internal 2.5V reference */ lp_loop.LpDacCfg.LpDacSrc = LPDACSRC_WG; /* The LPDAC data comes from WG not MMR in this case */ lp_loop.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_6BIT; /* Connect Vbias signal to 6Bit LPDAC output */ lp_loop.LpDacCfg.LpDacVzeroMux = LPDACVZERO_12BIT; /* Connect Vzero signal to 12bit LPDAC output */ lp_loop.LpDacCfg.PowerEn = bTRUE; /* Power up LPDAC */ lp_loop.LpAmpCfg.LpAmpSel = pCalCfg->LpAmpSel; lp_loop.LpAmpCfg.LpAmpPwrMod = pCalCfg->LpAmpPwrMod; /* Set low power amplifiers to normal power mode */ lp_loop.LpAmpCfg.LpPaPwrEn = bTRUE; /* Enable LP PA(potential-stat amplifier) power */ lp_loop.LpAmpCfg.LpTiaPwrEn = bTRUE; /* Enable LPTIA*/ lp_loop.LpAmpCfg.LpTiaRload = LPTIARLOAD_100R; lp_loop.LpAmpCfg.LpTiaRtia = pCalCfg->LpTiaRtia; lp_loop.LpAmpCfg.LpTiaRf = LPTIARF_OPEN; lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(6)|LPTIASW(8)|(pCalCfg->bWithCtia==bTRUE?LPTIASW(5)/*|LPTIASW(9)*/:0); AD5940_LPLoopCfgS(&lp_loop); /* Configure HS Loop */ AD5940_StructInit(&hs_loop, sizeof(hs_loop)); /* Take care of HSTIA, we need to disconnect internal RTIA because it connects to Tswitch directly. */ hs_loop.HsTiaCfg.DiodeClose = bFALSE; hs_loop.HsTiaCfg.HstiaBias = (pCalCfg->LpAmpSel == LPAMP0)?HSTIABIAS_VZERO0:HSTIABIAS_VZERO1; hs_loop.HsTiaCfg.HstiaCtia = 31; hs_loop.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN; hs_loop.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN; hs_loop.HsTiaCfg.HstiaDe1Rload = HSTIADERLOAD_OPEN; hs_loop.HsTiaCfg.HstiaDe1Rtia = HSTIADERTIA_OPEN; hs_loop.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_200; /* Configure HSDAC */ hs_loop.HsDacCfg.ExcitBufGain = 0; hs_loop.HsDacCfg.HsDacGain = 0; /* Don't care */ hs_loop.HsDacCfg.HsDacUpdateRate = 255; /* Lowest for LPDAC */ hs_loop.SWMatCfg.Dswitch = SWD_RCAL0|((pCalCfg->LpAmpSel == LPAMP0)?SWD_SE0:SWD_SE1); hs_loop.SWMatCfg.Pswitch = SWP_RCAL0; hs_loop.SWMatCfg.Nswitch = SWN_RCAL1; hs_loop.SWMatCfg.Tswitch = SWT_TRTIA|SWT_RCAL1; if(bDCMode) { int32_t time_out = -1; /* Always wait. */ int32_t offset_rcal, offset_rtia; /* Configure WG */ hs_loop.WgCfg.WgType = WGTYPE_MMR; hs_loop.WgCfg.WgCode = WgAmpWord; /* Amplitude word is exactly the maximum DC voltage we could use */ hs_loop.WgCfg.GainCalEn = bFALSE; /* We don't have calibration value for LPDAC, so we don't use it. */ hs_loop.WgCfg.OffsetCalEn = bFALSE; AD5940_HSLoopCfgS(&hs_loop); AD5940_WGDACCodeS(WgAmpWord + 0x800); AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_WG|AFECTRL_ADCPWR, bTRUE); /* Apply voltage to loop and turn on ADC */ /* Do offset measurement */ pSWCfg->Dswitch = SWD_RCAL0;//|SWD_SE0; /* Disconnect SE0 for now to measure the offset voltage. */ pSWCfg->Pswitch = SWP_RCAL0; pSWCfg->Nswitch = SWN_RCAL1; pSWCfg->Tswitch = SWT_TRTIA|SWT_RCAL1; AD5940_SWMatrixCfgS(pSWCfg); AD5940_Delay10us(1000); /* Wait some time here. */ /* Measure RCAL channel voltage offset */ pADCBaseCfg->ADCMuxN = ADCMUXN_N_NODE; pADCBaseCfg->ADCMuxP = ADCMUXP_P_NODE; pADCBaseCfg->ADCPga = ADCPgaGainRcal; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_Delay10us(50); /* Wait some time here. */ offset_rcal = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ /* Measure RTIA channel voltage offset */ if(pCalCfg->LpAmpSel == LPAMP0) { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA0_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA0_P; }else { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA1_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA1_P; } pADCBaseCfg->ADCPga = ADCPgaGainRtia; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_Delay10us(50); /* Wait some time here. */ offset_rtia = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ /* Connect LPTIA loop, let current flow to RTIA. */ pSWCfg->Dswitch = SWD_RCAL0|((pCalCfg->LpAmpSel == LPAMP0)?SWD_SE0:SWD_SE1); pSWCfg->Pswitch = SWP_RCAL0; pSWCfg->Nswitch = SWN_RCAL1; pSWCfg->Tswitch = SWT_TRTIA|SWT_RCAL1; AD5940_SWMatrixCfgS(pSWCfg); AD5940_Delay10us(1000); /* Wait some time here. */ /* Measure RCAL */ pADCBaseCfg = &dsp_cfg.ADCBaseCfg; pADCBaseCfg->ADCMuxN = ADCMUXN_N_NODE; pADCBaseCfg->ADCMuxP = ADCMUXP_P_NODE; pADCBaseCfg->ADCPga = ADCPgaGainRcal; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_Delay10us(50); /* Wait some time here. */ DftRcal.Real = (int32_t)__AD5940_TakeMeasurement(&time_out)- offset_rcal; DftRcal.Image = 0; /* Measure RTIA */ if(pCalCfg->LpAmpSel == LPAMP0) { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA0_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA0_P; }else { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA1_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA1_P; } pADCBaseCfg->ADCPga = ADCPgaGainRtia; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_Delay10us(50); /* Wait some time here. */ DftRtia.Real = (int32_t)__AD5940_TakeMeasurement(&time_out)- offset_rtia; DftRtia.Image = 0; } else { hs_loop.WgCfg.SinCfg.SinAmplitudeWord = WgAmpWord; hs_loop.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(pCalCfg->fFreq, pCalCfg->SysClkFreq); hs_loop.WgCfg.SinCfg.SinOffsetWord = 0; hs_loop.WgCfg.SinCfg.SinPhaseWord = 0; hs_loop.WgCfg.WgCode = 0; hs_loop.WgCfg.WgType = WGTYPE_SIN; hs_loop.WgCfg.GainCalEn = bFALSE; /* disable it */ hs_loop.WgCfg.OffsetCalEn = bFALSE; AD5940_HSLoopCfgS(&hs_loop); AD5940_INTCClrFlag(AFEINTSRC_DFTRDY); AD5940_AFECtrlS(AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR, bTRUE); AD5940_Delay10us(100); /* Wait for loop stable. */ pADCBaseCfg = &dsp_cfg.ADCBaseCfg; /* DFT on RCAL */ pADCBaseCfg->ADCMuxN = ADCMUXN_N_NODE; pADCBaseCfg->ADCMuxP = ADCMUXP_P_NODE; pADCBaseCfg->ADCPga = ADCPgaGainRcal; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE); AD5940_Delay10us(25); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Wait until DFT ready */ while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */ AD5940_INTCClrFlag(AFEINTSRC_DFTRDY); DftRcal.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL); DftRcal.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE); /* DFT on RTIA */ if(pCalCfg->LpAmpSel == LPAMP0) { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA0_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA0_P; }else { pADCBaseCfg->ADCMuxN = ADCMUXN_LPTIA1_N; pADCBaseCfg->ADCMuxP = ADCMUXP_LPTIA1_P; } pADCBaseCfg->ADCPga = ADCPgaGainRtia; AD5940_ADCBaseCfgS(pADCBaseCfg); AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_WG, bTRUE); AD5940_Delay10us(25); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE); /* Wait until DFT ready */ while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE); AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE); /* Stop ADC convert and DFT */ AD5940_INTCClrFlag(AFEINTSRC_DFTRDY); DftRtia.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL); DftRtia.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE); if(DftRcal.Real&(1L<<17)) DftRcal.Real |= 0xfffc0000; if(DftRcal.Image&(1L<<17)) DftRcal.Image |= 0xfffc0000; if(DftRtia.Real&(1L<<17)) DftRtia.Real |= 0xfffc0000; if(DftRtia.Image&(1L<<17)) DftRtia.Image |= 0xfffc0000; } /* The impedance engine inside of AD594x give us Real part and Imaginary part of DFT. Due to technology used, the Imaginary part in register is the opposite number. So we add a negative sign on the Imaginary part of results. */ DftRtia.Image = -DftRtia.Image; DftRcal.Image = -DftRcal.Image; fImpCar_Type res; /* RTIA = (DftRtia.Real, DftRtia.Image)/(DftRcal.Real, DftRcal.Image)*fRcal */ res = AD5940_ComplexDivInt(&DftRtia, &DftRcal); res.Real *= pCalCfg->fRcal/GainRatio; res.Image *= pCalCfg->fRcal/GainRatio; if(pCalCfg->bPolarResult == bFALSE) { ((fImpCar_Type*)pResult)->Real = res.Real; ((fImpCar_Type*)pResult)->Image = res.Image; } else { ((fImpPol_Type*)pResult)->Magnitude = AD5940_ComplexMag(&res); ((fImpPol_Type*)pResult)->Phase = AD5940_ComplexPhase(&res); } /* Restore INTC1 DFT configure */ if(INTCCfg&AFEINTSRC_DFTRDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bFALSE); /* Disable DFT Interrupt */ if(INTCCfg&AFEINTSRC_SINC2RDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bFALSE); /* Disable SINC2 Interrupt */ AD5940_WriteReg(REG_AFE_AFECON, reg_afecon); /* Restore AFECON register */ /* Open all switches in switch-matrix */ hs_loop.SWMatCfg.Dswitch = SWD_OPEN; hs_loop.SWMatCfg.Pswitch = SWP_OPEN; hs_loop.SWMatCfg.Nswitch = SWN_OPEN; hs_loop.SWMatCfg.Tswitch = SWT_OPEN; AD5940_SWMatrixCfgS(&hs_loop.SWMatCfg); return AD5940ERR_OK; } /** * @brief calibrate HSDAC output voltage using ADC. * @note It acutally calibrates voltage output of excitation buffer. * @param pCalCfg: pointer to configuration structure * @return return AD5940ERR_OK if succeeded. */ AD5940Err AD5940_HSDACCal(HSDACCal_Type *pCalCfg) { ADCBaseCfg_Type adc_base; ADCFilterCfg_Type adc_filter; HSLoopCfg_Type hsloop_cfg; LPLoopCfg_Type lploop_cfg; /* LSB_Numerator and LSB_Denometer are used to calculate the codes to write to calibration registers depending on which calibration register is used There are LSB_Numerator ADC LSBs in LSB_Denominator DAC Calibration LSBs*/ int32_t LSB_Numerator; int32_t LEB_Denominator; int32_t time_out; int32_t ADCCode; uint32_t HSDACCode = 0x800; /* Mid scale DAC */ uint32_t regaddr_offset; uint32_t ADCPGA_Sel; BoolFlag bHPMode; if(pCalCfg == NULL) return AD5940ERR_NULLP; if(pCalCfg->ExcitBufGain > 1) return AD5940ERR_PARA; if(pCalCfg->HsDacGain > 1) return AD5940ERR_PARA; bHPMode = pCalCfg->AfePwrMode == AFEPWR_HP?bTRUE:bFALSE; switch(pCalCfg->ExcitBufGain) { case EXCITBUFGAIN_2: regaddr_offset = bHPMode?REG_AFE_DACOFFSETHP:REG_AFE_DACOFFSET; if(pCalCfg->HsDacGain == HSDACGAIN_0P2) { LSB_Numerator = 40; LEB_Denominator = 14; ADCPGA_Sel = ADCPGA_4; } else { LSB_Numerator = 7; LEB_Denominator = 2; ADCPGA_Sel = ADCPGA_1; } break; case EXCITBUFGAIN_0P25: regaddr_offset = bHPMode?REG_AFE_DACOFFSETATTENHP:REG_AFE_DACOFFSETATTEN; if(pCalCfg->HsDacGain == HSDACGAIN_0P2) { LSB_Numerator = 5; LEB_Denominator = 14; } else { LSB_Numerator = 25; LEB_Denominator = 14; } ADCPGA_Sel = ADCPGA_4; break; default: return AD5940ERR_PARA; } /* Turn On References*/ __AD5940_ReferenceON(); /* Step0.0 Initialize ADC filters ADCRawData-->SINC3-->SINC2+NOTCH. Use SIN2 data for calibration-->Lower noise */ adc_filter.ADCSinc3Osr = pCalCfg->ADCSinc3Osr; adc_filter.ADCSinc2Osr = pCalCfg->ADCSinc2Osr; /* 800KSPS/4/1333 = 150SPS */ adc_filter.ADCAvgNum = ADCAVGNUM_2; /* Don't care about it. Average function is only used for DFT */ adc_filter.ADCRate = bHPMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; /* If ADC clock is 32MHz, then set it to ADCRATE_1P6MHZ. Default is 16MHz, use ADCRATE_800KHZ. */ adc_filter.BpNotch = bTRUE; /* SINC2+Notch is one block, when bypass notch filter, we can get fresh data from SINC2 filter. */ adc_filter.BpSinc3 = bFALSE; /* We use SINC3 filter. */ adc_filter.Sinc2NotchEnable = bTRUE; /* Enable the SINC2+Notch block. You can also use function AD5940_AFECtrlS */ AD5940_ADCFilterCfgS(&adc_filter); /* Step0.1 Initialize ADC basic function */ adc_base.ADCMuxP = ADCMUXP_P_NODE; adc_base.ADCMuxN = ADCMUXN_N_NODE; adc_base.ADCPga = ADCPGA_Sel; AD5940_ADCBaseCfgS(&adc_base); /* Step0.2 Configure LPDAC to connect VZERO to HSTIA */ lploop_cfg.LpDacCfg.LpdacSel = LPDAC0; lploop_cfg.LpDacCfg.DacData12Bit = 0x7C0; lploop_cfg.LpDacCfg.DacData6Bit = 0x1F; lploop_cfg.LpDacCfg.DataRst = bFALSE; lploop_cfg.LpDacCfg.LpDacRef = LPDACREF_2P5; lploop_cfg.LpDacCfg.LpDacSrc = LPDACSRC_MMR; lploop_cfg.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT; lploop_cfg.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT; lploop_cfg.LpDacCfg.PowerEn = bTRUE; lploop_cfg.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA|LPDACSW_VBIAS2PIN|LPDACSW_VZERO2HSTIA; AD5940_LPLoopCfgS(&lploop_cfg); /* Step0.3 Configure HSLOOP */ hsloop_cfg.HsDacCfg.ExcitBufGain = pCalCfg->ExcitBufGain; hsloop_cfg.HsDacCfg.HsDacGain = pCalCfg->HsDacGain; hsloop_cfg.HsDacCfg.HsDacUpdateRate = bHPMode?0x7:0x1B; hsloop_cfg.HsTiaCfg.DiodeClose = bFALSE; hsloop_cfg.HsTiaCfg.HstiaBias = HSTIABIAS_VZERO0; hsloop_cfg.HsTiaCfg.HstiaCtia = 8; hsloop_cfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN; hsloop_cfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN; hsloop_cfg.HsTiaCfg.HstiaDe1Rload = HSTIADERLOAD_OPEN; hsloop_cfg.HsTiaCfg.HstiaDe1Rtia = HSTIADERTIA_OPEN; hsloop_cfg.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_200; hsloop_cfg.SWMatCfg.Dswitch = SWD_RCAL0; hsloop_cfg.SWMatCfg.Pswitch = SWP_RCAL0; hsloop_cfg.SWMatCfg.Nswitch = SWN_RCAL1; hsloop_cfg.SWMatCfg.Tswitch = SWT_TRTIA|SWT_RCAL1; hsloop_cfg.WgCfg.GainCalEn = bTRUE; hsloop_cfg.WgCfg.OffsetCalEn = bTRUE; hsloop_cfg.WgCfg.WgType = WGTYPE_MMR; hsloop_cfg.WgCfg.WgCode = HSDACCode; AD5940_HSLoopCfgS(&hsloop_cfg); /* Step0.4 Turn ON reference and ADC power, and DAC power and DAC reference. We use DAC 1.8V reference to calibrate ADC. */ AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Disable all */ AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_HPREFPWR|AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|AFECTRL_SINC2NOTCH|\ AFECTRL_EXTBUFPWR|AFECTRL_INAMPPWR|AFECTRL_HSTIAPWR|AFECTRL_WG, bTRUE); AD5940_Delay10us(25); /* Wait 250us for reference power up */ /* Step0.5 INTC configure and open calibration lock */ AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ AD5940_WriteReg(REG_AFE_CALDATLOCK, KEY_CALDATLOCK); /* Unlock KEY */ /* Reset Offset register before calibration */ AD5940_WriteReg(regaddr_offset, 0); /* Update HSDACDAT after resetting calibration register */ AD5940_WriteReg(REG_AFE_HSDACDAT, 0x800); /* Step1: Do offset calibration. */ { int32_t ExpectedCode = 0x8000; /* Ideal ADC output */ AD5940_Delay10us(10); time_out = 1000; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); #ifdef ADI_DEBUG ADI_Print("Voltage before cal: %f \n", AD5940_ADCCode2Volt(ADCCode, ADCPGA_Sel, 1.82)); #endif if(time_out == 0) goto DACCALERROR_TIMEOUT; /* Time out error. */ ADCCode = ADCCode - ExpectedCode; ADCCode = (((ADCCode)*LEB_Denominator)/LSB_Numerator); if(ADCCode>0) ADCCode = 0xFFF - ADCCode; else ADCCode = -ADCCode; AD5940_WriteReg(regaddr_offset, ADCCode); AD5940_Delay10us(10); AD5940_WriteReg(REG_AFE_HSDACDAT, 0x800); AD5940_Delay10us(10); #ifdef ADI_DEBUG ADCCode = __AD5940_TakeMeasurement(&time_out); ADI_Print("Voltage after cal: %f \n", AD5940_ADCCode2Volt(ADCCode, ADCPGA_Sel, 1.82)); #endif } AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ return AD5940ERR_OK; DACCALERROR_TIMEOUT: AD5940_ADCConvtCtrlS(bFALSE); /* Stop conversion */ AD5940_WriteReg(REG_AFE_CALDATLOCK, 0); /* Lock KEY */ return AD5940ERR_TIMEOUT; } /** * @brief Use ADC to measure LPDAC offset and gain factor. * @note Assume ADC is accurate enough or accurate than LPDAC at least. * @param pCalCfg: pointer to structure. * @param pResult: the pointer to save calibration result. * @return AD5940ERR_OK if succeed. *LPDACCal() function is added in ad5940.c only to suggest an optional LPDAC calibration sequence. *It is not verified by ADI software team and user may use it at own risk. **/ AD5940Err AD5940_LPDACCal(LPDACCal_Type *pCalCfg, LPDACPara_Type *pResult) { AD5940Err error = AD5940ERR_OK; LPDACCfg_Type LpDacCfg; ADCBaseCfg_Type adc_base; ADCFilterCfg_Type adc_filter; int32_t time_out; uint32_t INTCCfg; int32_t ADCCode, ADCCodeVref1p1; BoolFlag bADCClk32MHzMode; if(pCalCfg == NULL) return AD5940ERR_NULLP; if(pResult == NULL) return AD5940ERR_NULLP; if(pCalCfg->AdcClkFreq > (32000000*0.8)) bADCClk32MHzMode = bTRUE; /* Step0: Do initialization */ /* Turn on AD5940 references in case it's disabled. */ __AD5940_ReferenceON(); LpDacCfg.LpdacSel = pCalCfg->LpdacSel; LpDacCfg.DacData12Bit = 0; LpDacCfg.DacData6Bit = 0; LpDacCfg.DataRst = bFALSE; LpDacCfg.LpDacRef = LPDACREF_2P5; LpDacCfg.LpDacSrc = LPDACSRC_MMR; LpDacCfg.LpDacSW = LPDACSW_VBIAS2PIN|LPDACSW_VZERO2PIN; LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT; LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT; LpDacCfg.PowerEn = bTRUE; AD5940_LPDACCfgS(&LpDacCfg); /* Initialize ADC filters ADCRawData-->SINC3-->SINC2+NOTCH. Use SIN2 data for calibration-->Lower noise */ adc_filter.ADCSinc3Osr = pCalCfg->ADCSinc3Osr; adc_filter.ADCSinc2Osr = pCalCfg->ADCSinc2Osr; /* 800KSPS/4/1333 = 150SPS */ adc_filter.ADCAvgNum = ADCAVGNUM_2; /* Don't care about it. Average function is only used for DFT */ adc_filter.ADCRate = bADCClk32MHzMode?ADCRATE_1P6MHZ:ADCRATE_800KHZ; /* If ADC clock is 32MHz, then set it to ADCRATE_1P6MHZ. Default is 16MHz, use ADCRATE_800KHZ. */ adc_filter.BpNotch = bTRUE; /* SINC2+Notch is one block, when bypass notch filter, we can get fresh data from SINC2 filter. */ adc_filter.BpSinc3 = bFALSE; /* We use SINC3 filter. */ adc_filter.Sinc2NotchEnable = bTRUE; /* Enable the SINC2+Notch block. You can also use function AD5940_AFECtrlS */ AD5940_ADCFilterCfgS(&adc_filter); /* Initialize ADC MUx and PGA */ adc_base.ADCMuxP = ADCMUXP_AGND; adc_base.ADCMuxN = ADCMUXN_VSET1P1; adc_base.ADCPga = ADCPGA_1; AD5940_ADCBaseCfgS(&adc_base); /* Turn ON ADC and its reference. And SINC2. */ AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); /* Disable all firstly, we only enable things we use */ AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_HPREFPWR|AFECTRL_SINC2NOTCH, bTRUE); AD5940_Delay10us(25); /* Wait 250us for reference power up */ /* INTC configure and open calibration lock */ INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bTRUE); /* Enable SINC2 Interrupt in INTC1 */ /* Step1: Measure internal 1.1V reference. */ { //AD5940_ADCMuxCfgS(ADCMUXP_AGND, ADCMUXN_VSET1P1); time_out = pCalCfg->TimeOut10us; /* Reset time out counter */ ADCCodeVref1p1 = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPDACCALERROR; } /* Time out error. */ /* Equation1: ADCCodeVref1p1 = AGND - Vref1p1 */ } /* Step2: Do offset measurement. */ { /* Equation2': ADCCode = Vbias0/1 - Vref1p1 */ AD5940_LPDACWriteS(0,0); /* Set LPDAC output voltage to 0.2V(zero code) */ if(pCalCfg->SettleTime10us > 0) AD5940_Delay10us(pCalCfg->SettleTime10us); /* Delay nx10us */ if(pCalCfg->LpdacSel == LPDAC0) AD5940_ADCMuxCfgS(ADCMUXP_VBIAS0, ADCMUXN_VREF1P1); /* Vbias0 is routed to 12BIT LPDAC */ else AD5940_ADCMuxCfgS(ADCMUXP_VBIAS1, ADCMUXN_VREF1P1); /* Vbias1 is routed to 12BIT LPDAC */ AD5940_Delay10us(5); /* Delay 50us */ time_out = pCalCfg->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPDACCALERROR; } /* Time out error. */ /* Calculate the offset voltage using Equation2 - Equation1 */ ADCCode -= ADCCodeVref1p1; /* Get the code of Vbias0-AGND. Then calculate the offset voltage in mV. */ pResult->bC2V_DAC12B = ADCCode*pCalCfg->ADCRefVolt*1e3f/32768*1.835f/1.82f; /*mV unit*/ /* Measure 6BIT DAC output(Vzero0/1) */ if(pCalCfg->LpdacSel == LPDAC0) AD5940_ADCMuxCfgS(ADCMUXP_VZERO0, ADCMUXN_VREF1P1); /* Vbias0 is routed to 12BIT LPDAC */ else AD5940_ADCMuxCfgS(ADCMUXP_VZERO1, ADCMUXN_VREF1P1); /* Vbias1 is routed to 12BIT LPDAC */ AD5940_Delay10us(5); /* Delay 50us */ time_out = pCalCfg->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPDACCALERROR; } /* Time out error. */ /* Calculate the offset voltage */ ADCCode -= ADCCodeVref1p1; /* Get the code of Vbias0-AGND. Then calculate the offset voltage in mV. */ pResult->bC2V_DAC6B = ADCCode*pCalCfg->ADCRefVolt*1e3f/32768*1.835f/1.82f; /*mV unit*/ } /* Step3: Do gain measurement */ { /* Equation2: ADCCode = Vbias0 - Vref1p1 */ AD5940_LPDACWriteS(0xfff,0x3f); /* Set LPDAC output voltage to 2.4V(zero code) */ if(pCalCfg->SettleTime10us > 0) AD5940_Delay10us(pCalCfg->SettleTime10us); /* Delay nx10us */ if(pCalCfg->LpdacSel == LPDAC0) AD5940_ADCMuxCfgS(ADCMUXP_VBIAS0, ADCMUXN_VREF1P1); /* Vbias0 is routed to 12BIT LPDAC */ else AD5940_ADCMuxCfgS(ADCMUXP_VBIAS1, ADCMUXN_VREF1P1); /* Vbias1 is routed to 12BIT LPDAC */ AD5940_Delay10us(5); /* Delay 50us */ time_out = pCalCfg->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPDACCALERROR; } /* Time out error. */ /* Calculate the offset voltage */ ADCCode -= ADCCodeVref1p1; /* Get the code of Vbias0-AGND. Then calculate the gain factor 'k'. */ pResult->kC2V_DAC12B = (ADCCode*pCalCfg->ADCRefVolt*1e3f/32768*1.835f/1.82f - pResult->bC2V_DAC12B)/0xfff;/*mV unit*/ /* Measure 6BIT DAC output(Vzero0) */ if(pCalCfg->LpdacSel == LPDAC0) AD5940_ADCMuxCfgS(ADCMUXP_VZERO0, ADCMUXN_VREF1P1); /* Vbias0 is routed to 12BIT LPDAC */ else AD5940_ADCMuxCfgS(ADCMUXP_VZERO1, ADCMUXN_VREF1P1); /* Vbias1 is routed to 12BIT LPDAC */ AD5940_Delay10us(5); /* Delay 50us */ time_out = pCalCfg->TimeOut10us; /* Reset time out counter */ ADCCode = __AD5940_TakeMeasurement(&time_out); /* Turn on ADC to get one valid data and then turn off ADC. */ if(time_out == 0) { error = AD5940ERR_TIMEOUT; goto LPDACCALERROR; } /* Time out error. */ /* Calculate the offset voltage */ ADCCode -= ADCCodeVref1p1; /* Get the code of Vbias0-AGND. Then calculate the offset voltage in mV. */ pResult->kC2V_DAC6B = (ADCCode*pCalCfg->ADCRefVolt*1e3f/32768*1.835f/1.82f - pResult->bC2V_DAC6B)/0x3f;/*mV unit*/ } /* Step4: calculate the parameters for voltage to code calculation. */ pResult->kV2C_DAC12B = 1/pResult->kC2V_DAC12B; pResult->bV2C_DAC12B = -pResult->bC2V_DAC12B/pResult->kC2V_DAC12B; pResult->kV2C_DAC6B = 1/pResult->kC2V_DAC6B; pResult->bV2C_DAC6B = -pResult->bC2V_DAC6B/pResult->kC2V_DAC6B; /* Restore INTC1 SINC2 configure */ if(INTCCfg&AFEINTSRC_SINC2RDY); else AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, bFALSE); /* Disable SINC2 Interrupt */ /* Done */ return AD5940ERR_OK; LPDACCALERROR: AD5940_ADCConvtCtrlS(bFALSE); /* Stop conversion */ return error; } /** * @brief Use system clock to measure LFOSC frequency. * @note Set system clock to external crystal to get a better measurement accuracy. * This function use 3 sequences and the start address is specified by parameter. * @param pCfg: pointer to structure. * @param pFreq: Pointer to a variable that used to store frequency in Hz. * @return AD5940ERR_OK if succeed. **/ AD5940Err AD5940_LFOSCMeasure(LFOSCMeasure_Type *pCfg, float *pFreq) /* Measure current LFOSC frequency. */ { /** * @code * Sleep wakeup timer running... * -SLP----WKP----SLP----WKP----SLP----WKP * --|-----|-------------|-------------|------------Trigger sequencer when Wakeup Timer over. * --------|SEQA---------|SEQB----------------------Execute SeqA then SeqB * ---------|InitT--------|StopT--------------------SeqA start timer and SeqB trigger interrupt so MCU read back current count * ------------------------|INT--------------------- * -----------------------------------------|Read---We read SEQTIMEOUT register here * ---------|-----TimerCount----------------|------- * ---------|--------------|---TimerCount2--|-------We change SeqB to reset timer so we measure how much time needed for MCU to read back SEQTIMEOUT register(TimerCount2) * @endcode * **/ uint32_t TimerCount, TimerCount2; SEQCfg_Type seq_cfg, seq_cfg_backup; SEQInfo_Type seqinfo; WUPTCfg_Type wupt_cfg; uint32_t INTCCfg; uint32_t WuptPeriod; static const uint32_t SeqA[]= { SEQ_TOUT(0x3fffffff), /* Set time-out timer. It will always run until disable Sequencer by SPI interface. */ }; static const uint32_t SeqB[]= { /** * Interrupt flag AFEINTSRC_ENDSEQ will be set after this command. So We can inform MCU to read back * current timer value. MCU will need some additional time to read back time count. * So we use SeqB to measure how much time needed for MCU to read back * */ SEQ_STOP(), }; static const uint32_t SeqBB[]= { SEQ_TOUT(0x3fffffff), /* Re-Set time-out timer, so we can measure the time needed for MCU to read out Timer Count register. */ SEQ_STOP(), /* Interrupt flag AFEINTSRC_ENDSEQ will be set here */ }; if(pCfg == NULL) return AD5940ERR_NULLP; if(pFreq == NULL) return AD5940ERR_NULLP; if(pCfg->CalDuration < 1.0f) return AD5940ERR_PARA; AD5940_SEQGetCfg(&seq_cfg_backup); INTCCfg = AD5940_INTCGetCfg(AFEINTC_1); AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ENDSEQ, bTRUE); AD5940_INTCClrFlag(AFEINTSRC_ALLINT); seq_cfg.SeqMemSize = SEQMEMSIZE_2KB; /* 2kB SRAM is used for sequencer */ seq_cfg.SeqBreakEn = bFALSE; seq_cfg.SeqIgnoreEn = bFALSE; seq_cfg.SeqCntCRCClr = bFALSE; seq_cfg.SeqEnable = bTRUE; seq_cfg.SeqWrTimer = 0; AD5940_SEQCfg(&seq_cfg); /* Enable sequencer */ seqinfo.pSeqCmd = SeqA; seqinfo.SeqId = SEQID_0; seqinfo.SeqLen = SEQ_LEN(SeqA); seqinfo.SeqRamAddr = pCfg->CalSeqAddr; seqinfo.WriteSRAM = bTRUE; AD5940_SEQInfoCfg(&seqinfo); seqinfo.SeqId = SEQID_1; seqinfo.SeqRamAddr = pCfg->CalSeqAddr + SEQ_LEN(SeqA) ; seqinfo.SeqLen = SEQ_LEN(SeqB); seqinfo.pSeqCmd = SeqB; AD5940_SEQInfoCfg(&seqinfo); /* Configure sequence0 and sequence1 with command SeqA and SeqB */ wupt_cfg.WuptEn = bFALSE; wupt_cfg.WuptOrder[0] = SEQID_0; wupt_cfg.WuptOrder[1] = SEQID_1; wupt_cfg.WuptEndSeq = WUPTENDSEQ_B; wupt_cfg.SeqxWakeupTime[0] = 4; /* Don't care. >4 is acceptable */ wupt_cfg.SeqxSleepTime[0] = (uint32_t)((pCfg->CalDuration)*32 + 0.5f) - 1 - 4; wupt_cfg.SeqxWakeupTime[1] = 4-1; wupt_cfg.SeqxSleepTime[1] = 0xffffffff; /* Don't care */ WuptPeriod = (wupt_cfg.SeqxSleepTime[0]+1) + (wupt_cfg.SeqxWakeupTime[1]+1); AD5940_WUPTCfg(&wupt_cfg); AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ); AD5940_WUPTCtrl(bTRUE); while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE); TimerCount = AD5940_SEQTimeOutRd(); AD5940_WUPTCtrl(bFALSE); AD5940_WUPTTime(SEQID_0, 4, 4); /* Set it to minimum value because we don't care about sequence0 now. We only want to measure how much time MCU will need to read register */ seqinfo.SeqId = SEQID_1; seqinfo.SeqRamAddr = pCfg->CalSeqAddr + SEQ_LEN(SeqA) ; seqinfo.SeqLen = SEQ_LEN(SeqBB); seqinfo.pSeqCmd = SeqBB; seqinfo.WriteSRAM = bTRUE; AD5940_SEQInfoCfg(&seqinfo); AD5940_SEQCtrlS(bTRUE); /* Enable Sequencer again */ AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ); AD5940_WUPTCtrl(bTRUE); while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE); TimerCount2 = AD5940_SEQTimeOutRd(); AD5940_INTCTestFlag(AFEINTC_0, AFEINTSRC_ENDSEQ); AD5940_WUPTCtrl(bFALSE); AD5940_SEQCfg(&seq_cfg_backup); /* restore sequencer configuration */ AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ENDSEQ, (INTCCfg&AFEINTSRC_ENDSEQ)?bTRUE:bFALSE); /* Restore interrupt configuration */ AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ); //printf("Time duration:%d ", (TimerCount2 - TimerCount)); *pFreq = pCfg->SystemClkFreq*WuptPeriod/(TimerCount2 - TimerCount); return AD5940ERR_OK; } /** * @} Calibration * @} Calibration_Block */ /** * @} AD5940_Functions * @} AD5940_Library */