474 lines
14 KiB
C
474 lines
14 KiB
C
// File: EIS/main.c
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include "pico/stdlib.h"
|
|
#include "hardware/spi.h"
|
|
#include "hardware/gpio.h"
|
|
#include "ad5940.h"
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Pin Definitions
|
|
// --------------------------------------------------------------------------
|
|
#define PIN_MISO 0
|
|
#define PIN_CS 1
|
|
#define PIN_SCK 2
|
|
#define PIN_MOSI 3
|
|
#define PIN_RESET 9
|
|
#define PIN_AD_INTERRUPT 29
|
|
|
|
#define SPI_PORT spi0
|
|
#define SPI_BAUDRATE_HIGH 500000
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Switch Matrix Bit Definitions
|
|
// --------------------------------------------------------------------------
|
|
#ifndef SWT_SE0
|
|
#define SWT_SE0 (1 << 6) // T7
|
|
#endif
|
|
#ifndef SWT_T9
|
|
#define SWT_T9 (1 << 8) // T9
|
|
#endif
|
|
#ifndef SWT_TR1
|
|
#define SWT_TR1 (1 << 11) // TR1
|
|
#endif
|
|
|
|
// DSWFULLCON
|
|
#ifndef SWD_AIN2
|
|
#define SWD_AIN2 (1 << 2) // D3
|
|
#endif
|
|
#ifndef SWD_CE0
|
|
#define SWD_CE0 (1 << 4) // D5
|
|
#endif
|
|
|
|
// TSWFULLCON
|
|
#ifndef SWT_AIN3
|
|
#define SWT_AIN3 (1 << 3) // T4
|
|
#endif
|
|
|
|
// --------------------------------------------------------------------------
|
|
// AD5940 Library Interface
|
|
// --------------------------------------------------------------------------
|
|
|
|
void AD5940_CsClr(void) {
|
|
gpio_put(PIN_CS, 0);
|
|
sleep_us(1);
|
|
}
|
|
|
|
void AD5940_CsSet(void) {
|
|
sleep_us(1);
|
|
gpio_put(PIN_CS, 1);
|
|
sleep_us(1);
|
|
}
|
|
|
|
void AD5940_RstClr(void) { gpio_put(PIN_RESET, 0); }
|
|
void AD5940_RstSet(void) { gpio_put(PIN_RESET, 1); }
|
|
void AD5940_Delay10us(uint32_t time) { sleep_us(time * 10); }
|
|
|
|
uint32_t AD5940_GetMCUIntFlag(void) {
|
|
uint32_t flag = AD5940_ReadReg(REG_INTC_INTCFLAG0);
|
|
return (flag != 0);
|
|
}
|
|
|
|
void AD5940_ReadWriteNBytes(unsigned char *pSendBuffer, unsigned char *pRecvBuffer, unsigned long length) {
|
|
if (pRecvBuffer == NULL) {
|
|
if (pSendBuffer != NULL) {
|
|
spi_write_blocking(SPI_PORT, pSendBuffer, length);
|
|
}
|
|
} else {
|
|
if (pSendBuffer == NULL) {
|
|
uint8_t dummy = 0x00;
|
|
for (unsigned long i = 0; i < length; i++) {
|
|
spi_write_read_blocking(SPI_PORT, &dummy, &pRecvBuffer[i], 1);
|
|
}
|
|
} else {
|
|
spi_write_read_blocking(SPI_PORT, pSendBuffer, pRecvBuffer, length);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Helpers
|
|
// --------------------------------------------------------------------------
|
|
|
|
void AD5940_PulseReset(void) {
|
|
AD5940_RstClr();
|
|
sleep_ms(10);
|
|
AD5940_RstSet();
|
|
sleep_ms(50); // Increased wait for boot stability
|
|
}
|
|
|
|
// Helper to map Register Macro to Actual Sample Count for calculations
|
|
uint32_t GetDftSamples(uint32_t dft_num_macro) {
|
|
if (dft_num_macro == DFTNUM_256) return 256;
|
|
if (dft_num_macro == DFTNUM_512) return 512;
|
|
if (dft_num_macro == DFTNUM_1024) return 1024;
|
|
if (dft_num_macro == DFTNUM_2048) return 2048;
|
|
if (dft_num_macro == DFTNUM_4096) return 4096;
|
|
if (dft_num_macro == DFTNUM_8192) return 8192;
|
|
if (dft_num_macro == DFTNUM_16384) return 16384;
|
|
return 2048; // Safe default
|
|
}
|
|
|
|
// Structure to hold calculated configuration
|
|
typedef struct {
|
|
uint32_t DftNum;
|
|
uint32_t DftSrc;
|
|
uint32_t Sinc3Osr;
|
|
uint32_t Sinc2Osr;
|
|
float SampleRate; // Estimated
|
|
} FilterConfig_Type;
|
|
|
|
// Calculates optimal DFT and Filter settings based on frequency
|
|
// Optimized for High Speed Loop (HFOSC 16MHz) only
|
|
void AD5940_GetFilterCfg(float freq, FilterConfig_Type *pCfg) {
|
|
// Strategy:
|
|
// For > 50Hz, use SINC3 (Fast, ~200kHz)
|
|
// For <= 50Hz, use SINC2 path (Slower, ~9kHz) to capture full waves
|
|
|
|
if (freq >= 50.0f) {
|
|
pCfg->DftSrc = DFTSRC_SINC3;
|
|
pCfg->Sinc3Osr = ADCSINC3OSR_4;
|
|
pCfg->Sinc2Osr = ADCSINC2OSR_22; // Don't care for SINC3 src
|
|
pCfg->SampleRate = 200000.0f; // 800k / 4
|
|
|
|
// Adjust DFT size for speed vs accuracy at high freq
|
|
if (freq > 40000.0f) pCfg->DftNum = DFTNUM_2048;
|
|
else if (freq > 1000.0f) pCfg->DftNum = DFTNUM_4096;
|
|
else pCfg->DftNum = DFTNUM_8192;
|
|
}
|
|
else {
|
|
// Low Frequency High Speed (e.g. 10Hz)
|
|
// Use SINC2 filter to slow down data rate
|
|
// Rate = 800k / (4 * 22) = 9090 Hz
|
|
pCfg->DftSrc = DFTSRC_SINC2NOTCH;
|
|
pCfg->Sinc3Osr = ADCSINC3OSR_4;
|
|
pCfg->Sinc2Osr = ADCSINC2OSR_22;
|
|
pCfg->SampleRate = 9090.0f;
|
|
|
|
// 8192 samples @ 9kHz = ~0.9 seconds
|
|
// Covers 9 cycles of 10Hz. Perfect.
|
|
pCfg->DftNum = DFTNUM_8192;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Measurement Functions
|
|
// --------------------------------------------------------------------------
|
|
|
|
static uint32_t AppBuff[512];
|
|
|
|
// Internal Helper for Calibration
|
|
static AD5940Err RunCalibration(void)
|
|
{
|
|
ADCPGACal_Type pga_cal;
|
|
|
|
pga_cal.AdcClkFreq = 16000000.0;
|
|
pga_cal.SysClkFreq = 16000000.0;
|
|
pga_cal.ADCPga = ADCPGA_1;
|
|
pga_cal.ADCSinc2Osr = ADCSINC2OSR_1333;
|
|
pga_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
|
pga_cal.TimeOut10us = 100000;
|
|
pga_cal.VRef1p82 = 1.82;
|
|
pga_cal.VRef1p11 = 1.11;
|
|
pga_cal.PGACalType = PGACALTYPE_OFFSETGAIN;
|
|
|
|
return AD5940_ADCPGACal(&pga_cal);
|
|
}
|
|
|
|
// Command Handler for "CAL"
|
|
static void AppADCPgaCal(void)
|
|
{
|
|
printf("LOG,Preparing for Calibration...\n");
|
|
// Ensure clean state before CAL
|
|
AD5940_PulseReset();
|
|
AD5940_Initialize();
|
|
AD5940_WriteReg(REG_INTC_INTCSEL0, 0xFFFFFFFF);
|
|
AD5940_WriteReg(REG_INTC_INTCCLR, 0xFFFFFFFF);
|
|
|
|
AD5940Err err = RunCalibration();
|
|
if(err != AD5940ERR_OK) printf("LOG,ADC Cal Failed: %d\n", err);
|
|
else printf("LOG,ADC Cal Passed.\n");
|
|
|
|
// Cleanup: Turn off everything after CAL to leave clean slate
|
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
|
}
|
|
|
|
// High Speed Loop (HSTIA + HSDAC) for ALL Frequencies
|
|
void MeasureHighSpeed(float freq)
|
|
{
|
|
// 1. HARD RESET & Re-Initialize
|
|
AD5940_PulseReset();
|
|
AD5940_Initialize();
|
|
AD5940_WriteReg(REG_INTC_INTCSEL0, 0xFFFFFFFF);
|
|
AD5940_WriteReg(REG_INTC_INTCCLR, 0xFFFFFFFF);
|
|
|
|
// 2. Calibrate
|
|
// Note: Calibration routines are invasive. They might leave the AFE in weird states.
|
|
AD5940Err calErr = RunCalibration();
|
|
if(calErr != AD5940ERR_OK) {
|
|
printf("LOG,Auto-Calibration Failed: %d\n", calErr);
|
|
}
|
|
|
|
// CRITICAL: Shut down EVERYTHING after calibration.
|
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
|
|
|
// CRITICAL: Disable Sequencer
|
|
AD5940_SEQCtrlS(bFALSE);
|
|
|
|
// 3. Clock Configuration
|
|
AD5940_WriteReg(REG_ALLON_OSCKEY, 0xCB14);
|
|
AD5940_WriteReg(REG_ALLON_OSCCON, 0x0003); // Enable HFOSC
|
|
sleep_ms(2);
|
|
|
|
AD5940_WriteReg(REG_AFE_LPMODEKEY, 0xC59D6);
|
|
AD5940_WriteReg(REG_AFE_LPMODECLKSEL, 0); // Select HFOSC
|
|
|
|
spi_set_baudrate(SPI_PORT, SPI_BAUDRATE_HIGH);
|
|
|
|
// 4. Switch Matrix Cleanup
|
|
AD5940_WriteReg(REG_AFE_LPTIASW0, 0x0000);
|
|
AD5940_WriteReg(REG_AFE_LPDACSW0, 0x0000);
|
|
|
|
// 5. Configure Loop
|
|
HSLoopCfg_Type hs_loop;
|
|
hs_loop.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2;
|
|
hs_loop.HsDacCfg.HsDacGain = HSDACGAIN_1;
|
|
hs_loop.HsDacCfg.HsDacUpdateRate = 7;
|
|
|
|
if (freq > 1000.0f) {
|
|
hs_loop.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_1K;
|
|
} else {
|
|
hs_loop.HsTiaCfg.HstiaRtiaSel = HSTIARTIA_5K;
|
|
}
|
|
|
|
hs_loop.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
|
|
hs_loop.HsTiaCfg.HstiaCtia = 16;
|
|
hs_loop.HsTiaCfg.DiodeClose = bFALSE;
|
|
|
|
AD5940_HSLoopCfgS(&hs_loop);
|
|
|
|
// 6. Switch Matrix (S+/S-)
|
|
AD5940_WriteReg(REG_AFE_SWCON, BITM_AFE_SWCON_SWSOURCESEL);
|
|
AD5940_WriteReg(REG_AFE_DSWFULLCON, SWD_CE0);
|
|
AD5940_WriteReg(REG_AFE_PSWFULLCON, 0x0000);
|
|
AD5940_WriteReg(REG_AFE_NSWFULLCON, 0x0000);
|
|
AD5940_WriteReg(REG_AFE_TSWFULLCON, SWT_SE0 | SWT_T9);
|
|
|
|
// 7. Get Dynamic Configuration
|
|
FilterConfig_Type cfg;
|
|
AD5940_GetFilterCfg(freq, &cfg);
|
|
|
|
// 8. ADC Configuration
|
|
ADCBaseCfg_Type adc_cfg;
|
|
memset(&adc_cfg, 0, sizeof(adc_cfg));
|
|
adc_cfg.ADCMuxP = ADCMUXP_HSTIA_P;
|
|
adc_cfg.ADCMuxN = ADCMUXN_HSTIA_N;
|
|
adc_cfg.ADCPga = ADCPGA_1;
|
|
AD5940_ADCBaseCfgS(&adc_cfg);
|
|
|
|
// Initialize and Enable Clocks
|
|
ADCFilterCfg_Type filter_cfg;
|
|
memset(&filter_cfg, 0, sizeof(filter_cfg));
|
|
|
|
filter_cfg.ADCSinc3Osr = cfg.Sinc3Osr;
|
|
filter_cfg.ADCSinc2Osr = cfg.Sinc2Osr;
|
|
filter_cfg.ADCAvgNum = ADCAVGNUM_16;
|
|
filter_cfg.ADCRate = ADCRATE_800KHZ;
|
|
filter_cfg.BpNotch = bTRUE;
|
|
filter_cfg.BpSinc3 = bFALSE;
|
|
filter_cfg.Sinc2NotchEnable = bTRUE;
|
|
|
|
// Explicitly Enable Clocks
|
|
filter_cfg.DFTClkEnable = bTRUE;
|
|
filter_cfg.WGClkEnable = bTRUE;
|
|
filter_cfg.Sinc3ClkEnable = bTRUE;
|
|
filter_cfg.Sinc2NotchClkEnable = bTRUE;
|
|
|
|
AD5940_ADCFilterCfgS(&filter_cfg);
|
|
|
|
// Configure ADC Buffers
|
|
AD5940_WriteReg(REG_AFE_ADCBUFCON, 0x005F3D0F);
|
|
|
|
// 9. Waveform
|
|
AD5940_WriteReg(REG_AFE_WGFCW, AD5940_WGFreqWordCal(freq, 16000000.0));
|
|
AD5940_WriteReg(REG_AFE_WGAMPLITUDE, 2047);
|
|
AD5940_WriteReg(REG_AFE_WGOFFSET, 0);
|
|
AD5940_WriteReg(REG_AFE_WGPHASE, 0);
|
|
AD5940_WriteReg(REG_AFE_WGCON, WGTYPE_SIN);
|
|
|
|
// 10. DFT
|
|
DFTCfg_Type dft_cfg;
|
|
dft_cfg.DftNum = cfg.DftNum;
|
|
dft_cfg.DftSrc = cfg.DftSrc;
|
|
dft_cfg.HanWinEn = bTRUE;
|
|
AD5940_DFTCfgS(&dft_cfg);
|
|
|
|
// 11. Configure FIFO for DFT
|
|
// Ensure we capture DFT results in FIFO
|
|
FIFOCfg_Type fifo_cfg;
|
|
fifo_cfg.FIFOEn = bTRUE;
|
|
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
|
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
|
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
|
fifo_cfg.FIFOThresh = 4;
|
|
AD5940_FIFOCfg(&fifo_cfg);
|
|
|
|
// 12. Run
|
|
|
|
// Power Up Analog Blocks
|
|
AD5940_AFECtrlS(AFECTRL_HSDACPWR|AFECTRL_HSTIAPWR|AFECTRL_ADCPWR|AFECTRL_DACREFPWR|AFECTRL_EXTBUFPWR|AFECTRL_INAMPPWR, bTRUE);
|
|
|
|
sleep_ms(20);
|
|
|
|
// CRITICAL FIX: Force AFECON Clean
|
|
uint32_t current_afecon = AD5940_ReadReg(REG_AFE_AFECON);
|
|
current_afecon &= ~(0x00003000); // Clear Temp bits
|
|
AD5940_WriteReg(REG_AFE_AFECON, current_afecon);
|
|
|
|
// Enable ADC Repeat Mode
|
|
AD5940_ADCRepeatCfgS(0xffffffff);
|
|
|
|
// Clear and Enable Interrupts
|
|
AD5940_WriteReg(REG_INTC_INTCCLR, 0xFFFFFFFF);
|
|
AD5940_WriteReg(REG_INTC_INTCSEL0, 0xFFFFFFFF);
|
|
|
|
// Start Conversion
|
|
AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG, bTRUE);
|
|
|
|
// Calculate Timeout using Helper
|
|
uint32_t sample_count = GetDftSamples(cfg.DftNum);
|
|
float duration = (float)sample_count / cfg.SampleRate;
|
|
uint32_t timeout_us = (uint32_t)(duration * 1000000.0f) + 2000000;
|
|
|
|
// Polling Loop with Exit Condition
|
|
uint32_t start_time = to_us_since_boot(get_absolute_time());
|
|
bool success = false;
|
|
|
|
// Results
|
|
int32_t real = 0;
|
|
int32_t image = 0;
|
|
|
|
while((to_us_since_boot(get_absolute_time()) - start_time) < timeout_us) {
|
|
|
|
// Polling Strategy: Check FIFO Count instead of Interrupt Flags
|
|
// One DFT result = 2 words (Real + Image). We wait for >= 2 words.
|
|
uint32_t fifo_cnt = (AD5940_ReadReg(REG_AFE_FIFOCNTSTA) & 0x07FF0000) >> 16;
|
|
|
|
if(fifo_cnt >= 2) {
|
|
// Read result from FIFO
|
|
uint32_t buffer[2];
|
|
AD5940_FIFORd(buffer, 2);
|
|
real = (int32_t)buffer[0];
|
|
image = (int32_t)buffer[1];
|
|
|
|
AD5940_WriteReg(REG_INTC_INTCCLR, 0xFFFFFFFF);
|
|
success = true;
|
|
break;
|
|
}
|
|
sleep_us(100);
|
|
}
|
|
|
|
if (!success) {
|
|
uint32_t flags = AD5940_ReadReg(REG_INTC_INTCFLAG0);
|
|
uint32_t afecon_read = AD5940_ReadReg(REG_AFE_AFECON);
|
|
uint32_t fifo_cnt = (AD5940_ReadReg(REG_AFE_FIFOCNTSTA) & 0x07FF0000) >> 16;
|
|
printf("LOG,Error: Measurement Timeout (HS). Exp Duration: %.2fs. Flags: 0x%08X, AFECON: 0x%08X, FIFO: %d\n", duration, flags, afecon_read, fifo_cnt);
|
|
}
|
|
|
|
// 13. Process Result
|
|
// Sign Extend 18-bit to 32-bit
|
|
// Store original raw values before processing
|
|
int32_t raw_real = real;
|
|
int32_t raw_image = image;
|
|
|
|
if(real & (1<<17)) real |= 0xFFFC0000;
|
|
if(image & (1<<17)) image |= 0xFFFC0000;
|
|
|
|
float mag = sqrt((float)real*real + (float)image*image);
|
|
float rtia_val = (freq > 1000.0f) ? 1000.0f : 5000.0f;
|
|
float impedance = (mag > 0) ? (2047.0f / mag) * rtia_val : 0;
|
|
float phase = atan2((float)image, (float)real);
|
|
|
|
// Updated Output Format: Freq, Impedance, Phase, RawReal, RawImag
|
|
printf("DATA,%.2f,%.4f,%.4f,%d,%d\n", freq, impedance, phase, raw_real, raw_image);
|
|
|
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Main
|
|
// --------------------------------------------------------------------------
|
|
|
|
void process_command(char* cmd) {
|
|
char* token = strtok(cmd, " ");
|
|
if (!token) return;
|
|
|
|
if (strcmp(token, "CAL") == 0) {
|
|
AppADCPgaCal();
|
|
}
|
|
else if (strcmp(token, "MEAS") == 0) {
|
|
char* arg1 = strtok(NULL, " ");
|
|
if (arg1) {
|
|
float freq = strtof(arg1, NULL);
|
|
if (freq < 10.0f) {
|
|
freq = 10.0f;
|
|
}
|
|
MeasureHighSpeed(freq);
|
|
}
|
|
}
|
|
else if (strcmp(token, "ID") == 0) {
|
|
uint32_t chip_id = AD5940_ReadReg(REG_AFECON_CHIPID);
|
|
printf("LOG,Chip ID: 0x%04X\n", chip_id);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
stdio_init_all();
|
|
sleep_ms(1000);
|
|
|
|
spi_init(SPI_PORT, SPI_BAUDRATE_HIGH);
|
|
spi_set_format(SPI_PORT, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
|
|
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
|
|
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
|
|
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
|
|
|
|
gpio_init(PIN_CS);
|
|
gpio_set_dir(PIN_CS, GPIO_OUT);
|
|
gpio_put(PIN_CS, 1);
|
|
|
|
gpio_init(PIN_RESET);
|
|
gpio_set_dir(PIN_RESET, GPIO_OUT);
|
|
gpio_put(PIN_RESET, 1);
|
|
|
|
gpio_init(PIN_AD_INTERRUPT);
|
|
gpio_set_dir(PIN_AD_INTERRUPT, GPIO_IN);
|
|
gpio_pull_up(PIN_AD_INTERRUPT);
|
|
|
|
// Initial Start
|
|
AD5940_PulseReset();
|
|
AD5940_Initialize();
|
|
AD5940_WriteReg(REG_INTC_INTCSEL0, 0xFFFFFFFF);
|
|
AD5940_WriteReg(REG_INTC_INTCCLR, 0xFFFFFFFF);
|
|
AD5940_SEQGenInit(AppBuff, 512);
|
|
|
|
char buffer[64];
|
|
int pos = 0;
|
|
|
|
while (true) {
|
|
int c = getchar_timeout_us(1000);
|
|
if (c != PICO_ERROR_TIMEOUT) {
|
|
if (c == '\n' || c == '\r') {
|
|
if (pos > 0) {
|
|
buffer[pos] = 0;
|
|
process_command(buffer);
|
|
pos = 0;
|
|
}
|
|
} else if (pos < 63) {
|
|
buffer[pos++] = (char)c;
|
|
}
|
|
}
|
|
}
|
|
} |