362 lines
10 KiB
C
362 lines
10 KiB
C
#include "eis.h"
|
||
#include <math.h>
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
#ifndef M_PI
|
||
#define M_PI 3.14159265358979323846
|
||
#endif
|
||
|
||
/* resolved hardware state */
|
||
static struct {
|
||
EISConfig cfg;
|
||
float sys_clk;
|
||
float rcal_ohms;
|
||
uint32_t rcal_sw_d, rcal_sw_p, rcal_sw_n, rcal_sw_t;
|
||
uint32_t rtia_reg;
|
||
uint32_t dertia_reg;
|
||
} ctx;
|
||
|
||
static const uint32_t rtia_map[] = {
|
||
[RTIA_200] = HSTIARTIA_200,
|
||
[RTIA_1K] = HSTIARTIA_1K,
|
||
[RTIA_5K] = HSTIARTIA_5K,
|
||
[RTIA_10K] = HSTIARTIA_10K,
|
||
[RTIA_20K] = HSTIARTIA_20K,
|
||
[RTIA_40K] = HSTIARTIA_40K,
|
||
[RTIA_80K] = HSTIARTIA_80K,
|
||
[RTIA_160K] = HSTIARTIA_160K,
|
||
[RTIA_EXT_DE0] = HSTIARTIA_OPEN,
|
||
};
|
||
|
||
static void resolve_config(void)
|
||
{
|
||
/* RTIA */
|
||
ctx.rtia_reg = rtia_map[ctx.cfg.rtia];
|
||
ctx.dertia_reg = (ctx.cfg.rtia == RTIA_EXT_DE0) ? HSTIADERTIA_TODE : HSTIADERTIA_OPEN;
|
||
|
||
/* RCAL */
|
||
switch (ctx.cfg.rcal) {
|
||
case RCAL_200R:
|
||
ctx.rcal_ohms = 200.0f;
|
||
ctx.rcal_sw_d = SWD_RCAL0;
|
||
ctx.rcal_sw_p = SWP_RCAL0;
|
||
ctx.rcal_sw_n = SWN_RCAL1;
|
||
ctx.rcal_sw_t = SWT_RCAL1 | SWT_TRTIA;
|
||
break;
|
||
default: /* RCAL_3K */
|
||
ctx.rcal_ohms = 3000.0f;
|
||
ctx.rcal_sw_d = SWD_RCAL0;
|
||
ctx.rcal_sw_p = SWP_RCAL0;
|
||
ctx.rcal_sw_n = SWN_AIN0;
|
||
ctx.rcal_sw_t = SWT_AIN0 | SWT_TRTIA;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void apply_hsloop(void)
|
||
{
|
||
HSLoopCfg_Type hs;
|
||
AD5940_StructInit(&hs, sizeof(hs));
|
||
hs.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2;
|
||
hs.HsDacCfg.HsDacGain = HSDACGAIN_0P2;
|
||
hs.HsDacCfg.HsDacUpdateRate = 7;
|
||
hs.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
|
||
hs.HsTiaCfg.HstiaRtiaSel = ctx.rtia_reg;
|
||
hs.HsTiaCfg.HstiaCtia = 16;
|
||
hs.HsTiaCfg.HstiaDeRtia = ctx.dertia_reg;
|
||
hs.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
|
||
hs.HsTiaCfg.HstiaDe1Rtia = HSTIADERTIA_OPEN;
|
||
hs.HsTiaCfg.HstiaDe1Rload = HSTIADERLOAD_OPEN;
|
||
hs.HsTiaCfg.DiodeClose = bFALSE;
|
||
hs.WgCfg.WgType = WGTYPE_SIN;
|
||
hs.WgCfg.GainCalEn = bTRUE;
|
||
hs.WgCfg.OffsetCalEn = bTRUE;
|
||
hs.WgCfg.SinCfg.SinAmplitudeWord = ctx.cfg.excit_amp;
|
||
hs.WgCfg.SinCfg.SinFreqWord = 0;
|
||
hs.WgCfg.SinCfg.SinOffsetWord = 0;
|
||
hs.WgCfg.SinCfg.SinPhaseWord = 0;
|
||
hs.SWMatCfg.Dswitch = ctx.rcal_sw_d;
|
||
hs.SWMatCfg.Pswitch = ctx.rcal_sw_p;
|
||
hs.SWMatCfg.Nswitch = ctx.rcal_sw_n;
|
||
hs.SWMatCfg.Tswitch = ctx.rcal_sw_t;
|
||
AD5940_HSLoopCfgS(&hs);
|
||
|
||
if (ctx.cfg.rtia == RTIA_EXT_DE0)
|
||
AD5940_WriteReg(REG_AFE_DE0RESCON, 0x97);
|
||
}
|
||
|
||
/* ---------- public ---------- */
|
||
|
||
void eis_default_config(EISConfig *cfg)
|
||
{
|
||
memset(cfg, 0, sizeof(*cfg));
|
||
cfg->freq_start_hz = 1000.0f;
|
||
cfg->freq_stop_hz = 200000.0f;
|
||
cfg->points_per_decade = 10;
|
||
cfg->rtia = RTIA_5K;
|
||
cfg->rcal = RCAL_3K;
|
||
cfg->pga = ADCPGA_1P5;
|
||
cfg->excit_amp = 500;
|
||
}
|
||
|
||
uint32_t eis_calc_num_points(const EISConfig *cfg)
|
||
{
|
||
if (cfg->freq_stop_hz <= cfg->freq_start_hz || cfg->points_per_decade == 0)
|
||
return 1;
|
||
float decades = log10f(cfg->freq_stop_hz / cfg->freq_start_hz);
|
||
uint32_t n = (uint32_t)(decades * cfg->points_per_decade + 0.5f) + 1;
|
||
if (n > EIS_MAX_POINTS) n = EIS_MAX_POINTS;
|
||
if (n < 2) n = 2;
|
||
return n;
|
||
}
|
||
|
||
void eis_init(const EISConfig *cfg)
|
||
{
|
||
memcpy(&ctx.cfg, cfg, sizeof(EISConfig));
|
||
ctx.sys_clk = 16000000.0f;
|
||
resolve_config();
|
||
|
||
CLKCfg_Type clk;
|
||
memset(&clk, 0, sizeof(clk));
|
||
clk.HFOSCEn = bTRUE;
|
||
clk.HfOSC32MHzMode = bFALSE;
|
||
clk.SysClkSrc = SYSCLKSRC_HFOSC;
|
||
clk.ADCCLkSrc = ADCCLKSRC_HFOSC;
|
||
clk.SysClkDiv = SYSCLKDIV_1;
|
||
clk.ADCClkDiv = ADCCLKDIV_1;
|
||
clk.LFOSCEn = bTRUE;
|
||
clk.HFXTALEn = bFALSE;
|
||
AD5940_CLKCfg(&clk);
|
||
|
||
AFERefCfg_Type ref;
|
||
AD5940_StructInit(&ref, sizeof(ref));
|
||
ref.HpBandgapEn = bTRUE;
|
||
ref.Hp1V1BuffEn = bTRUE;
|
||
ref.Hp1V8BuffEn = bTRUE;
|
||
ref.HSDACRefEn = bTRUE;
|
||
ref.LpBandgapEn = bFALSE;
|
||
ref.LpRefBufEn = bFALSE;
|
||
AD5940_REFCfgS(&ref);
|
||
|
||
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
|
||
|
||
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DFTRDY, bTRUE);
|
||
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bTRUE);
|
||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
||
|
||
AGPIOCfg_Type gpio;
|
||
AD5940_StructInit(&gpio, sizeof(gpio));
|
||
gpio.FuncSet = GP0_INT;
|
||
gpio.OutputEnSet = AGPIO_Pin0;
|
||
AD5940_AGPIOCfg(&gpio);
|
||
|
||
FIFOCfg_Type fifo;
|
||
AD5940_StructInit(&fifo, sizeof(fifo));
|
||
fifo.FIFOEn = bFALSE;
|
||
fifo.FIFOMode = FIFOMODE_FIFO;
|
||
fifo.FIFOSize = FIFOSIZE_2KB;
|
||
fifo.FIFOSrc = FIFOSRC_DFT;
|
||
fifo.FIFOThresh = 4;
|
||
AD5940_FIFOCfg(&fifo);
|
||
|
||
SEQCfg_Type seq;
|
||
seq.SeqMemSize = SEQMEMSIZE_4KB;
|
||
seq.SeqBreakEn = bFALSE;
|
||
seq.SeqIgnoreEn = bFALSE;
|
||
seq.SeqCntCRCClr = bFALSE;
|
||
seq.SeqEnable = bTRUE;
|
||
seq.SeqWrTimer = 0;
|
||
AD5940_SEQCfg(&seq);
|
||
|
||
apply_hsloop();
|
||
|
||
ADCBaseCfg_Type adc;
|
||
adc.ADCMuxP = ADCMUXP_P_NODE;
|
||
adc.ADCMuxN = ADCMUXN_N_NODE;
|
||
adc.ADCPga = cfg->pga;
|
||
AD5940_ADCBaseCfgS(&adc);
|
||
|
||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||
}
|
||
|
||
void eis_reconfigure(const EISConfig *cfg)
|
||
{
|
||
memcpy(&ctx.cfg, cfg, sizeof(EISConfig));
|
||
resolve_config();
|
||
apply_hsloop();
|
||
}
|
||
|
||
/* ---------- internal helpers ---------- */
|
||
|
||
static void configure_freq(float freq_hz)
|
||
{
|
||
FreqParams_Type fp = AD5940_GetFreqParameters(freq_hz);
|
||
|
||
if (fp.HighPwrMode) {
|
||
fp.DftSrc = DFTSRC_ADCRAW;
|
||
fp.ADCSinc3Osr = ADCSINC3OSR_2;
|
||
fp.ADCSinc2Osr = 0;
|
||
fp.DftNum = DFTNUM_16384;
|
||
}
|
||
|
||
AD5940_WriteReg(REG_AFE_WGFCW,
|
||
AD5940_WGFreqWordCal(freq_hz, ctx.sys_clk));
|
||
|
||
ADCFilterCfg_Type filt;
|
||
AD5940_StructInit(&filt, sizeof(filt));
|
||
filt.ADCSinc3Osr = fp.ADCSinc3Osr;
|
||
filt.ADCSinc2Osr = fp.ADCSinc2Osr;
|
||
filt.ADCAvgNum = ADCAVGNUM_16;
|
||
filt.ADCRate = ADCRATE_800KHZ;
|
||
filt.BpNotch = bTRUE;
|
||
filt.BpSinc3 = bFALSE;
|
||
filt.Sinc2NotchEnable = bTRUE;
|
||
filt.Sinc3ClkEnable = bTRUE;
|
||
filt.Sinc2NotchClkEnable = bTRUE;
|
||
filt.DFTClkEnable = bTRUE;
|
||
filt.WGClkEnable = bTRUE;
|
||
AD5940_ADCFilterCfgS(&filt);
|
||
|
||
DFTCfg_Type dft;
|
||
dft.DftNum = fp.DftNum;
|
||
dft.DftSrc = fp.DftSrc;
|
||
dft.HanWinEn = bTRUE;
|
||
AD5940_DFTCfgS(&dft);
|
||
}
|
||
|
||
static int32_t sign_extend_18(uint32_t v)
|
||
{
|
||
return (v & (1UL << 17)) ? (int32_t)(v | 0xFFFC0000UL) : (int32_t)v;
|
||
}
|
||
|
||
static void dft_measure(uint32_t mux_p, uint32_t mux_n, iImpCar_Type *out)
|
||
{
|
||
AD5940_ADCMuxCfgS(mux_p, mux_n);
|
||
|
||
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR, bTRUE);
|
||
AD5940_Delay10us(25);
|
||
|
||
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
|
||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bTRUE);
|
||
|
||
uint32_t timeout = 10000000;
|
||
while (AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE) {
|
||
if (--timeout == 0) break;
|
||
}
|
||
|
||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT |
|
||
AFECTRL_WG | AFECTRL_ADCPWR, bFALSE);
|
||
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
|
||
|
||
out->Real = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTREAL));
|
||
out->Image = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTIMAGE));
|
||
out->Image = -out->Image;
|
||
}
|
||
|
||
/* ---------- measurement ---------- */
|
||
|
||
int eis_measure_point(float freq_hz, EISPoint *out)
|
||
{
|
||
configure_freq(freq_hz);
|
||
|
||
iImpCar_Type v_rcal, v_rtia, v_tia, v_sense;
|
||
|
||
/* Phase 1: RTIA calibration through RCAL */
|
||
SWMatrixCfg_Type sw;
|
||
sw.Dswitch = ctx.rcal_sw_d;
|
||
sw.Pswitch = ctx.rcal_sw_p;
|
||
sw.Nswitch = ctx.rcal_sw_n;
|
||
sw.Tswitch = ctx.rcal_sw_t;
|
||
AD5940_SWMatrixCfgS(&sw);
|
||
|
||
AD5940_AFECtrlS(AFECTRL_HPREFPWR | AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR |
|
||
AFECTRL_EXTBUFPWR | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR |
|
||
AFECTRL_SINC2NOTCH, bTRUE);
|
||
|
||
dft_measure(ADCMUXP_P_NODE, ADCMUXN_N_NODE, &v_rcal);
|
||
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_rtia);
|
||
|
||
v_rtia.Real = -v_rtia.Real;
|
||
v_rtia.Image = -v_rtia.Image;
|
||
|
||
fImpCar_Type rtia = AD5940_ComplexDivInt(&v_rtia, &v_rcal);
|
||
rtia.Real *= ctx.rcal_ohms;
|
||
rtia.Image *= ctx.rcal_ohms;
|
||
|
||
/* Phase 2: DUT — software 4-wire */
|
||
sw.Dswitch = SWD_AIN3;
|
||
sw.Pswitch = SWP_AIN3;
|
||
sw.Nswitch = SWN_AIN0;
|
||
sw.Tswitch = SWT_AIN0 | SWT_TRTIA;
|
||
AD5940_SWMatrixCfgS(&sw);
|
||
AD5940_Delay10us(50);
|
||
|
||
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia);
|
||
v_tia.Real = -v_tia.Real;
|
||
v_tia.Image = -v_tia.Image;
|
||
|
||
dft_measure(ADCMUXP_AIN2, ADCMUXN_AIN1, &v_sense);
|
||
|
||
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR | AFECTRL_ADCCNV |
|
||
AFECTRL_DFT | AFECTRL_SINC2NOTCH | AFECTRL_HSDACPWR |
|
||
AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR |
|
||
AFECTRL_EXTBUFPWR, bFALSE);
|
||
|
||
sw.Dswitch = SWD_OPEN;
|
||
sw.Pswitch = SWP_OPEN;
|
||
sw.Nswitch = SWN_OPEN;
|
||
sw.Tswitch = SWT_OPEN;
|
||
AD5940_SWMatrixCfgS(&sw);
|
||
|
||
/* Z_DUT = V_sense × Rtia_cal / V_TIA */
|
||
fImpCar_Type fv_sense = { (float)v_sense.Real, (float)v_sense.Image };
|
||
fImpCar_Type fv_tia = { (float)v_tia.Real, (float)v_tia.Image };
|
||
|
||
fImpCar_Type num = AD5940_ComplexMulFloat(&fv_sense, &rtia);
|
||
fImpCar_Type z = AD5940_ComplexDivFloat(&num, &fv_tia);
|
||
|
||
out->freq_hz = freq_hz;
|
||
out->z_real = z.Real;
|
||
out->z_imag = z.Image;
|
||
out->mag_ohms = AD5940_ComplexMag(&z);
|
||
out->phase_deg = AD5940_ComplexPhase(&z) * (float)(180.0 / M_PI);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int eis_sweep(EISPoint *out, uint32_t max_points)
|
||
{
|
||
uint32_t n = eis_calc_num_points(&ctx.cfg);
|
||
if (n > max_points) n = max_points;
|
||
|
||
SoftSweepCfg_Type sweep;
|
||
sweep.SweepEn = bTRUE;
|
||
sweep.SweepStart = ctx.cfg.freq_start_hz;
|
||
sweep.SweepStop = ctx.cfg.freq_stop_hz;
|
||
sweep.SweepPoints = n;
|
||
sweep.SweepLog = bTRUE;
|
||
sweep.SweepIndex = 0;
|
||
|
||
printf("\n%10s %12s %10s %12s %12s\n",
|
||
"Freq(Hz)", "|Z|(Ohm)", "Phase(deg)", "Re(Ohm)", "Im(Ohm)");
|
||
printf("--------------------------------------------------------------\n");
|
||
|
||
eis_measure_point(ctx.cfg.freq_start_hz, &out[0]);
|
||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f\n",
|
||
out[0].freq_hz, out[0].mag_ohms, out[0].phase_deg,
|
||
out[0].z_real, out[0].z_imag);
|
||
|
||
for (uint32_t i = 1; i < n; i++) {
|
||
float freq;
|
||
AD5940_SweepNext(&sweep, &freq);
|
||
eis_measure_point(freq, &out[i]);
|
||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f\n",
|
||
out[i].freq_hz, out[i].mag_ohms, out[i].phase_deg,
|
||
out[i].z_real, out[i].z_imag);
|
||
}
|
||
|
||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||
return (int)n;
|
||
}
|