recovered working tree: paired DFT, ratiometric Z, open-cal, BLE event refactor
This commit is contained in:
parent
5268d55b6f
commit
5ae607eec4
|
|
@ -8,6 +8,7 @@ use std::fmt::Write;
|
|||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::ble::BleEvent;
|
||||
use crate::native_menu::{MenuAction, NativeMenu};
|
||||
use crate::protocol::{
|
||||
self, AmpPoint, ClPoint, ClResult, Electrode, EisMessage, EisPoint, LpRtia, LsvPoint,
|
||||
|
|
@ -732,11 +733,30 @@ impl App {
|
|||
self.midi_gen,
|
||||
iced::stream::channel(100, |mut output| async move {
|
||||
loop {
|
||||
let _ = output.send(Message::BleStatus("Looking for MIDI device...".into())).await;
|
||||
match crate::ble::connect_and_stream(&mut output).await {
|
||||
Ok(()) => eprintln!("BLE: session ended cleanly"),
|
||||
Err(e) => eprintln!("BLE: session error: {e}"),
|
||||
let (ble_tx, mut ble_rx) = mpsc::unbounded_channel::<BleEvent>();
|
||||
let (cmd_tx, cmd_rx) = mpsc::unbounded_channel::<Vec<u8>>();
|
||||
|
||||
let tx = ble_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::ble::connect_and_run(tx, cmd_rx).await {
|
||||
eprintln!("BLE: {e}");
|
||||
}
|
||||
});
|
||||
|
||||
let mut ready_sent = false;
|
||||
while let Some(ev) = ble_rx.recv().await {
|
||||
let msg = match ev {
|
||||
BleEvent::Status(ref s) if s == "Connected" && !ready_sent => {
|
||||
ready_sent = true;
|
||||
let _ = output.send(Message::BleReady(cmd_tx.clone())).await;
|
||||
Message::BleStatus(s.clone())
|
||||
}
|
||||
BleEvent::Status(s) => Message::BleStatus(s),
|
||||
BleEvent::Data(m) => Message::BleData(m),
|
||||
};
|
||||
let _ = output.send(msg).await;
|
||||
}
|
||||
|
||||
let _ = output.send(Message::BleStatus("Reconnecting...".into())).await;
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
use futures::SinkExt;
|
||||
use midir::{MidiInput, MidiOutput, MidiInputConnection, MidiOutputConnection};
|
||||
use std::sync::mpsc as std_mpsc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::protocol;
|
||||
use crate::protocol::{self, EisMessage};
|
||||
|
||||
const DEVICE_NAME: &str = "EIS4";
|
||||
|
||||
pub async fn connect_and_stream(
|
||||
output: &mut futures::channel::mpsc::Sender<Message>,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BleEvent {
|
||||
Status(String),
|
||||
Data(EisMessage),
|
||||
}
|
||||
|
||||
pub async fn connect_and_run(
|
||||
tx: mpsc::UnboundedSender<BleEvent>,
|
||||
mut cmd_rx: mpsc::UnboundedReceiver<Vec<u8>>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
eprintln!("BLE: scanning for MIDI device '{DEVICE_NAME}'...");
|
||||
let _ = tx.send(BleEvent::Status("Looking for MIDI device...".into()));
|
||||
|
||||
let (midi_in, in_port, midi_out, out_port) = loop {
|
||||
match find_midi_ports() {
|
||||
Some(found) => break found,
|
||||
None => {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
if let Some(found) = find_midi_ports() {
|
||||
break found;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
};
|
||||
|
||||
eprintln!("BLE: found ports, connecting...");
|
||||
let _ = output.send(Message::BleStatus("Connecting MIDI...".into())).await;
|
||||
let _ = tx.send(BleEvent::Status("Connecting MIDI...".into()));
|
||||
|
||||
let (sysex_tx, sysex_rx) = std_mpsc::channel::<Vec<u8>>();
|
||||
|
||||
|
|
@ -41,16 +43,12 @@ pub async fn connect_and_stream(
|
|||
&out_port, "cue-out",
|
||||
).map_err(|e| format!("MIDI output connect: {e}"))?;
|
||||
|
||||
eprintln!("BLE: connected");
|
||||
let _ = output.send(Message::BleStatus("Connected".into())).await;
|
||||
|
||||
let (cmd_tx, mut cmd_rx) = mpsc::unbounded_channel::<Vec<u8>>();
|
||||
let _ = output.send(Message::BleReady(cmd_tx)).await;
|
||||
let _ = tx.send(BleEvent::Status("Connected".into()));
|
||||
|
||||
loop {
|
||||
while let Ok(sysex) = sysex_rx.try_recv() {
|
||||
if let Some(msg) = protocol::parse_sysex(&sysex) {
|
||||
let _ = output.send(Message::BleData(msg)).await;
|
||||
let _ = tx.send(BleEvent::Data(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,14 +56,10 @@ pub async fn connect_and_stream(
|
|||
match cmd_rx.try_recv() {
|
||||
Ok(pkt) => {
|
||||
if let Err(e) = out_conn.send(&pkt) {
|
||||
eprintln!("BLE: MIDI send error: {e}");
|
||||
return Err(e.into());
|
||||
eprintln!("MIDI send error: {e}");
|
||||
}
|
||||
}
|
||||
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||
eprintln!("BLE: cmd channel closed");
|
||||
return Ok(());
|
||||
}
|
||||
Err(mpsc::error::TryRecvError::Disconnected) => return Ok(()),
|
||||
Err(mpsc::error::TryRecvError::Empty) => break,
|
||||
}
|
||||
}
|
||||
|
|
@ -81,25 +75,11 @@ fn find_midi_ports() -> Option<(
|
|||
let midi_in = MidiInput::new("cue-in").ok()?;
|
||||
let midi_out = MidiOutput::new("cue-out").ok()?;
|
||||
|
||||
let in_ports = midi_in.ports();
|
||||
let out_ports = midi_out.ports();
|
||||
|
||||
let in_names: Vec<_> = in_ports.iter()
|
||||
.filter_map(|p| midi_in.port_name(p).ok())
|
||||
.collect();
|
||||
let out_names: Vec<_> = out_ports.iter()
|
||||
.filter_map(|p| midi_out.port_name(p).ok())
|
||||
.collect();
|
||||
|
||||
if !in_names.is_empty() || !out_names.is_empty() {
|
||||
eprintln!("BLE: MIDI ports — in: {:?}, out: {:?}", in_names, out_names);
|
||||
}
|
||||
|
||||
let in_port = in_ports.into_iter().find(|p| {
|
||||
let in_port = midi_in.ports().into_iter().find(|p| {
|
||||
midi_in.port_name(p).map_or(false, |n| n.contains(DEVICE_NAME))
|
||||
})?;
|
||||
|
||||
let out_port = out_ports.into_iter().find(|p| {
|
||||
let out_port = midi_out.ports().into_iter().find(|p| {
|
||||
midi_out.port_name(p).map_or(false, |n| n.contains(DEVICE_NAME))
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ static void parse_one_sysex(const uint8_t *midi, uint16_t mlen)
|
|||
case CMD_START_REFS:
|
||||
case CMD_GET_REFS:
|
||||
case CMD_CLEAR_REFS:
|
||||
case CMD_OPEN_CAL:
|
||||
case CMD_CLEAR_OPEN_CAL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#define CMD_START_CL 0x23
|
||||
#define CMD_START_PH 0x24
|
||||
#define CMD_START_CLEAN 0x25
|
||||
#define CMD_OPEN_CAL 0x26
|
||||
#define CMD_CLEAR_OPEN_CAL 0x27
|
||||
#define CMD_START_REFS 0x30
|
||||
#define CMD_GET_REFS 0x31
|
||||
#define CMD_CLEAR_REFS 0x32
|
||||
|
|
|
|||
202
main/eis.c
202
main/eis.c
|
|
@ -4,6 +4,8 @@
|
|||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
|
@ -21,6 +23,14 @@ static struct {
|
|||
uint32_t dertia_reg;
|
||||
} ctx;
|
||||
|
||||
/* open-circuit calibration data */
|
||||
static struct {
|
||||
fImpCar_Type y[EIS_MAX_POINTS]; /* admittance at each freq */
|
||||
float freq[EIS_MAX_POINTS];
|
||||
uint32_t n;
|
||||
int valid;
|
||||
} ocal;
|
||||
|
||||
static const uint32_t rtia_map[] = {
|
||||
[RTIA_200] = HSTIARTIA_200,
|
||||
[RTIA_1K] = HSTIARTIA_1K,
|
||||
|
|
@ -127,9 +137,13 @@ void eis_default_config(EISConfig *cfg)
|
|||
|
||||
uint32_t eis_calc_num_points(const EISConfig *cfg)
|
||||
{
|
||||
if (cfg->freq_stop_hz <= cfg->freq_start_hz || cfg->points_per_decade == 0)
|
||||
if (cfg->points_per_decade == 0)
|
||||
return 1;
|
||||
float decades = log10f(cfg->freq_stop_hz / cfg->freq_start_hz);
|
||||
if (cfg->freq_start_hz == cfg->freq_stop_hz)
|
||||
return 24; /* fixed-freq repeatability test */
|
||||
float lo = fminf(cfg->freq_start_hz, cfg->freq_stop_hz);
|
||||
float hi = fmaxf(cfg->freq_start_hz, cfg->freq_stop_hz);
|
||||
float decades = log10f(hi / lo);
|
||||
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;
|
||||
|
|
@ -164,7 +178,7 @@ void eis_init(const EISConfig *cfg)
|
|||
ref.LpRefBufEn = bFALSE;
|
||||
AD5940_REFCfgS(&ref);
|
||||
|
||||
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
|
||||
AD5940_AFEPwrBW(AFEPWR_HP, AFEBW_250KHZ);
|
||||
|
||||
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DFTRDY, bTRUE);
|
||||
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bTRUE);
|
||||
|
|
@ -214,7 +228,7 @@ static void configure_freq(float freq_hz)
|
|||
fp.DftSrc = DFTSRC_ADCRAW;
|
||||
fp.ADCSinc3Osr = ADCSINC3OSR_2;
|
||||
fp.ADCSinc2Osr = 0;
|
||||
fp.DftNum = DFTNUM_16384;
|
||||
fp.DftNum = DFTNUM_4096;
|
||||
}
|
||||
|
||||
AD5940_WriteReg(REG_AFE_WGFCW,
|
||||
|
|
@ -247,7 +261,10 @@ 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)
|
||||
/* paired DFT: two measurements under continuous WG excitation */
|
||||
static void dft_measure_pair(
|
||||
uint32_t mux1_p, uint32_t mux1_n, iImpCar_Type *out1,
|
||||
uint32_t mux2_p, uint32_t mux2_n, iImpCar_Type *out2)
|
||||
{
|
||||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bFALSE);
|
||||
AD5940_WriteReg(REG_AFE_FIFOCON, 0);
|
||||
|
|
@ -255,32 +272,48 @@ static void dft_measure(uint32_t mux_p, uint32_t mux_n, iImpCar_Type *out)
|
|||
AD5940_ReadAfeResult(AFERESULT_DFTIMAGE);
|
||||
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
|
||||
|
||||
AD5940_ADCMuxCfgS(mux_p, mux_n);
|
||||
|
||||
AD5940_ADCMuxCfgS(mux1_p, mux1_n);
|
||||
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR, bTRUE);
|
||||
AD5940_Delay10us(25);
|
||||
|
||||
AD5940_ClrMCUIntFlag();
|
||||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bTRUE);
|
||||
|
||||
while (!AD5940_GetMCUIntFlag())
|
||||
vTaskDelay(1);
|
||||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bFALSE);
|
||||
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
|
||||
|
||||
out1->Real = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTREAL));
|
||||
out1->Image = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTIMAGE));
|
||||
out1->Image = -out1->Image;
|
||||
|
||||
/* switch ADC mux, flush stale pipeline, short settle */
|
||||
AD5940_ADCMuxCfgS(mux2_p, mux2_n);
|
||||
AD5940_ReadAfeResult(AFERESULT_DFTREAL);
|
||||
AD5940_ReadAfeResult(AFERESULT_DFTIMAGE);
|
||||
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
|
||||
AD5940_Delay10us(5);
|
||||
|
||||
AD5940_ClrMCUIntFlag();
|
||||
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bTRUE);
|
||||
while (!AD5940_GetMCUIntFlag())
|
||||
vTaskDelay(1);
|
||||
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;
|
||||
out2->Real = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTREAL));
|
||||
out2->Image = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTIMAGE));
|
||||
out2->Image = -out2->Image;
|
||||
}
|
||||
|
||||
/* RTIA calibration: 2 DFTs through current RCAL switch config */
|
||||
static fImpCar_Type measure_rtia(void)
|
||||
static fImpCar_Type measure_rtia(iImpCar_Type *out_hstia)
|
||||
{
|
||||
iImpCar_Type v_rcal, v_raw;
|
||||
dft_measure(ADCMUXP_P_NODE, ADCMUXN_N_NODE, &v_rcal);
|
||||
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_raw);
|
||||
dft_measure_pair(
|
||||
ADCMUXP_P_NODE, ADCMUXN_N_NODE, &v_rcal,
|
||||
ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_raw);
|
||||
if (out_hstia) *out_hstia = v_raw;
|
||||
v_raw.Real = -v_raw.Real;
|
||||
v_raw.Image = -v_raw.Image;
|
||||
fImpCar_Type rtia = AD5940_ComplexDivInt(&v_raw, &v_rcal);
|
||||
|
|
@ -309,8 +342,9 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
AFECTRL_EXTBUFPWR | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR |
|
||||
AFECTRL_SINC2NOTCH, bTRUE);
|
||||
|
||||
/* RCAL before */
|
||||
fImpCar_Type rtia_before = measure_rtia();
|
||||
/* RCAL before — capture raw HSTIA DFT for ratiometric diagnostic */
|
||||
iImpCar_Type rcal_hstia;
|
||||
fImpCar_Type rtia_before = measure_rtia(&rcal_hstia);
|
||||
|
||||
/* DUT forward */
|
||||
sw.Dswitch = ctx.dut_sw_d;
|
||||
|
|
@ -320,10 +354,11 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
AD5940_SWMatrixCfgS(&sw);
|
||||
AD5940_Delay10us(50);
|
||||
|
||||
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia);
|
||||
dft_measure_pair(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia,
|
||||
ctx.dut_mux_vp, ctx.dut_mux_vn, &v_sense);
|
||||
iImpCar_Type dut_hstia_raw = v_tia;
|
||||
v_tia.Real = -v_tia.Real;
|
||||
v_tia.Image = -v_tia.Image;
|
||||
dft_measure(ctx.dut_mux_vp, ctx.dut_mux_vn, &v_sense);
|
||||
|
||||
iImpCar_Type v_tia_fwd = v_tia;
|
||||
iImpCar_Type v_sense_fwd = v_sense;
|
||||
|
|
@ -336,7 +371,7 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
AD5940_SWMatrixCfgS(&sw);
|
||||
AD5940_Delay10us(50);
|
||||
|
||||
fImpCar_Type rtia_after = measure_rtia();
|
||||
fImpCar_Type rtia_after = measure_rtia(NULL);
|
||||
|
||||
/* DUT reverse (DUT first, then RCAL) */
|
||||
sw.Dswitch = ctx.dut_sw_d;
|
||||
|
|
@ -346,10 +381,10 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
AD5940_SWMatrixCfgS(&sw);
|
||||
AD5940_Delay10us(50);
|
||||
|
||||
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia);
|
||||
dft_measure_pair(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia,
|
||||
ctx.dut_mux_vp, ctx.dut_mux_vn, &v_sense);
|
||||
v_tia.Real = -v_tia.Real;
|
||||
v_tia.Image = -v_tia.Image;
|
||||
dft_measure(ctx.dut_mux_vp, ctx.dut_mux_vn, &v_sense);
|
||||
|
||||
iImpCar_Type v_tia_rev = v_tia;
|
||||
iImpCar_Type v_sense_rev = v_sense;
|
||||
|
|
@ -362,7 +397,7 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
AD5940_SWMatrixCfgS(&sw);
|
||||
AD5940_Delay10us(50);
|
||||
|
||||
fImpCar_Type rtia_rev = measure_rtia();
|
||||
fImpCar_Type rtia_rev = measure_rtia(NULL);
|
||||
|
||||
/* power down, open switches */
|
||||
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR | AFECTRL_ADCCNV |
|
||||
|
|
@ -391,9 +426,29 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
fImpCar_Type ft_rev = { (float)v_tia_rev.Real, (float)v_tia_rev.Image };
|
||||
num = AD5940_ComplexMulFloat(&fs_rev, &rtia_rev);
|
||||
fImpCar_Type z_rev = AD5940_ComplexDivFloat(&num, &ft_rev);
|
||||
(void)z_rev;
|
||||
|
||||
/* HSTIA-only ratiometric: Z = (DftRcal / DftDut) * RCAL */
|
||||
fImpCar_Type fr = { (float)rcal_hstia.Real, (float)rcal_hstia.Image };
|
||||
fImpCar_Type fd = { (float)dut_hstia_raw.Real, (float)dut_hstia_raw.Image };
|
||||
fImpCar_Type z_ratio = AD5940_ComplexDivFloat(&fr, &fd);
|
||||
z_ratio.Real *= ctx.rcal_ohms;
|
||||
z_ratio.Image *= ctx.rcal_ohms;
|
||||
|
||||
/* apply open-circuit compensation if available */
|
||||
if (ocal.valid) {
|
||||
for (uint32_t k = 0; k < ocal.n; k++) {
|
||||
if (fabsf(ocal.freq[k] - freq_hz) < freq_hz * 0.01f) {
|
||||
fImpCar_Type one = {1.0f, 0.0f};
|
||||
fImpCar_Type y_meas = AD5940_ComplexDivFloat(&one, &z_fwd);
|
||||
fImpCar_Type y_corr = AD5940_ComplexSubFloat(&y_meas, &ocal.y[k]);
|
||||
z_fwd = AD5940_ComplexDivFloat(&one, &y_corr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float mag_fwd = AD5940_ComplexMag(&z_fwd);
|
||||
float mag_rev = AD5940_ComplexMag(&z_rev);
|
||||
|
||||
out->freq_hz = freq_hz;
|
||||
out->z_real = z_fwd.Real;
|
||||
|
|
@ -402,10 +457,9 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
|||
out->phase_deg = AD5940_ComplexPhase(&z_fwd) * (float)(180.0 / M_PI);
|
||||
out->rtia_mag_before = AD5940_ComplexMag(&rtia_before);
|
||||
out->rtia_mag_after = AD5940_ComplexMag(&rtia_after);
|
||||
out->rev_mag = mag_rev;
|
||||
out->rev_phase = AD5940_ComplexPhase(&z_rev) * (float)(180.0 / M_PI);
|
||||
out->pct_err = (mag_fwd > 0.0f)
|
||||
? fabsf(mag_fwd - mag_rev) / mag_fwd * 100.0f : 0.0f;
|
||||
out->rev_mag = AD5940_ComplexMag(&z_ratio);
|
||||
out->rev_phase = AD5940_ComplexPhase(&z_ratio) * (float)(180.0 / M_PI);
|
||||
out->pct_err = 0.0f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -427,23 +481,33 @@ int eis_sweep(EISPoint *out, uint32_t max_points, eis_point_cb_t cb)
|
|||
sweep.SweepLog = bTRUE;
|
||||
sweep.SweepIndex = 0;
|
||||
|
||||
printf("\n%10s %12s %10s %12s %12s %7s\n",
|
||||
"Freq(Hz)", "|Z|(Ohm)", "Phase(deg)", "Re(Ohm)", "Im(Ohm)", "Err%");
|
||||
printf("---------------------------------------------------------------------\n");
|
||||
printf("\n%10s %12s %10s %12s %12s | %12s %10s %6s\n",
|
||||
"Freq(Hz)", "|Z|dual", "Ph_dual", "Re_dual", "Im_dual",
|
||||
"|Z|ratio", "Ph_ratio", "ms");
|
||||
printf("--------------------------------------------------------------------------"
|
||||
"-------------------------\n");
|
||||
|
||||
uint32_t t0 = xTaskGetTickCount();
|
||||
eis_measure_point(ctx.cfg.freq_start_hz, &out[0]);
|
||||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f %6.2f%%\n",
|
||||
uint32_t t1 = xTaskGetTickCount();
|
||||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f | %12.2f %10.2f %6lu\n",
|
||||
out[0].freq_hz, out[0].mag_ohms, out[0].phase_deg,
|
||||
out[0].z_real, out[0].z_imag, out[0].pct_err);
|
||||
out[0].z_real, out[0].z_imag,
|
||||
out[0].rev_mag, out[0].rev_phase,
|
||||
(unsigned long)((t1 - t0) * portTICK_PERIOD_MS));
|
||||
if (cb) cb(0, &out[0]);
|
||||
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
float freq;
|
||||
AD5940_SweepNext(&sweep, &freq);
|
||||
t0 = xTaskGetTickCount();
|
||||
eis_measure_point(freq, &out[i]);
|
||||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f %6.2f%%\n",
|
||||
t1 = xTaskGetTickCount();
|
||||
printf("%10.1f %12.2f %10.2f %12.2f %12.2f | %12.2f %10.2f %6lu\n",
|
||||
out[i].freq_hz, out[i].mag_ohms, out[i].phase_deg,
|
||||
out[i].z_real, out[i].z_imag, out[i].pct_err);
|
||||
out[i].z_real, out[i].z_imag,
|
||||
out[i].rev_mag, out[i].rev_phase,
|
||||
(unsigned long)((t1 - t0) * portTICK_PERIOD_MS));
|
||||
if (cb) cb((uint16_t)i, &out[i]);
|
||||
}
|
||||
|
||||
|
|
@ -453,3 +517,71 @@ int eis_sweep(EISPoint *out, uint32_t max_points, eis_point_cb_t cb)
|
|||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
#define NVS_OCAL_NS "eis"
|
||||
#define NVS_OCAL_KEY "ocal"
|
||||
|
||||
static void ocal_save_nvs(void)
|
||||
{
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_OCAL_NS, NVS_READWRITE, &h) != ESP_OK) return;
|
||||
nvs_set_blob(h, NVS_OCAL_KEY, &ocal, sizeof(ocal));
|
||||
nvs_commit(h);
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
static void ocal_erase_nvs(void)
|
||||
{
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_OCAL_NS, NVS_READWRITE, &h) != ESP_OK) return;
|
||||
nvs_erase_key(h, NVS_OCAL_KEY);
|
||||
nvs_commit(h);
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
void eis_load_open_cal(void)
|
||||
{
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_OCAL_NS, NVS_READONLY, &h) != ESP_OK) return;
|
||||
size_t len = sizeof(ocal);
|
||||
if (nvs_get_blob(h, NVS_OCAL_KEY, &ocal, &len) == ESP_OK && len == sizeof(ocal) && ocal.valid) {
|
||||
printf("Open-circuit cal loaded from NVS: %u points\n", (unsigned)ocal.n);
|
||||
} else {
|
||||
ocal.valid = 0;
|
||||
ocal.n = 0;
|
||||
}
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
int eis_open_cal(EISPoint *buf, uint32_t max_points, eis_point_cb_t cb)
|
||||
{
|
||||
ocal.valid = 0;
|
||||
ocal.n = 0;
|
||||
|
||||
int n = eis_sweep(buf, max_points, cb);
|
||||
if (n <= 0) return n;
|
||||
|
||||
fImpCar_Type one = {1.0f, 0.0f};
|
||||
for (int i = 0; i < n && i < EIS_MAX_POINTS; i++) {
|
||||
fImpCar_Type z = { buf[i].z_real, buf[i].z_imag };
|
||||
ocal.y[i] = AD5940_ComplexDivFloat(&one, &z);
|
||||
ocal.freq[i] = buf[i].freq_hz;
|
||||
}
|
||||
ocal.n = (uint32_t)n;
|
||||
ocal.valid = 1;
|
||||
ocal_save_nvs();
|
||||
printf("Open-circuit cal stored: %d points\n", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
void eis_clear_open_cal(void)
|
||||
{
|
||||
ocal.valid = 0;
|
||||
ocal.n = 0;
|
||||
ocal_erase_nvs();
|
||||
}
|
||||
|
||||
int eis_has_open_cal(void)
|
||||
{
|
||||
return ocal.valid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,4 +58,9 @@ int eis_measure_point(float freq_hz, EISPoint *out);
|
|||
int eis_sweep(EISPoint *out, uint32_t max_points, eis_point_cb_t cb);
|
||||
uint32_t eis_calc_num_points(const EISConfig *cfg);
|
||||
|
||||
int eis_open_cal(EISPoint *buf, uint32_t max_points, eis_point_cb_t cb);
|
||||
void eis_clear_open_cal(void);
|
||||
int eis_has_open_cal(void);
|
||||
void eis_load_open_cal(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
17
main/eis4.c
17
main/eis4.c
|
|
@ -52,6 +52,7 @@ void app_main(void)
|
|||
if (adiid != AD5941_EXPECTED_ADIID) return;
|
||||
|
||||
eis_default_config(&cfg);
|
||||
eis_load_open_cal();
|
||||
temp_init();
|
||||
|
||||
esp_log_level_set("NimBLE", ESP_LOG_WARN);
|
||||
|
|
@ -183,6 +184,22 @@ void app_main(void)
|
|||
printf("Refs cleared\n");
|
||||
break;
|
||||
|
||||
case CMD_OPEN_CAL: {
|
||||
printf("Open-circuit cal starting\n");
|
||||
eis_init(&cfg);
|
||||
uint32_t n = eis_calc_num_points(&cfg);
|
||||
ble_send_sweep_start(n, cfg.freq_start_hz, cfg.freq_stop_hz);
|
||||
int got = eis_open_cal(results, n, ble_send_eis_point);
|
||||
printf("Open-circuit cal: %d points\n", got);
|
||||
ble_send_sweep_end();
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_CLEAR_OPEN_CAL:
|
||||
eis_clear_open_cal();
|
||||
printf("Open-circuit cal cleared\n");
|
||||
break;
|
||||
|
||||
case CMD_START_CL: {
|
||||
ClConfig cl_cfg;
|
||||
cl_cfg.v_cond = cmd.cl.v_cond;
|
||||
|
|
|
|||
Loading…
Reference in New Issue