334 lines
10 KiB
C
334 lines
10 KiB
C
#include "refs.h"
|
|
#include "ble.h"
|
|
#include "temp.h"
|
|
#include "ad5940.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
/* LP RTIA register mapping (mirrors echem.c) */
|
|
extern const uint32_t lp_rtia_map[];
|
|
extern const float lp_rtia_ohms[];
|
|
|
|
/* ---- ADC helpers ---- */
|
|
|
|
static int32_t read_adc_code(void)
|
|
{
|
|
AD5940_INTCClrFlag(AFEINTSRC_SINC2RDY);
|
|
AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
|
|
AD5940_Delay10us(25);
|
|
AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);
|
|
|
|
AD5940_ClrMCUIntFlag();
|
|
while (!AD5940_GetMCUIntFlag())
|
|
vTaskDelay(1);
|
|
|
|
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_ADCPWR, bFALSE);
|
|
AD5940_INTCClrFlag(AFEINTSRC_SINC2RDY);
|
|
|
|
uint32_t raw = AD5940_ReadAfeResult(AFERESULT_SINC2);
|
|
return (raw & (1UL << 15)) ? (int32_t)(raw | 0xFFFF0000UL) : (int32_t)raw;
|
|
}
|
|
|
|
/* ---- Clipping detection ---- */
|
|
|
|
#define ADC_OK 0
|
|
#define ADC_CLIPPED 1
|
|
#define ADC_OSCILLATING 2
|
|
#define CLIP_THRESHOLD 30000
|
|
#define PROBE_SAMPLES 8
|
|
|
|
static void init_lp_for_probe(uint32_t rtia_reg)
|
|
{
|
|
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.LpBandgapEn = bTRUE;
|
|
ref.LpRefBufEn = bTRUE;
|
|
ref.LpRefBoostEn = bTRUE;
|
|
AD5940_REFCfgS(&ref);
|
|
|
|
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
|
|
|
|
LPLoopCfg_Type lp;
|
|
AD5940_StructInit(&lp, sizeof(lp));
|
|
lp.LpDacCfg.LpdacSel = LPDAC0;
|
|
lp.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
|
|
lp.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
|
|
lp.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
|
|
lp.LpDacCfg.LpDacSW = LPDACSW_VZERO2LPTIA | LPDACSW_VBIAS2LPPA;
|
|
lp.LpDacCfg.LpDacRef = LPDACREF_2P5;
|
|
lp.LpDacCfg.DataRst = bFALSE;
|
|
lp.LpDacCfg.PowerEn = bTRUE;
|
|
lp.LpDacCfg.DacData6Bit = 26; /* VZERO ~1094mV */
|
|
lp.LpDacCfg.DacData12Bit = (uint16_t)((1093.75f - 200.0f) / 0.537f + 0.5f); /* 0mV cell */
|
|
|
|
lp.LpAmpCfg.LpAmpSel = LPAMP0;
|
|
lp.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_BOOST3;
|
|
lp.LpAmpCfg.LpPaPwrEn = bTRUE;
|
|
lp.LpAmpCfg.LpTiaPwrEn = bTRUE;
|
|
lp.LpAmpCfg.LpTiaRf = LPTIARF_SHORT;
|
|
lp.LpAmpCfg.LpTiaRload = LPTIARLOAD_SHORT;
|
|
lp.LpAmpCfg.LpTiaRtia = rtia_reg;
|
|
lp.LpAmpCfg.LpTiaSW = LPTIASW(2) | LPTIASW(4) | LPTIASW(5) |
|
|
LPTIASW(7) | LPTIASW(12);
|
|
AD5940_LPLoopCfgS(&lp);
|
|
|
|
ADCBaseCfg_Type adc;
|
|
adc.ADCMuxP = ADCMUXP_LPTIA0_P;
|
|
adc.ADCMuxN = ADCMUXN_LPTIA0_N;
|
|
adc.ADCPga = ADCPGA_1P5;
|
|
AD5940_ADCBaseCfgS(&adc);
|
|
|
|
ADCFilterCfg_Type filt;
|
|
AD5940_StructInit(&filt, sizeof(filt));
|
|
filt.ADCSinc3Osr = ADCSINC3OSR_4;
|
|
filt.ADCSinc2Osr = ADCSINC2OSR_667;
|
|
filt.ADCAvgNum = ADCAVGNUM_16;
|
|
filt.ADCRate = ADCRATE_800KHZ;
|
|
filt.BpNotch = bFALSE;
|
|
filt.BpSinc3 = bFALSE;
|
|
filt.Sinc2NotchEnable = bTRUE;
|
|
filt.Sinc3ClkEnable = bTRUE;
|
|
filt.Sinc2NotchClkEnable = bTRUE;
|
|
filt.DFTClkEnable = bFALSE;
|
|
filt.WGClkEnable = bFALSE;
|
|
AD5940_ADCFilterCfgS(&filt);
|
|
|
|
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_SINC2RDY, bTRUE);
|
|
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_SINC2RDY, 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);
|
|
}
|
|
|
|
static void shutdown_lp(void)
|
|
{
|
|
LPLoopCfg_Type lp;
|
|
AD5940_StructInit(&lp, sizeof(lp));
|
|
lp.LpDacCfg.LpdacSel = LPDAC0;
|
|
lp.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
|
|
lp.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
|
|
lp.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
|
|
lp.LpDacCfg.LpDacSW = 0;
|
|
lp.LpDacCfg.LpDacRef = LPDACREF_2P5;
|
|
lp.LpDacCfg.PowerEn = bFALSE;
|
|
lp.LpDacCfg.DataRst = bFALSE;
|
|
lp.LpDacCfg.DacData6Bit = 0;
|
|
lp.LpDacCfg.DacData12Bit = 0;
|
|
lp.LpAmpCfg.LpAmpSel = LPAMP0;
|
|
lp.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
|
|
lp.LpAmpCfg.LpPaPwrEn = bFALSE;
|
|
lp.LpAmpCfg.LpTiaPwrEn = bFALSE;
|
|
lp.LpAmpCfg.LpTiaRf = LPTIARF_OPEN;
|
|
lp.LpAmpCfg.LpTiaRload = LPTIARLOAD_SHORT;
|
|
lp.LpAmpCfg.LpTiaRtia = LPTIARTIA_OPEN;
|
|
lp.LpAmpCfg.LpTiaSW = 0;
|
|
AD5940_LPLoopCfgS(&lp);
|
|
|
|
SWMatrixCfg_Type sw = { SWD_OPEN, SWP_OPEN, SWN_OPEN, SWT_OPEN };
|
|
AD5940_SWMatrixCfgS(&sw);
|
|
}
|
|
|
|
static int check_adc_clipping(uint8_t lp_rtia_idx)
|
|
{
|
|
init_lp_for_probe(lp_rtia_map[lp_rtia_idx]);
|
|
vTaskDelay(pdMS_TO_TICKS(50));
|
|
|
|
int clip_count = 0;
|
|
int sign_changes = 0;
|
|
int any_high = 0;
|
|
int32_t prev_sign = 0;
|
|
|
|
for (int i = 0; i < PROBE_SAMPLES; i++) {
|
|
int32_t code = read_adc_code();
|
|
int32_t abs_code = code < 0 ? -code : code;
|
|
|
|
if (abs_code > CLIP_THRESHOLD) {
|
|
clip_count++;
|
|
any_high = 1;
|
|
}
|
|
|
|
int32_t cur_sign = (code > 0) ? 1 : ((code < 0) ? -1 : 0);
|
|
if (i > 0 && cur_sign != 0 && prev_sign != 0 && cur_sign != prev_sign)
|
|
sign_changes++;
|
|
if (cur_sign != 0) prev_sign = cur_sign;
|
|
}
|
|
|
|
shutdown_lp();
|
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
|
|
|
if (sign_changes >= 3 && any_high)
|
|
return ADC_OSCILLATING;
|
|
if (clip_count > PROBE_SAMPLES / 2)
|
|
return ADC_CLIPPED;
|
|
return ADC_OK;
|
|
}
|
|
|
|
static void lp_find_valid_range(LpRtiaRange *range)
|
|
{
|
|
int low_clip = check_adc_clipping(0);
|
|
int high_clip = check_adc_clipping(LP_RTIA_512K);
|
|
|
|
uint8_t lo = 0, hi = LP_RTIA_512K;
|
|
|
|
if (low_clip != ADC_OK) {
|
|
/* binary search upward for first non-clipping */
|
|
uint8_t a = 0, b = LP_RTIA_512K;
|
|
while (a < b) {
|
|
uint8_t m = (a + b) / 2;
|
|
if (check_adc_clipping(m) != ADC_OK)
|
|
a = m + 1;
|
|
else
|
|
b = m;
|
|
}
|
|
lo = a;
|
|
}
|
|
|
|
if (high_clip != ADC_OK) {
|
|
/* binary search downward for last non-clipping */
|
|
uint8_t a = lo, b = LP_RTIA_512K;
|
|
while (a < b) {
|
|
uint8_t m = (a + b + 1) / 2;
|
|
if (check_adc_clipping(m) != ADC_OK)
|
|
a = m;
|
|
else
|
|
b = m - 1;
|
|
}
|
|
hi = a;
|
|
}
|
|
|
|
range->low_idx = lo;
|
|
range->high_idx = hi;
|
|
range->valid = (lo <= hi) ? 1 : 0;
|
|
}
|
|
|
|
/* ---- Collection ---- */
|
|
|
|
void refs_collect(RefStore *store, const EISConfig *cfg)
|
|
{
|
|
memset(store, 0, sizeof(*store));
|
|
|
|
/* EIS phase: sweep each RTIA 0-7 with RCAL=3K */
|
|
EISConfig ref_cfg = *cfg;
|
|
ref_cfg.rcal = RCAL_3K;
|
|
|
|
for (int r = 0; r < REF_EIS_RTIA_COUNT; r++) {
|
|
ref_cfg.rtia = (EISRtia)r;
|
|
eis_init(&ref_cfg);
|
|
|
|
ble_send_ref_frame(REF_MODE_EIS, (uint8_t)r);
|
|
|
|
uint32_t n = eis_calc_num_points(&ref_cfg);
|
|
ble_send_sweep_start(n, ref_cfg.freq_start_hz, ref_cfg.freq_stop_hz);
|
|
|
|
int got = eis_sweep(store->eis[r].pts, n, ble_send_eis_point);
|
|
store->eis[r].n_points = (uint32_t)got;
|
|
store->eis[r].valid = (got > 0) ? 1 : 0;
|
|
|
|
ble_send_sweep_end();
|
|
printf("Ref EIS RTIA %d: %d pts\n", r, got);
|
|
}
|
|
|
|
/* LP phase: binary search valid RTIA range for each LP mode */
|
|
printf("Ref: LP range search (LSV)\n");
|
|
ble_send_ref_frame(REF_MODE_LSV, 0);
|
|
lp_find_valid_range(&store->lsv_range);
|
|
if (store->lsv_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_LSV, store->lsv_range.low_idx, store->lsv_range.high_idx);
|
|
printf("Ref LSV range: %u-%u\n", store->lsv_range.low_idx, store->lsv_range.high_idx);
|
|
|
|
printf("Ref: LP range search (Amp)\n");
|
|
ble_send_ref_frame(REF_MODE_AMP, 0);
|
|
lp_find_valid_range(&store->amp_range);
|
|
if (store->amp_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_AMP, store->amp_range.low_idx, store->amp_range.high_idx);
|
|
printf("Ref Amp range: %u-%u\n", store->amp_range.low_idx, store->amp_range.high_idx);
|
|
|
|
printf("Ref: LP range search (Cl)\n");
|
|
ble_send_ref_frame(REF_MODE_CL, 0);
|
|
lp_find_valid_range(&store->cl_range);
|
|
if (store->cl_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_CL, store->cl_range.low_idx, store->cl_range.high_idx);
|
|
printf("Ref Cl range: %u-%u\n", store->cl_range.low_idx, store->cl_range.high_idx);
|
|
|
|
/* pH phase: OCP measurement */
|
|
printf("Ref: pH OCP\n");
|
|
ble_send_ref_frame(REF_MODE_PH, 0);
|
|
PhConfig ph_cfg;
|
|
ph_cfg.stabilize_s = 10.0f;
|
|
ph_cfg.temp_c = temp_get();
|
|
echem_ph_ocp(&ph_cfg, &store->ph_ref);
|
|
store->ph_valid = 1;
|
|
ble_send_ph_result(store->ph_ref.v_ocp_mv, store->ph_ref.ph, store->ph_ref.temp_c);
|
|
|
|
store->has_refs = 1;
|
|
ble_send_refs_done();
|
|
printf("Ref collection complete\n");
|
|
}
|
|
|
|
/* ---- Send stored refs to client ---- */
|
|
|
|
void refs_send(const RefStore *store)
|
|
{
|
|
if (!store->has_refs) {
|
|
ble_send_ref_status(0);
|
|
return;
|
|
}
|
|
ble_send_ref_status(1);
|
|
|
|
for (int r = 0; r < REF_EIS_RTIA_COUNT; r++) {
|
|
if (!store->eis[r].valid) continue;
|
|
|
|
ble_send_ref_frame(REF_MODE_EIS, (uint8_t)r);
|
|
uint32_t n = store->eis[r].n_points;
|
|
ble_send_sweep_start(n, store->eis[r].pts[0].freq_hz,
|
|
store->eis[r].pts[n - 1].freq_hz);
|
|
for (uint32_t i = 0; i < n; i++)
|
|
ble_send_eis_point((uint16_t)i, &store->eis[r].pts[i]);
|
|
ble_send_sweep_end();
|
|
}
|
|
|
|
if (store->lsv_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_LSV, store->lsv_range.low_idx, store->lsv_range.high_idx);
|
|
if (store->amp_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_AMP, store->amp_range.low_idx, store->amp_range.high_idx);
|
|
if (store->cl_range.valid)
|
|
ble_send_ref_lp_range(REF_MODE_CL, store->cl_range.low_idx, store->cl_range.high_idx);
|
|
|
|
if (store->ph_valid) {
|
|
ble_send_ref_frame(REF_MODE_PH, 0);
|
|
ble_send_ph_result(store->ph_ref.v_ocp_mv, store->ph_ref.ph, store->ph_ref.temp_c);
|
|
}
|
|
|
|
ble_send_refs_done();
|
|
}
|
|
|
|
void refs_clear(RefStore *store)
|
|
{
|
|
memset(store, 0, sizeof(*store));
|
|
ble_send_ref_status(0);
|
|
}
|