EIS-BLE-S3/main/eis.c

359 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
AD5940_WriteReg(REG_AFE_FIFOCON, 0);
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();
}
/* ---------- 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_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bFALSE);
AD5940_WriteReg(REG_AFE_FIFOCON, 0);
AD5940_ReadAfeResult(AFERESULT_DFTREAL);
AD5940_ReadAfeResult(AFERESULT_DFTIMAGE);
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
AD5940_ADCMuxCfgS(mux_p, mux_n);
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR, bTRUE);
AD5940_Delay10us(25);
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;
}