This commit is contained in:
pszsh 2026-01-22 03:43:44 -08:00
commit 9eff1ecd36
16 changed files with 1729 additions and 0 deletions

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
.vscode
Packed
*.py
venv
build
*.swp
*.cmake
requirements.txt
ad5940*
pico_sdk_import.cmake
build*
*.png
icons/
*.pdf
build/
build_android/
build_ios/
build_macos/
build_windows/
icons
*.png

39
CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
# File: CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(EIS C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
# Add ad5940.c to the build
add_executable(EIS
main.c
ad5940.c
)
# Define CHIPSEL_594X for the library
target_compile_definitions(EIS PRIVATE CHIPSEL_594X)
# REQUIRED: Tell CMake where to find headers
target_include_directories(EIS PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(EIS
pico_stdlib
hardware_spi
hardware_gpio
hardware_dma
hardware_irq
hardware_vreg
hardware_clocks
m
)
pico_enable_stdio_usb(EIS 1)
pico_enable_stdio_uart(EIS 0)
pico_add_extra_outputs(EIS)

28
Makefile Normal file
View File

@ -0,0 +1,28 @@
# Name of your build directory
BUILD_DIR = build
# Name of the output file (Must match what is in CMakeLists.txt)
TARGET = EIS
# Default target: Build the project
all: $(BUILD_DIR)/Makefile
@$(MAKE) -C $(BUILD_DIR)
# Ensure the build directory exists and run CMake if needed
$(BUILD_DIR)/Makefile: CMakeLists.txt
@mkdir -p $(BUILD_DIR)
@cd $(BUILD_DIR) && cmake ..
# Clean up the build directory
clean:
@rm -rf $(BUILD_DIR)
# Helper to automatically flash (Mac specific path)
flash: all
@echo "Waiting for RPI-RP2 volume..."
@while [ ! -d /Volumes/RPI-RP2 ]; do sleep 0.1; done
@echo "Flashing..."
@cp $(BUILD_DIR)/$(TARGET).uf2 /Volumes/RPI-RP2/
@echo "Done."
.PHONY: all clean flash

262
drv.min.h Normal file
View File

@ -0,0 +1,262 @@
/* Library Version */
AD5940LIB_VER_MAJOR, AD5940LIB_VER_MINOR, AD5940LIB_VER_PATCH, AD5940LIB_VER
/* AGPIO Registers (0x00) */
REG_AGPIO_GP0CON, REG_AGPIO_GP0OEN, REG_AGPIO_GP0PE, REG_AGPIO_GP0IEN
REG_AGPIO_GP0IN, REG_AGPIO_GP0OUT, REG_AGPIO_GP0SET, REG_AGPIO_GP0CLR, REG_AGPIO_GP0TGL
/* AGPIO Bitmasks */
BITP/M_AGPIO_GP0CON_PINxCFG (x=0-7)
BITP/M_AGPIO_GP0OEN_OEN, BITP/M_AGPIO_GP0PE_PE, BITP/M_AGPIO_GP0IEN_IEN
BITP/M_AGPIO_GP0IN_IN, BITP/M_AGPIO_GP0OUT_OUT, BITP/M_AGPIO_GP0SET_SET
BITP/M_AGPIO_GP0CLR_CLR, BITP/M_AGPIO_GP0TGL_TGL
/* AFECON Registers (0x400) */
REG_AFECON_ADIID, REG_AFECON_CHIPID, REG_AFECON_CLKCON0, REG_AFECON_CLKEN1
REG_AFECON_CLKSEL, REG_AFECON_CLKCON0KEY, REG_AFECON_SWRSTCON, REG_AFECON_TRIGSEQ
/* AFECON Bitmasks */
BITP/M_AFECON_ADIID_ADIID, BITP/M_AFECON_CHIPID_PARTID/REVISION
BITP/M_AFECON_CLKCON0_SFFTCLKDIVCNT, ADCCLKDIV, SYSCLKDIV
BITP/M_AFECON_CLKEN1_GPT1DIS, GPT0DIS, ACLKDIS
BITP/M_AFECON_CLKSEL_ADCCLKSEL, SYSCLKSEL
BITP/M_AFECON_CLKCON0KEY_DIVSYSCLK_ULP_EN
BITP/M_AFECON_SWRSTCON_SWRSTL
BITP/M_AFECON_TRIGSEQ_TRIGx (x=0-3)
/* AFEWDT Registers (0x900) */
REG_AFEWDT_WDTLD, REG_AFEWDT_WDTVALS, REG_AFEWDT_WDTCON, REG_AFEWDT_WDTCLRI
REG_AFEWDT_WDTSTA, REG_AFEWDT_WDTMINLD
/* AFEWDT Bitmasks */
BITP/M_AFEWDT_WDTLD_LOAD, BITP/M_AFEWDT_WDTVALS_CCOUNT
BITP/M_AFEWDT_WDTCON_WDTIRQEN, MINLOAD_EN, CLKDIV2, MDE, EN, PRE, IRQ, PDSTOP
ENUM_AFEWDT_WDTCON_RESET, INTERRUPT, CONTINUE, STOP
BITP/M_AFEWDT_WDTCLRI_CLRWDG
BITP/M_AFEWDT_WDTSTA_TMINLD, OTPWRDONE, LOCK, CON, TLD, CLRI, IRQ
ENUM_AFEWDT_WDTSTA_OPEN, LOCKED, SYNC_COMPLETE, SYNC_IN_PROGRESS, CLEARED, PENDING
BITP/M_AFEWDT_WDTMINLD_MIN_LOAD
/* WUPTMR Registers (0x800) */
REG_WUPTMR_CON, REG_WUPTMR_SEQORDER
REG_WUPTMR_SEQxWUPL/H (x=0-3), REG_WUPTMR_SEQxSLEEPL/H (x=0-3)
/* WUPTMR Bitmasks */
BITP/M_WUPTMR_CON_MSKTRG, CLKSEL, ENDSEQ, EN
ENUM_WUPTMR_CON_SWT32K0, SWTEXT0, SWT32K, SWTEXT, SWTEN, SWTDIS
ENUM_WUPTMR_CON_ENDSEQx (x=A-H)
BITP/M_WUPTMR_SEQORDER_SEQx (x=A-H)
ENUM_WUPTMR_SEQORDER_SEQx0-3
BITP/M_WUPTMR_SEQxWUPL/H_WAKEUPTIMEx, BITP/M_WUPTMR_SEQxSLEEPL/H_SLEEPTIMEx
/* ALLON Registers (0xA00) */
REG_ALLON_PWRMOD, REG_ALLON_PWRKEY, REG_ALLON_OSCKEY, REG_ALLON_OSCCON
REG_ALLON_TMRCON, REG_ALLON_EI0CON, REG_ALLON_EI1CON, REG_ALLON_EI2CON
REG_ALLON_EICLR, REG_ALLON_RSTSTA, REG_ALLON_RSTCONKEY, REG_ALLON_LOSCTST, REG_ALLON_CLKEN0
/* ALLON Bitmasks */
BITP/M_ALLON_PWRMOD_RAMRETEN, ADCRETEN, SEQSLPEN, TMRSLPEN, PWRMOD
BITP/M_ALLON_PWRKEY_PWRKEY, BITP/M_ALLON_OSCKEY_OSCKEY
BITP/M_ALLON_OSCCON_HFXTALOK, HFOSCOK, LFOSCOK, HFXTALEN, HFOSCEN, LFOSCEN
BITP/M_ALLON_TMRCON_TMRINTEN
BITP/M_ALLON_EI0CON_IRQxEN/MDE (x=0-3)
BITP/M_ALLON_EI1CON_IRQxEN/MDE (x=4-7)
BITP/M_ALLON_EI2CON_BUSINTEN, BUSINTMDE
BITP/M_ALLON_EICLR_AUTCLRBUSEN, BUSINT
BITP/M_ALLON_RSTSTA_PINSWRST, MMRSWRST, WDRST, EXTRST, POR
BITP/M_ALLON_RSTCONKEY_KEY
BITP/M_ALLON_LOSCTST_TRIM
BITP/M_ALLON_CLKEN0_TIACHPDIS, SLPWUTDIS, WDTDIS
/* AGPT0 Registers (0xD00) & AGPT1 Registers (0xE00) */
REG_AGPT0_LD0, REG_AGPT0_VAL0, REG_AGPT0_CON0, REG_AGPT0_CLRI0, REG_AGPT0_CAP0
REG_AGPT0_ALD0, REG_AGPT0_AVAL0, REG_AGPT0_STA0, REG_AGPT0_PWMCON0, REG_AGPT0_PWMMAT0, REG_AGPT0_INTEN
REG_AGPT1_LD1, REG_AGPT1_VAL1, REG_AGPT1_CON1, REG_AGPT1_CLRI1, REG_AGPT1_CAP1
REG_AGPT1_ALD1, REG_AGPT1_AVAL1, REG_AGPT1_STA1, REG_AGPT1_PWMCON1, REG_AGPT1_PWMMAT1, REG_AGPT1_INTEN1
/* AGPT Bitmasks */
BITP/M_AGPTx_CONx_SYNCBYP, RSTEN, EVTEN, EVENT, RLD, CLK, ENABLE, MOD, UP, PRE
BITP/M_AGPTx_CLRIx_CAP, TMOUT
BITP/M_AGPTx_STAx_RSTCNT, PDOK, BUSY, CAP, TMOUT
BITP/M_AGPTx_PWMCONx_IDLE, MATCHEN
/* AFECRC Registers (0x1000) */
REG_AFECRC_CTL, REG_AFECRC_IPDATA, REG_AFECRC_RESULT, REG_AFECRC_POLY
REG_AFECRC_IPBITS, REG_AFECRC_IPBYTE, REG_AFECRC_CRC_SIG_COMP, REG_AFECRC_CRCINTEN, REG_AFECRC_INTSTA
/* AFECRC Bitmasks */
BITP/M_AFECRC_CTL_REVID, MON_EN, W16SWP, BYTMIRR, BITMIRR, LSBFIRST, EN
/* AFE Registers (0x2000) */
REG_AFE_AFECON, REG_AFE_SEQCON, REG_AFE_FIFOCON, REG_AFE_SWCON, REG_AFE_HSDACCON
REG_AFE_WGCON, REG_AFE_WGDCLEVEL1/2, REG_AFE_WGDELAY1/2, REG_AFE_WGSLOPE1/2
REG_AFE_WGFCW, REG_AFE_WGPHASE, REG_AFE_WGOFFSET, REG_AFE_WGAMPLITUDE
REG_AFE_ADCFILTERCON, REG_AFE_HSDACDAT, REG_AFE_LPREFBUFCON, REG_AFE_SYNCEXTDEVICE
REG_AFE_SEQCRC, REG_AFE_SEQCNT, REG_AFE_SEQTIMEOUT, REG_AFE_DATAFIFORD, REG_AFE_CMDFIFOWRITE
REG_AFE_ADCDAT, REG_AFE_DFTREAL, REG_AFE_DFTIMAG, REG_AFE_SINC2DAT, REG_AFE_TEMPSENSDAT
REG_AFE_AFEGENINTSTA, REG_AFE_ADCMIN/SM, REG_AFE_ADCMAX/SMEN, REG_AFE_ADCDELTA
REG_AFE_HPOSCCON, REG_AFE_DFTCON
REG_AFE_LPTIASW0/1, REG_AFE_LPTIACON0/1
REG_AFE_HSRTIACON, REG_AFE_DE0/1RESCON, REG_AFE_HSTIACON
REG_AFE_LPMODEKEY, REG_AFE_LPMODECLKSEL, REG_AFE_LPMODECON
REG_AFE_SEQSLPLOCK, REG_AFE_SEQTRGSLP
REG_AFE_LPDACDAT0/1, REG_AFE_LPDACSW0/1, REG_AFE_LPDACCON0/1
REG_AFE_DSWFULLCON, REG_AFE_NSWFULLCON, REG_AFE_PSWFULLCON, REG_AFE_TSWFULLCON
REG_AFE_TEMPSENS, REG_AFE_BUFSENCON, REG_AFE_ADCCON
REG_AFE_DSWSTA, REG_AFE_PSWSTA, REG_AFE_NSWSTA, REG_AFE_TSWSTA
REG_AFE_STATSVAR, REG_AFE_STATSCON, REG_AFE_STATSMEAN
REG_AFE_SEQ0/1/2/3INFO, REG_AFE_CMDFIFOWADDR, REG_AFE_CMDDATACON, REG_AFE_DATAFIFOTHRES
REG_AFE_REPEATADCCNV, REG_AFE_FIFOCNTSTA, REG_AFE_CALDATLOCK
REG_AFE_ADCOFFSETHSTIA, REG_AFE_ADCGAINTEMPSENS0, REG_AFE_ADCOFFSETTEMPSENS0/1
REG_AFE_ADCGAINGN1/1P5/2/4/9, REG_AFE_ADCOFFSETGN1/1P5/2/4/9
REG_AFE_DACGAIN, REG_AFE_DACOFFSET, REG_AFE_DACOFFSETATTEN/HP
REG_AFE_ADCPGAOFFSETCANCEL, REG_AFE_ADCGNHSTIA, REG_AFE_ADCPGAGN4OFCAL
REG_AFE_ADCOFFSETLPTIA0/1, REG_AFE_ADCGNLPTIA0/1, REG_AFE_ADCGAINDIOTEMPSENS
REG_AFE_PMBW, REG_AFE_SWMUX, REG_AFE_AFE_TEMPSEN_DIO, REG_AFE_ADCBUFCON
/* AFE Bitmasks & Enums */
BITP/M_AFE_AFECON_DACBUFEN, DACREFEN, ALDOILIMITEN, SINC2EN, DFTEN, WAVEGENEN
BITP/M_AFE_AFECON_TEMPCONVEN, TEMPSENSEN, TIAEN, INAMPEN, EXBUFEN, ADCCONVEN, ADCEN, DACEN, HPREFDIS
ENUM_AFE_AFECON_OFF, ENUM_AFE_AFECON_ON
BITP/M_AFE_SEQCON_SEQWRTMR, SEQHALT, SEQHALTFIFOEMPTY, SEQEN
BITP/M_AFE_FIFOCON_DATAFIFOSRCSEL, DATAFIFOEN
BITP/M_AFE_SWCON_TxCON, SWSOURCESEL, TMUXCON, NMUXCON, PMUXCON, DMUXCON
BITP/M_AFE_HSDACCON_INAMPGNMDE, RATE, ATTENEN
BITP/M_AFE_WGCON_DACGAINCAL, DACOFFSETCAL, TYPESEL, TRAPRSTEN
BITP/M_AFE_ADCFILTERCON_AVRGNUM, SINC3OSR, SINC2OSR, AVRGEN, SINC3BYP, LPFBYPEN, ADCCLK
BITP/M_AFE_LPREFBUFCON_BOOSTCURRENT, LPBUF2P5DIS, LPREFDIS
BITP/M_AFE_CMDFIFOWRITE_CMDFIFOIN
BITP/M_AFE_CMDDATACON_DATAMEMMDE, DATA_MEM_SEL, CMDMEMMDE, CMD_MEM_SEL
ENUM_AFE_CMDDATACON_DFIFO, DSTM, DMEM32B, DMEM2K, DMEM4K, DMEM6K, CMEM, CFIFO, CSTM, CMEM32B, CMEM2K, CMEM4K, CMEM6K
BITP/M_AFE_DFTCON_DFTINSEL, DFTNUM, HANNINGEN
BITP/M_AFE_LPTIASWx_TIABIASSEL, PABIASSEL, TIASWCON
ENUM_AFE_LPTIASWx_CAPA_LP, NORM, DIO, SHORTSW, LOWNOISE, CAPA_RAMP_H, BUFDIS, BUFEN/2, TWOLEAD, SESHORTRE
BITP/M_AFE_LPTIACONx_CHOPEN, TIARF, TIARL, TIAGAIN, IBOOST, HALFPWR, PAPDEN, TIAPDEN
ENUM_AFE_LPTIACONx_DISCONRF, BYPRF, RF20K, RF100K, RF200K, RF400K, RF600K, RF1MOHM
ENUM_AFE_LPTIACONx_RL0, RL10, RL30, RL50, RL100, RL1P6K, RL3P1K, RL3P5K
ENUM_AFE_LPTIACONx_DISCONTIA, TIAGAIN200, 1K, 2K...512K
BITP/M_AFE_HSRTIACON_CTIACON, TIASW6CON, RTIACON
BITP/M_AFE_DExRESCON_DExRCON
BITP/M_AFE_HSTIACON_VBIASSEL
BITP/M_AFE_DACDCBUFCON_CHANSEL, CHAN0, CHAN1
BITP/M_AFE_LPMODECON_ALDOEN, V1P1HPADCEN, V1P8HPADCEN, PTATEN, ZTATEN, REPEATADCCNVEN_P, ADCCONVEN, HPREFDIS, HFOSCPD
BITP/M_AFE_LPDACSWx_LPMODEDIS, LPDACSW, DACCONBIT5, OVRRIDE
BITP/M_AFE_LPDACCONx_WAVETYPE, DACMDE, VZEROMUX, VBIASMUX, REFSEL, PWDEN, RSTEN
ENUM_AFE_LPDACCONx_MMR, WAVEGEN, NORM, DIAG, BITS6, BITS12, 12BIT, EN, ULPREF, AVDD, PWREN, PWRDIS, WRITEDIS, WRITEEN
BITP/M_AFE_DSWFULLCON_Dx (x=2-8), DR0
BITP/M_AFE_NSWFULLCON_NL2, NL, NR1, Nx (x=1-9)
BITP/M_AFE_PSWFULLCON_PL2, PL, Px (x=2-12), PR0
BITP/M_AFE_TSWFULLCON_TR1, Tx (x=1-11)
BITP/M_AFE_TEMPSENS_CHOPFRESEL, CHOPCON, ENABLE
BITP/M_AFE_BUFSENCON_V1P8THERMSTEN, V1P1LPADCCHGDIS, V1P1LPADCEN, V1P1HPADCEN, V1P8HPADCCHGDIS, V1P8LPADCEN, V1P8HPADCILIMITEN, V1P8HPADCEN
BITP/M_AFE_ADCCON_GNPGA, GNOFSELPGA, GNOFFSEL, MUXSELN, MUXSELP
BITP/M_AFE_STATSCON_STDDEV, SAMPLENUM, RESRVED, STATSEN
BITP/M_AFE_PMBW_SYSBW, SYSHP, BWNA, BW50, BW100, BW250, LP, HP
/* INTC Registers (0x3000) */
REG_INTC_INTCPOL, REG_INTC_INTCCLR, REG_INTC_INTCSEL0/1, REG_INTC_INTCFLAG0/1
/* SPI Commands */
SPICMD_SETADDR, SPICMD_READREG, SPICMD_WRITEREG, SPICMD_READFIFO
/* Interrupt Controller Constants */
AFEINTC_0, AFEINTC_1
AFEINTSRC_ADCRDY, DFTRDY, SINC2RDY, TEMPRDY, ADCMINERR, ADCMAXERR, ADCDIFFERR, MEANRDY, VARRDY
AFEINTSRC_CUSTOMINTx (x=0-3), BOOTLDDONE, WAKEUP, ENDSEQ, SEQTIMEOUT, SEQTIMEOUTERR
AFEINTSRC_CMDFIFOFULL, CMDFIFOEMPTY, CMDFIFOTHRESH, CMDFIFOOF, CMDFIFOUF
AFEINTSRC_DATAFIFOFULL, DATAFIFOEMPTY, DATAFIFOTHRESH, DATAFIFOOF, DATAFIFOUF
AFEINTSRC_WDTIRQ, CRC_OUTLIER, GPT0INT_SLPWUT, GPT1INT_TRYBRK, ALLINT
/* Power & Bandwidth */
AFEPWR_LP, AFEPWR_HP
AFEBW_AUTOSET, AFEBW_50KHZ, AFEBW_100KHZ, AFEBW_250KHZ
/* AFE Control Signals (Masks) */
AFECTRL_HPREFPWR, HSDACPWR, ADCPWR, ADCCNV, EXTBUFPWR, INAMPPWR, HSTIAPWR
AFECTRL_TEMPSPWR, TEMPCNV, WG, DFT, SINC2NOTCH, ALDOLIMIT, DACREFPWR, DCBUFPWR, ALL
/* Low Power Mode Control Signals */
LPMODECTRL_HFOSCEN, HPREFPWR, ADCCNV, REPEATEN, GLBBIASZ, GLBBIASP, BUFHP1P8V, BUFHP1P1V, ALDOPWR, ALL, NONE
/* Result Types */
AFERESULT_SINC3, SINC2, TEMPSENSOR, DFTREAL, DFTIMAGE, STATSMEAN, STATSVAR
/* Switch Matrix Definitions */
SWD_OPEN, RCAL0, AIN1, AIN2, AIN3, CE0, CE1, AFE1, SE0, SE1, AFE3
SWP_OPEN, RCAL0, AIN1, AIN2, AIN3, RE0, RE1, AFE2, SE0, DE0, SE1, AFE3, DE1, CE0, CE1, AFE1, PL, PL2
SWN_OPEN, RCAL1, AIN0, AIN1, AIN2, AIN3, SE0LOAD, DE0LOAD, SE1LOAD, AFE3LOAD, DE1LOAD, SE0, NL, NL2
SWT_OPEN, RCAL1, AIN0, AIN1, AIN2, AIN3, SE0LOAD, DE0, SE1LOAD, AFE3LOAD, DE1, TRTIA, DE0LOAD, DE1LOAD
/* Waveform Generator Types */
WGTYPE_MMR, WGTYPE_SIN, WGTYPE_TRAPZ
/* HSDAC Constants */
EXCITBUFGAIN_2, EXCITBUFGAIN_0P25
HSDACGAIN_1, HSDACGAIN_0P2
/* HSTIA Constants */
HSTIABIAS_1P1, VZERO0, VZERO1
HSTIARTIA_200, 1K, 5K, 10K, 20K, 40K, 80K, 160K, OPEN
HSTIADERTIA_50, 100, 200, 1K, 5K, 10K, 20K, 40K, 80K, 160K, TODE, OPEN
HSTIADERLOAD_0R, 10R, 30R, 50R, 100R, OPEN
HSTIAPWRMOE_LP, HP
/* LPDAC Constants */
LPDAC0, LPDAC1
LPDACSRC_MMR, WG
LPDACSW_VBIAS2LPPA, VBIAS2PIN, VZERO2LPTIA, VZERO2PIN, VZERO2HSTIA
LPDACVZERO_6BIT, 12BIT
LPDACVBIAS_6BIT, 12BIT
LPDACREF_2P5, AVDD
/* LPAMP/LPTIA Constants */
LPTIA0, LPTIA1
LPTIARF_OPEN, SHORT, 20K, 100K, 200K, 400K, 600K, 1M
LPTIARLOAD_SHORT, 10R, 30R, 50R, 100R, 1K6, 3K1, 3K6
LPTIARTIA_OPEN, 200R, 1K...512K
LPAMP0, LPAMP1
LPAMPPWR_NORM, BOOST1, BOOST2, BOOST3, HALF
LPTIASW(n)
/* ADC Constants */
ADCPGA_1, 1P5, 2, 4, 9
ADCMUXP_FLOAT, HSTIA_P, AIN0-6, AVDD_2, DVDD_2, AVDDREG, TEMPP/N, VSET1P1, VDE0, VSE0/1, VAFE1-4, VREF2P5, VREF1P8DAC, VZERO0/1, VBIAS0/1, VCE0/1, VRE0/1, VCE0_2, VCE1_2, LPTIA0_P, LPTIA1_P, AGND, P_NODE, IOVDD_2
ADCMUXN_FLOAT, HSTIA_N, LPTIA0_N, LPTIA1_N, AIN0-6, VSET1P1, VREF1P1, TEMPN, VZERO0/1, VBIAS0/1, AFE4, N_NODE
ADCRATE_800KHZ, 1P6MHZ
ADCSINC3OSR_2, 4, 5
ADCSINC2OSR_22, 44...1333
ADCAVGNUM_2, 4, 8, 16
/* DFT & Statistics Constants */
DFTSRC_SINC2NOTCH, SINC3, ADCRAW, AVG
DFTNUM_4, 8...16384
STATSAMPLE_128, 64, 32, 16, 8
STATDEV_1, 4, 9, 16, 25
/* Sequencer & FIFO Constants */
SEQID_0, 1, 2, 3
SEQMEMSIZE_32B, 2KB, 4KB, 6KB
SEQPINTRIGMODE_RISING, FALLING, BOTHEDGE, HIGHL, LOWL
SEQ_WAIT(ClkNum), SEQ_TOUT(ClkNum), SEQ_WR(addr,data)
SEQ_NOP(), SEQ_HALT(), SEQ_STOP(), SEQ_SLP(), SEQ_INTx(), SEQ_LEN(n)
FIFOMODE_FIFO, STREAM
FIFOSRC_SINC3, DFT, SINC2NOTCH, VAR, MEAN
FIFO_SEQID(data), ECC, CHANID
FIFOSIZE_32B, 2KB, 4KB, 6KB
WUPTENDSEQ_A - H
/* Misc Constants */
DATATYPE_ADCRAW, SINC3, SINC2, DFT, NOTCH
SLPKEY_LOCK, UNLOCK
HPOSCOUT_32MHZ, 16MHZ
AGPIO_Pin0 - Pin7
GPx_INT/TRIG/SYNC/GPIO/SLEEP/PORB/EXTCLK
LPMODECLK_HFOSC, LFOSC
SYSCLKSRC_HFOSC, XTAL, LFOSC, EXT
ADCCLKSRC_HFOSC, XTAL, EXT
ADCCLKDIV_1, 2
SYSCLKDIV_1, 2
PGACALTYPE_OFFSET, GAIN, OFFSETGAIN
AD5940ERR_OK, ERROR, PARA, NULLP, BUFF, ADDROR, SEQGEN, SEQREG, SEQLEN, WAKEUP, TIMEOUT, CALOR, APPERROR
MATH_PI, AD5940_ADIID, AD5940_CHIPID, M355_ADIID, M355_CHIPID, AD5940_SWRST
KEY_OSCCON, KEY_CALDATLOCK, KEY_LPMODEKEY

7
host/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
build/
build_android/
build_ios/
build_macos/
build_windows/
icons
*.png

123
host/CMakeLists.txt Normal file
View File

@ -0,0 +1,123 @@
# File: host/CMakeLists.txt
cmake_minimum_required(VERSION 3.18)
project(EISConfigurator VERSION 1.0 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# Added PrintSupport and OpenGLWidgets which are often needed by QCustomPlot
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets SerialPort PrintSupport OpenGLWidgets)
# --- QCustomPlot Integration ---
include(FetchContent)
FetchContent_Declare(
QCustomPlot
URL https://www.qcustomplot.com/release/2.1.1/QCustomPlot.tar.gz
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_GetProperties(QCustomPlot)
if(NOT qcustomplot_POPULATED)
FetchContent_Populate(QCustomPlot)
# QCustomPlot source distribution doesn't have a CMakeLists.txt
add_library(QCustomPlot
${qcustomplot_SOURCE_DIR}/qcustomplot.cpp
${qcustomplot_SOURCE_DIR}/qcustomplot.h
)
# Link required Qt modules to the library
target_link_libraries(QCustomPlot PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport)
target_include_directories(QCustomPlot PUBLIC ${qcustomplot_SOURCE_DIR})
endif()
# --- Icon Generation ---
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/assets/icon_source.png")
set(ICON_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_icons.sh")
set(MACOS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/assets/icons/app_icon.icns")
set(WINDOWS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/assets/icons/app_icon.ico")
# Find ImageMagick 'magick' executable
find_program(MAGICK_EXECUTABLE NAMES magick)
if(NOT MAGICK_EXECUTABLE)
message(WARNING "ImageMagick 'magick' not found. Icons will not be generated.")
endif()
if(EXISTS "${ICON_SOURCE}" AND MAGICK_EXECUTABLE)
execute_process(COMMAND chmod +x "${ICON_SCRIPT}")
add_custom_command(
OUTPUT "${MACOS_ICON}" "${WINDOWS_ICON}"
COMMAND "${ICON_SCRIPT}" "${MAGICK_EXECUTABLE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
DEPENDS "${ICON_SOURCE}" "${ICON_SCRIPT}"
COMMENT "Generating icons from source using ${MAGICK_EXECUTABLE}..."
VERBATIM
)
add_custom_target(GenerateIcons DEPENDS "${MACOS_ICON}" "${WINDOWS_ICON}")
endif()
# --- Sources ---
set(PROJECT_SOURCES
src/main.cpp
src/MainWindow.cpp
src/GraphWidget.cpp
)
if(EXISTS "${ICON_SOURCE}")
list(APPEND PROJECT_SOURCES ${MACOS_ICON} ${WINDOWS_ICON})
endif()
set(PROJECT_HEADERS
src/MainWindow.h
src/GraphWidget.h
)
if(ANDROID)
add_library(EISConfigurator SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
else()
add_executable(EISConfigurator MANUAL_FINALIZATION ${PROJECT_SOURCES} ${PROJECT_HEADERS})
endif()
if(EXISTS "${ICON_SOURCE}" AND MAGICK_EXECUTABLE)
add_dependencies(EISConfigurator GenerateIcons)
endif()
target_link_libraries(EISConfigurator PRIVATE
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::SerialPort
QCustomPlot
)
if(ANDROID)
set_target_properties(EISConfigurator PROPERTIES QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android)
endif()
if(APPLE)
set_target_properties(EISConfigurator PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME "EIS Configurator"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.eis.configurator"
MACOSX_BUNDLE_ICON_FILE "app_icon.icns"
RESOURCE "${MACOS_ICON}"
)
elseif(WIN32)
set_target_properties(EISConfigurator PROPERTIES
WIN32_EXECUTABLE TRUE
)
if(EXISTS "${WINDOWS_ICON}")
set_target_properties(EISConfigurator PROPERTIES
WIN32_ICON "${WINDOWS_ICON}"
)
endif()
endif()
if(NOT ANDROID)
qt_finalize_executable(EISConfigurator)
endif()

95
host/Makefile Normal file
View File

@ -0,0 +1,95 @@
# Makefile
QT_ANDROID_KIT ?= $(HOME)/Qt/6.8.3/android_arm64_v8a
QT_IOS_KIT ?= $(HOME)/Qt/6.8.3/ios
QT_MACOS_PATH ?= /opt/homebrew/opt/qt@6
QT_WIN_PATH ?= C:/Qt/6.8.3/msvc2019_64
BUILD_DIR_MACOS = build_macos
BUILD_DIR_WIN = build_windows
BUILD_DIR_ANDROID = build_android
BUILD_DIR_IOS = build_ios
TARGET = EISConfigurator
# Android Specifics
PKG_NAME = org.qtproject.example.EISConfigurator
# CRITICAL FIX: Qt6 generates 'android-build-debug.apk' by default
APK_PATH = $(BUILD_DIR_ANDROID)/android-build/build/outputs/apk/debug/android-build-debug.apk
all: macos
desktop: macos
macos:
@echo "Building for macOS (Apple Silicon)..."
@mkdir -p $(BUILD_DIR_MACOS)
@cd $(BUILD_DIR_MACOS) && cmake .. \
-DCMAKE_PREFIX_PATH="$(QT_MACOS_PATH);/opt/homebrew" \
-DCMAKE_BUILD_TYPE=Release
@$(MAKE) -C $(BUILD_DIR_MACOS)
@echo "Build Complete. Run with: open $(BUILD_DIR_MACOS)/$(TARGET).app"
windows:
@echo "Building for Windows..."
@mkdir -p $(BUILD_DIR_WIN)
@cd $(BUILD_DIR_WIN) && cmake .. \
-DCMAKE_PREFIX_PATH="$(QT_WIN_PATH)" \
-DCMAKE_BUILD_TYPE=Release
@cmake --build $(BUILD_DIR_WIN)
android:
@if [ ! -d "$(QT_ANDROID_KIT)" ]; then echo "Error: QT_ANDROID_KIT not found at $(QT_ANDROID_KIT)"; exit 1; fi
@mkdir -p $(BUILD_DIR_ANDROID)
@cd $(BUILD_DIR_ANDROID) && cmake .. \
-DCMAKE_TOOLCHAIN_FILE=$(QT_ANDROID_KIT)/lib/cmake/Qt6/qt.toolchain.cmake \
-DQT_ANDROID_ABIS="arm64-v8a" \
-DANDROID_PLATFORM=android-24 \
-DQT_ANDROID_BUILD_ALL_ABIS=OFF \
-DBUILD_ANDROID=ON \
-DCMAKE_BUILD_TYPE=Debug
@cmake --build $(BUILD_DIR_ANDROID) --target apk
@echo "APK generated at $(APK_PATH)"
ios:
@if [ ! -d "$(QT_IOS_KIT)" ]; then echo "Error: QT_IOS_KIT not found at $(QT_IOS_KIT)"; exit 1; fi
@mkdir -p $(BUILD_DIR_IOS)
@echo "Configuring iOS CMake..."
@cd $(BUILD_DIR_IOS) && cmake .. -G Xcode \
-DCMAKE_TOOLCHAIN_FILE=$(QT_IOS_KIT)/lib/cmake/Qt6/qt.toolchain.cmake \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_DEPLOYMENT_TARGET=16.0 \
-DBUILD_IOS=ON
@echo "iOS Project generated at $(BUILD_DIR_IOS)/$(TARGET).xcodeproj"
run:
@open $(BUILD_DIR_MACOS)/$(TARGET).app
# --- Android Deployment Wrappers ---
install_android: android
@echo "Installing $(APK_PATH) to device..."
@adb install -r $(APK_PATH)
run_android: install_android
@echo "Launching $(PKG_NAME)..."
@adb shell am start -n $(PKG_NAME)/org.qtproject.qt.android.bindings.QtActivity
debug_android: run_android
@echo "Attaching Logcat (Ctrl+C to exit)..."
@adb logcat -v color -s EISConfigurator Qt:* DEBUG
# --- Cleaning ---
clean:
@echo "Cleaning build artifacts..."
@if [ -f "$(BUILD_DIR_MACOS)/Makefile" ]; then cmake --build $(BUILD_DIR_MACOS) --target clean; fi
@if [ -f "$(BUILD_DIR_WIN)/Makefile" ] || [ -f "$(BUILD_DIR_WIN)/build.ninja" ]; then cmake --build $(BUILD_DIR_WIN) --target clean; fi
@if [ -f "$(BUILD_DIR_ANDROID)/Makefile" ] || [ -f "$(BUILD_DIR_ANDROID)/build.ninja" ]; then cmake --build $(BUILD_DIR_ANDROID) --target clean; fi
@if [ -d "$(BUILD_DIR_IOS)/$(TARGET).xcodeproj" ]; then cmake --build $(BUILD_DIR_IOS) --target clean; fi
distclean:
@rm -rf $(BUILD_DIR_MACOS) $(BUILD_DIR_WIN) $(BUILD_DIR_ANDROID) $(BUILD_DIR_IOS)
@echo "Removed all build directories."
.PHONY: all desktop macos windows android ios run install_android run_android debug_android clean distclean

View File

@ -0,0 +1,32 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.qtproject.example.YrCrystals"
android:versionName="1.0"
android:versionCode="1"
android:installLocation="auto">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application android:label="Yr Crystals"
android:name="org.qtproject.qt.android.bindings.QtApplication"
android:requestLegacyExternalStorage="true">
<activity android:name="org.qtproject.qt.android.bindings.QtActivity"
android:label="Yr Crystals"
android:screenOrientation="unspecified"
android:launchMode="singleTop"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="YrCrystals"/>
</activity>
</application>
</manifest>

78
host/ios/Info.plist Normal file
View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Yr Crystals</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIRequiresFullScreen</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<!-- Permissions -->
<key>NSMicrophoneUsageDescription</key>
<string>This app requires audio access to visualize music.</string>
<key>NSAppleMusicUsageDescription</key>
<string>This app requires access to your music library to play tracks.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library to load album art.</string>
<key>NSCameraUsageDescription</key>
<string>This app requires camera access for visualizer input.</string>
<!-- File Access -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- Document Types -->
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Audio</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.audio</string>
<string>public.mp3</string>
<string>public.mpeg-4-audio</string>
<string>public.folder</string>
<string>public.directory</string>
<string>public.content</string>
<string>public.data</string>
</array>
</dict>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict>
</plist>

135
host/scripts/generate_icons.sh Executable file
View File

@ -0,0 +1,135 @@
# scripts/generate_icons.sh
#!/bin/bash
# Argument 1: Path to magick executable
MAGICK_BIN="$1"
# Fallback if not provided
if [ -z "$MAGICK_BIN" ]; then
MAGICK_BIN="magick"
fi
# Assumes running from Project Root
SOURCE="assets/icon_source.png"
OUT_DIR="assets/icons"
if [ ! -f "$SOURCE" ]; then
echo "Error: Source image '$SOURCE' not found in $(pwd)"
exit 1
fi
# Verify magick works
"$MAGICK_BIN" -version >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: ImageMagick tool '$MAGICK_BIN' not found or not working."
exit 1
fi
mkdir -p "$OUT_DIR"
# macOS
ICONSET="$OUT_DIR/icon.iconset"
mkdir -p "$ICONSET"
"$MAGICK_BIN" "$SOURCE" -scale 16x16 "$ICONSET/icon_16x16.png"
"$MAGICK_BIN" "$SOURCE" -scale 32x32 "$ICONSET/icon_16x16@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 32x32 "$ICONSET/icon_32x32.png"
"$MAGICK_BIN" "$SOURCE" -scale 64x64 "$ICONSET/icon_32x32@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 128x128 "$ICONSET/icon_128x128.png"
"$MAGICK_BIN" "$SOURCE" -scale 256x256 "$ICONSET/icon_128x128@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 256x256 "$ICONSET/icon_256x256.png"
"$MAGICK_BIN" "$SOURCE" -scale 512x512 "$ICONSET/icon_256x256@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 512x512 "$ICONSET/icon_512x512.png"
"$MAGICK_BIN" "$SOURCE" -scale 1024x1024 "$ICONSET/icon_512x512@2x.png"
iconutil -c icns "$ICONSET" -o "$OUT_DIR/app_icon.icns"
rm -rf "$ICONSET"
# Windows
"$MAGICK_BIN" "$SOURCE" \
\( -clone 0 -scale 256x256 \) \
\( -clone 0 -scale 128x128 \) \
\( -clone 0 -scale 64x64 \) \
\( -clone 0 -scale 48x48 \) \
\( -clone 0 -scale 32x32 \) \
\( -clone 0 -scale 16x16 \) \
-delete 0 -alpha off -colors 256 "$OUT_DIR/app_icon.ico"
# Android
ANDROID_DIR="$OUT_DIR/android/res"
mkdir -p "$ANDROID_DIR/mipmap-mdpi"
"$MAGICK_BIN" "$SOURCE" -scale 48x48 "$ANDROID_DIR/mipmap-mdpi/ic_launcher.png"
mkdir -p "$ANDROID_DIR/mipmap-hdpi"
"$MAGICK_BIN" "$SOURCE" -scale 72x72 "$ANDROID_DIR/mipmap-hdpi/ic_launcher.png"
mkdir -p "$ANDROID_DIR/mipmap-xhdpi"
"$MAGICK_BIN" "$SOURCE" -scale 96x96 "$ANDROID_DIR/mipmap-xhdpi/ic_launcher.png"
mkdir -p "$ANDROID_DIR/mipmap-xxhdpi"
"$MAGICK_BIN" "$SOURCE" -scale 144x144 "$ANDROID_DIR/mipmap-xxhdpi/ic_launcher.png"
mkdir -p "$ANDROID_DIR/mipmap-xxxhdpi"
"$MAGICK_BIN" "$SOURCE" -scale 192x192 "$ANDROID_DIR/mipmap-xxxhdpi/ic_launcher.png"
# iOS
XCASSETS_DIR="$OUT_DIR/ios/Assets.xcassets"
IOS_DIR="$XCASSETS_DIR/AppIcon.appiconset"
mkdir -p "$IOS_DIR"
cat > "$XCASSETS_DIR/Contents.json" <<EOF
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
EOF
"$MAGICK_BIN" "$SOURCE" -scale 20x20 "$IOS_DIR/Icon-20.png"
"$MAGICK_BIN" "$SOURCE" -scale 40x40 "$IOS_DIR/Icon-20@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 60x60 "$IOS_DIR/Icon-20@3x.png"
"$MAGICK_BIN" "$SOURCE" -scale 29x29 "$IOS_DIR/Icon-29.png"
"$MAGICK_BIN" "$SOURCE" -scale 58x58 "$IOS_DIR/Icon-29@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 87x87 "$IOS_DIR/Icon-29@3x.png"
"$MAGICK_BIN" "$SOURCE" -scale 40x40 "$IOS_DIR/Icon-40.png"
"$MAGICK_BIN" "$SOURCE" -scale 80x80 "$IOS_DIR/Icon-40@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 120x120 "$IOS_DIR/Icon-40@3x.png"
"$MAGICK_BIN" "$SOURCE" -scale 120x120 "$IOS_DIR/Icon-60@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 180x180 "$IOS_DIR/Icon-60@3x.png"
"$MAGICK_BIN" "$SOURCE" -scale 76x76 "$IOS_DIR/Icon-76.png"
"$MAGICK_BIN" "$SOURCE" -scale 152x152 "$IOS_DIR/Icon-76@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 167x167 "$IOS_DIR/Icon-83.5@2x.png"
"$MAGICK_BIN" "$SOURCE" -scale 1024x1024 "$IOS_DIR/Icon-1024.png"
cat > "$IOS_DIR/Contents.json" <<EOF
{
"images" : [
{ "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-20@2x.png", "scale" : "2x" },
{ "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-20@3x.png", "scale" : "3x" },
{ "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-29.png", "scale" : "1x" },
{ "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-29@2x.png", "scale" : "2x" },
{ "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-29@3x.png", "scale" : "3x" },
{ "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-40@2x.png", "scale" : "2x" },
{ "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-40@3x.png", "scale" : "3x" },
{ "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-60@2x.png", "scale" : "2x" },
{ "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-60@3x.png", "scale" : "3x" },
{ "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-20.png", "scale" : "1x" },
{ "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-20@2x.png", "scale" : "2x" },
{ "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-29.png", "scale" : "1x" },
{ "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-29@2x.png", "scale" : "2x" },
{ "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-40.png", "scale" : "1x" },
{ "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-40@2x.png", "scale" : "2x" },
{ "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-76.png", "scale" : "1x" },
{ "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-76@2x.png", "scale" : "2x" },
{ "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "Icon-83.5@2x.png", "scale" : "2x" },
{ "size" : "1024x1024", "idiom" : "ios-marketing", "filename" : "Icon-1024.png", "scale" : "1x" }
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
EOF

89
host/src/GraphWidget.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "GraphWidget.h"
#include <QVBoxLayout>
GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
plot = new JKQTPlotter(this);
layout->addWidget(plot);
// Initialize Graphs
graphY1 = new JKQTPXYLineGraph(plot);
graphY2 = new JKQTPXYLineGraph(plot);
// Styling Y1 (e.g., Blue)
graphY1->setColor(QColor("blue"));
graphY1->setSymbolColor(QColor("blue"));
graphY1->setSymbol(JKQTPCircle);
graphY1->setSymbolSize(7);
graphY1->setLineWidth(2);
// Styling Y2 (e.g., Red)
graphY2->setColor(QColor("red"));
graphY2->setSymbolColor(QColor("red"));
graphY2->setSymbol(JKQTPTriangle);
graphY2->setSymbolSize(7);
graphY2->setLineWidth(2);
plot->addGraph(graphY1);
plot->addGraph(graphY2);
}
void GraphWidget::setupPlot(QString title, QString xLabel, QString y1Label, QString y2Label)
{
// Basic Settings
plot->getPlotter()->setPlotLabel(title);
plot->getXAxis()->setAxisLabel(xLabel);
// Y-Axis setup
plot->getYAxis()->setAxisLabel(y1Label); // Left Axis
// If we want a secondary axis for Y2, JKQTPlotter supports it,
// but for simplicity here we might plot on the same if scales are similar,
// or rely on user interpretation.
// For Bode (Ohm vs Degree), scales are vastly different.
// Ideally, Y2 should be on the right axis.
// Assign Y1 to Left Axis
graphY1->setTitle(y1Label);
// Assign Y2 to Right Axis (if supported by specific JKQT config, otherwise same axis)
// Assuming simple usage for now:
graphY2->setTitle(y2Label);
// Enable Legend
plot->getPlotter()->setShowKey(true);
}
void GraphWidget::setLogAxis(bool logX, bool logY)
{
plot->getXAxis()->setLogAxis(logX);
plot->getYAxis()->setLogAxis(logY);
}
void GraphWidget::addData(double x, double y1, double y2)
{
xData.append(x);
y1Data.append(y1);
y2Data.append(y2);
// Update graph data pointers
graphY1->setXColumn(xData);
graphY1->setYColumn(y1Data);
graphY2->setXColumn(xData);
graphY2->setYColumn(y2Data);
// Redraw
plot->redrawPlot();
}
void GraphWidget::clear()
{
xData.clear();
y1Data.clear();
y2Data.clear();
plot->redrawPlot();
}

39
host/src/GraphWidget.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef GRAPHWIDGET_H
#define GRAPHWIDGET_H
#include <QWidget>
#include <QVector>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/graphs/jkqtpigraph.h"
#include "jkqtplotter/graphs/jkqtpimage.h"
#include "jkqtplotter/graphs/jkqtpscatter.h"
#include "jkqtplotter/graphs/jkqtplyines.h"
class GraphWidget : public QWidget
{
Q_OBJECT
public:
explicit GraphWidget(QWidget *parent = nullptr);
// Configuration
void setupPlot(QString title, QString xLabel, QString y1Label, QString y2Label);
void setLogAxis(bool logX, bool logY);
// Data Access
void addData(double x, double y1, double y2);
void clear();
private:
JKQTPlotter *plot;
// Internal Data Storage
QVector<double> xData;
QVector<double> y1Data;
QVector<double> y2Data;
// Graph Objects
JKQTPXYLineGraph *graphY1;
JKQTPXYLineGraph *graphY2;
};
#endif // GRAPHWIDGET_H

228
host/src/MainWindow.cpp Normal file
View File

@ -0,0 +1,228 @@
#include "MainWindow.h"
#include <QSerialPortInfo>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QToolBar>
#include <QDebug>
#include <QMessageBox>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
serial = new QSerialPort(this);
connect(serial, &QSerialPort::readyRead, this, &MainWindow::onReadData);
setupUi();
}
MainWindow::~MainWindow()
{
if (serial->isOpen())
serial->close();
}
void MainWindow::setupUi()
{
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
// --- Toolbar Area ---
QHBoxLayout *toolLayout = new QHBoxLayout();
portSelector = new QComboBox();
const auto ports = QSerialPortInfo::availablePorts();
for (const QSerialPortInfo &info : ports) {
portSelector->addItem(info.portName());
}
btnConnect = new QPushButton("Connect");
connect(btnConnect, &QPushButton::clicked, [this]() {
onPortToggled(serial->isOpen());
});
btnMeasure = new QPushButton("Measure");
btnMeasure->setEnabled(false);
connect(btnMeasure, &QPushButton::clicked, this, &MainWindow::onMeasureClicked);
btnCalibrate = new QPushButton("Calibrate");
btnCalibrate->setEnabled(false);
connect(btnCalibrate, &QPushButton::clicked, this, &MainWindow::onCalibrateClicked);
viewSelector = new QComboBox();
viewSelector->addItem("Tabs (Swipe)");
viewSelector->addItem("Split (Dual)");
connect(viewSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onViewModeChanged);
toolLayout->addWidget(portSelector);
toolLayout->addWidget(btnConnect);
toolLayout->addWidget(btnMeasure);
toolLayout->addWidget(btnCalibrate);
toolLayout->addWidget(viewSelector); // Add view switcher
mainLayout->addLayout(toolLayout);
// --- Graphs Initialization ---
finalGraph = new GraphWidget();
finalGraph->setupPlot("Bode Plot", "Frequency (Hz)", "Impedance (Ohm)", "Phase (Rad)");
finalGraph->setLogAxis(true, true); // Log-Log for Bode
rawGraph = new GraphWidget();
rawGraph->setupPlot("Raw Data (DFT)", "Frequency (Hz)", "Real", "Imaginary");
rawGraph->setLogAxis(true, false); // Log X, Linear Y (Raw data can be neg)
// --- Layout Containers ---
// 1. Tab Widget (Mobile Style)
tabWidget = new QTabWidget();
tabWidget->addTab(finalGraph, "Final Data");
tabWidget->addTab(rawGraph, "Raw Data");
// 2. Splitter (Desktop Style)
splitter = new QSplitter(Qt::Horizontal);
// Note: We can't add the same widget to two parents.
// We will reparent them in onViewModeChanged.
// Default to Tabs (Mobile friendly default)
mainLayout->addWidget(tabWidget);
// Status Bar
statusLabel = new QLabel("Ready");
statusBar()->addWidget(statusLabel);
// Auto-detect Platform for default view
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
viewSelector->setCurrentIndex(0); // Tabs
viewSelector->setVisible(false); // Hide selector on mobile, force tabs
#else
viewSelector->setCurrentIndex(1); // Default to Split on Desktop
#endif
}
void MainWindow::onViewModeChanged(int index)
{
// 0 = Tabs, 1 = Split
// Remove graphs from their current parents
finalGraph->setParent(nullptr);
rawGraph->setParent(nullptr);
QVBoxLayout *layout = qobject_cast<QVBoxLayout*>(centralWidget->layout());
if (index == 0) { // Tabs
if (splitter->isVisible()) {
layout->removeWidget(splitter);
splitter->hide();
}
tabWidget->clear();
tabWidget->addTab(finalGraph, "Final Data");
tabWidget->addTab(rawGraph, "Raw Data");
if (!tabWidget->isVisible()) {
layout->addWidget(tabWidget);
tabWidget->show();
}
}
else { // Split
if (tabWidget->isVisible()) {
layout->removeWidget(tabWidget);
tabWidget->hide();
}
splitter->addWidget(finalGraph);
splitter->addWidget(rawGraph);
if (!splitter->isVisible()) {
layout->addWidget(splitter);
splitter->show();
}
}
}
void MainWindow::onPortToggled(bool connected)
{
if (connected) {
serial->close();
btnConnect->setText("Connect");
btnMeasure->setEnabled(false);
btnCalibrate->setEnabled(false);
statusLabel->setText("Disconnected");
} else {
serial->setPortName(portSelector->currentText());
serial->setBaudRate(115200); // Or whatever your Pico uses, standard 115200 usually
if (serial->open(QIODevice::ReadWrite)) {
btnConnect->setText("Disconnect");
btnMeasure->setEnabled(true);
btnCalibrate->setEnabled(true);
statusLabel->setText("Connected");
// Clear old data on connect
finalGraph->clear();
rawGraph->clear();
} else {
QMessageBox::critical(this, "Error", "Could not open serial port.");
}
}
}
void MainWindow::onMeasureClicked()
{
if (serial->isOpen()) {
finalGraph->clear();
rawGraph->clear();
// Trigger a sweep or single measurement
// Example command logic
// For single: serial->write("MEAS 10000\n");
// For sweep logic, the MCU might handle it or we loop here.
// Assuming user triggers 'MEAS 1000' style commands manually or logic exists.
// Let's just send a command if needed, or rely on user knowing what to do.
// For now, let's assume we want to measure 10kHz as a test
serial->write("MEAS 10000\n");
}
}
void MainWindow::onCalibrateClicked()
{
if (serial->isOpen()) {
serial->write("CAL\n");
}
}
void MainWindow::onReadData()
{
while (serial->canReadLine()) {
QByteArray line = serial->readLine().trimmed();
processLine(line);
}
}
void MainWindow::processLine(const QByteArray &line)
{
if (line.startsWith("DATA")) {
// Format: DATA,freq,impedance,phase,raw_real,raw_imag
float freq, imp, phase;
int raw_real, raw_imag;
// sscanf is dangerous with comma delimited strings if not careful, but works for fixed format
int count = sscanf(line.constData(), "DATA,%f,%f,%f,%d,%d", &freq, &imp, &phase, &raw_real, &raw_imag);
if (count >= 3) {
// We have at least the final data
finalGraph->addData(freq, imp, phase); // Y1=Imp, Y2=Phase
}
if (count >= 5) {
// We have raw data too
rawGraph->addData(freq, (double)raw_real, (double)raw_imag);
}
statusLabel->setText(QString("Received: %1 Hz").arg(freq));
}
else if (line.startsWith("LOG")) {
statusLabel->setText(line);
qDebug() << "Device Log:" << line;
}
}

53
host/src/MainWindow.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QTabWidget>
#include <QSplitter>
#include <QComboBox>
#include <QPushButton>
#include <QLabel>
#include "GraphWidget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onPortToggled(bool connected);
void onReadData();
void onMeasureClicked();
void onCalibrateClicked();
void onViewModeChanged(int index); // New slot for desktop view switching
private:
void setupUi();
void processLine(const QByteArray &line);
// Hardware
QSerialPort *serial;
// UI Elements
QComboBox *portSelector;
QPushButton *btnConnect;
QPushButton *btnMeasure;
QPushButton *btnCalibrate;
QLabel *statusLabel;
// Graphs
GraphWidget *finalGraph; // Bode Plot
GraphWidget *rawGraph; // Raw Real/Imag Plot
// Layout Containers
QWidget *centralWidget;
QTabWidget *tabWidget; // For Mobile/Tab Mode
QSplitter *splitter; // For Desktop/Split Mode
QComboBox *viewSelector; // For Desktop to toggle modes
};
#endif // MAINWINDOW_H

25
host/src/main.cpp Normal file
View File

@ -0,0 +1,25 @@
// File: aluf/src/main.cpp
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QApplication::setApplicationName("EIS Configurator");
QApplication::setApplicationVersion("1.0");
// Dark Theme
QPalette p = app.palette();
p.setColor(QPalette::Window, QColor(30, 30, 30));
p.setColor(QPalette::WindowText, Qt::white);
p.setColor(QPalette::Base, QColor(15, 15, 15));
p.setColor(QPalette::AlternateBase, QColor(45, 45, 45));
p.setColor(QPalette::Text, Qt::white);
p.setColor(QPalette::Button, QColor(45, 45, 45));
p.setColor(QPalette::ButtonText, Qt::white);
app.setPalette(p);
MainWindow w;
w.show();
return app.exec();
}

474
main.c Normal file
View File

@ -0,0 +1,474 @@
// 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;
}
}
}
}