nvm i broke it BAD (EIS is FUCKED - but the other modes work?!??!)
This commit is contained in:
parent
b1427c57c8
commit
5723e89a1a
|
|
@ -390,7 +390,105 @@ The AD594x includes two 16-bit general-purpose timers, AGPT0 and AGPT1. These ti
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0-15 | **MATCHVAL** | 0x0000FFFF | PWM Match Value |
|
| 0-15 | **MATCHVAL** | 0x0000FFFF | PWM Match Value |
|
||||||
For data integrity tasks, the device includes a dedicated hardware accelerator.
|
For data integrity tasks, the device includes a dedicated hardware accelerator.
|
||||||
### 8.0 CRC Accelerator (AFECRC)
|
|
||||||
|
## 8.0 Switch Matrix & Electrode Connections (SWMAT)
|
||||||
|
The AD5940 features a highly flexible switch matrix that routes external electrode signals (SE0, RE0, CE0, AINx) to internal blocks like the High-Speed DAC (Excitation), the ADC, and the TIA. The matrix is divided into four main blocks: **D** (DAC/Excitation), **P** (Positive Input), **N** (Negative Input), and **T** (TIA/Transimpedance).
|
||||||
|
**8.1 Core Switch Registers**
|
||||||
|
These registers provide direct control over every switch in the matrix.
|
||||||
|
**AFE_SWCON**
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **SWCON** | 0x0000200C | 0x0000FFFF | Master Switch Matrix Configuration |
|
||||||
|
| Bit Position(s) | Bitfield Name | Bitmask | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 12-15 | **TMUXCON** | 0x0000F000 | Control of T Switch MUX |
|
||||||
|
| 8-11 | **NMUXCON** | 0x00000F00 | Control of N Switch MUX |
|
||||||
|
| 4-7 | **PMUXCON** | 0x000000F0 | Control of P Switch MUX |
|
||||||
|
| 0-3 | **DMUXCON** | 0x0000000F | Control of D Switch MUX |
|
||||||
|
**AFE_DSWFULLCON (Excitation/DAC Path)**
|
||||||
|
Controls switches connecting pins to the Excitation Loop (High-Speed DAC output).
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **DSWFULLCON** | 0x00002150 | 0x00000000 | D-Block Switch Configuration |
|
||||||
|
| Bit Position(s) | Bitfield Name | Bitmask | Connection/Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 7 | **D8** | 0x00000080 | Connects **SE0** to D-Node |
|
||||||
|
| 6 | **D7** | 0x00000040 | Connects **CE0** to D-Node |
|
||||||
|
| 5 | **D6** | 0x00000020 | Connects **RE0** to D-Node |
|
||||||
|
| 4 | **D5** | 0x00000010 | Connects **AIN3** to D-Node |
|
||||||
|
| 3 | **D4** | 0x00000008 | Connects **AIN2** to D-Node |
|
||||||
|
| 2 | **D3** | 0x00000004 | Connects **AIN1** to D-Node |
|
||||||
|
| 1 | **D2** | 0x00000002 | Connects **AIN0** to D-Node |
|
||||||
|
| 0 | **DR0** | 0x00000001 | Connects **RCAL0** to D-Node |
|
||||||
|
**AFE_PSWFULLCON (Positive Input Path)**
|
||||||
|
Controls switches connecting pins to the Positive Input bus (ADC P-Input, etc.).
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **PSWFULLCON** | 0x00002154 | 0x00000000 | P-Block Switch Configuration |
|
||||||
|
| Bit Position(s) | Bitfield Name | Bitmask | Connection/Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 14 | **PL2** | 0x00004000 | Connects **CE0** to P-Node |
|
||||||
|
| 13 | **PL** | 0x00002000 | Connects **SE0** to P-Node |
|
||||||
|
| 11 | **P12** | 0x00000800 | Connects **DE0** to P-Node |
|
||||||
|
| 10 | **P11** | 0x00000400 | Connects **RE0** to P-Node |
|
||||||
|
| 4 | **P5** | 0x00000010 | Connects **AIN3** to P-Node |
|
||||||
|
| 3 | **P4** | 0x00000008 | Connects **AIN2** to P-Node |
|
||||||
|
| 2 | **P3** | 0x00000004 | Connects **AIN1** to P-Node |
|
||||||
|
| 1 | **P2** | 0x00000002 | Connects **AIN0** to P-Node |
|
||||||
|
| 0 | **PR0** | 0x00000001 | Connects **RCAL0** to P-Node |
|
||||||
|
**AFE_NSWFULLCON (Negative Input Path)**
|
||||||
|
Controls switches connecting pins to the Negative Input bus (ADC N-Input, TIA Input).
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **NSWFULLCON** | 0x00002158 | 0x00000000 | N-Block Switch Configuration |
|
||||||
|
| Bit Position(s) | Bitfield Name | Bitmask | Connection/Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 11 | **NL2** | 0x00000800 | Connects **CE0** to N-Node |
|
||||||
|
| 10 | **NL** | 0x00000400 | Connects **SE0** to N-Node |
|
||||||
|
| 8 | **N9** | 0x00000100 | Connects **DE0** to N-Node |
|
||||||
|
| 7 | **N8** | 0x00000080 | Connects **RE0** to N-Node |
|
||||||
|
| 4 | **N5** | 0x00000010 | Connects **AIN3** to N-Node |
|
||||||
|
| 3 | **N4** | 0x00000008 | Connects **AIN2** to N-Node |
|
||||||
|
| 2 | **N3** | 0x00000004 | Connects **AIN1** to N-Node |
|
||||||
|
| 1 | **N2** | 0x00000002 | Connects **AIN0** to N-Node |
|
||||||
|
| 0 | **NR1** | 0x00000200 | Connects **RCAL0** to N-Node |
|
||||||
|
**AFE_TSWFULLCON (TIA Feedback Path)**
|
||||||
|
Controls switches connecting pins primarily for TIA feedback loops.
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **TSWFULLCON** | 0x0000215C | 0x00000000 | T-Block Switch Configuration |
|
||||||
|
| Bit Position(s) | Bitfield Name | Bitmask | Connection/Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 10 | **T11** | 0x00000400 | **SE0** T-Switch (Often used for SE0-RE0 Short) |
|
||||||
|
| 9 | **T10** | 0x00000200 | **DE0** T-Switch |
|
||||||
|
| 8 | **T9** | 0x00000100 | **RE0** T-Switch |
|
||||||
|
| 6 | **T7** | 0x00000040 | **AIN3** T-Switch |
|
||||||
|
| 4 | **T5** | 0x00000010 | **AIN2** T-Switch |
|
||||||
|
| 3 | **T4** | 0x00000008 | **AIN1** T-Switch |
|
||||||
|
| 2 | **T3** | 0x00000004 | **AIN0** T-Switch |
|
||||||
|
| 1 | **T2** | 0x00000002 | **RCAL0** T-Switch |
|
||||||
|
**8.2 Shorting SE0 to RE0 (LPTIA Switch)**
|
||||||
|
While the TSWFULLCON register contains bit **T11** (associated with SE0), the specific functionality to short **SE0** to **RE0** is explicitly defined as a macro configuration in the Low Power TIA Switch register. This closes internal switch **SW11**.
|
||||||
|
**AFE_LPTIASW0**
|
||||||
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **LPTIASW0** | 0x000020E4 | 0x00000000 | LPTIA Channel 0 Switch Config |
|
||||||
|
**Key Configuration for SE0-RE0 Short:**
|
||||||
|
* **Bitmask**: 0x00000800
|
||||||
|
* **Enum Name**: ENUM_AFE_LPTIASWO_SESHORTRE
|
||||||
|
* **Description**: Closes **SW11**. This physically shorts the Sense Electrode 0 (SE0) to the Reference Electrode 0 (RE0). This is commonly used in 2-wire measurement modes or specific sensor biasing configurations.
|
||||||
|
|
||||||
|
⠀**8.3 Electrode Pin Capability Summary**
|
||||||
|
| **Pin Name** | **Can Source (DAC)?** | **Can Sink (TIA)?** | **Can Sense (ADC P/N)?** | **Relevant Switches** |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **CE0** | Yes (via D7) | Yes (via NL2) | Yes (via PL2/NL2) | D7, PL2, NL2 |
|
||||||
|
| **RE0** | Yes (via D6) | Yes (via N8) | Yes (via P11/N8) | D6, P11, N8, T9 |
|
||||||
|
| **SE0** | Yes (via D8) | Yes (via NL) | Yes (via PL/NL) | D8, PL, NL, T11 |
|
||||||
|
| **AIN0-3** | Yes (via D2-D5) | Yes (via N2-N5) | Yes (via P2-P5) | D2-D5, P2-P5, N2-N5 |
|
||||||
|
| **RCAL0** | Yes (via DR0) | Yes (via NR1) | Yes (via PR0) | DR0, PR0, NR1 |
|
||||||
|
|
||||||
|
|
||||||
|
### 9.0 CRC Accelerator (AFECRC)
|
||||||
The AFECRC module is a dedicated hardware accelerator for calculating Cyclic Redundancy Checks (CRC). This feature is essential for ensuring data integrity, allowing for fast and efficient error-checking of memory contents (like the sequencer command SRAM) or communication data without burdening the host processor.
|
The AFECRC module is a dedicated hardware accelerator for calculating Cyclic Redundancy Checks (CRC). This feature is essential for ensuring data integrity, allowing for fast and efficient error-checking of memory contents (like the sequencer command SRAM) or communication data without burdening the host processor.
|
||||||
**AFECRC_CTL**
|
**AFECRC_CTL**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|
|
@ -460,9 +558,9 @@ The AFECRC module is a dedicated hardware accelerator for calculating Cyclic Red
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0 | **CRC_ERR_ST** | 0x00000001 | CRC Error Interrupt Status Bit |
|
| 0 | **CRC_ERR_ST** | 0x00000001 | CRC Error Interrupt Status Bit |
|
||||||
The next section details the main Analog Front End, which integrates all measurement capabilities.
|
The next section details the main Analog Front End, which integrates all measurement capabilities.
|
||||||
### 9.0 Analog Front End Core (AFE)
|
### 10.0 Analog Front End Core (AFE)
|
||||||
The AFE block is the heart of the AD594x, integrating all the high-performance analog and digital components required for advanced electrochemical and bioimpedance measurements. This comprehensive module contains the high-speed and low-power Digital-to-Analog Converters (DACs) and Transimpedance Amplifiers (TIAs), the ADC and its associated digital filters, the powerful command sequencer with its data and command FIFOs, and the flexible switch matrix that routes signals between the analog blocks and external pins.
|
The AFE block is the heart of the AD594x, integrating all the high-performance analog and digital components required for advanced electrochemical and bioimpedance measurements. This comprehensive module contains the high-speed and low-power Digital-to-Analog Converters (DACs) and Transimpedance Amplifiers (TIAs), the ADC and its associated digital filters, the powerful command sequencer with its data and command FIFOs, and the flexible switch matrix that routes signals between the analog blocks and external pins.
|
||||||
**9.1 Core and Sequencer Configuration**
|
**10.1 Core and Sequencer Configuration**
|
||||||
**AFE_AFECON**
|
**AFE_AFECON**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -523,7 +621,7 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| Bit Position(s) | Bitfield Name | Bitmask | Description |
|
| Bit Position(s) | Bitfield Name | Bitmask | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0-29 | **TIMEOUT** | 0x3FFFFFFF | Current Value of the Sequencer Timeout Counter. |
|
| 0-29 | **TIMEOUT** | 0x3FFFFFFF | Current Value of the Sequencer Timeout Counter. |
|
||||||
**9.2 Waveform Generator**
|
**10.2 Waveform Generator**
|
||||||
**AFE_WGCON**
|
**AFE_WGCON**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -571,7 +669,7 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| **WGSLOPE1** | 0x00002024 | 0x00000000 | Waveform Generator - Trapezoid Slope 1 Time |
|
| **WGSLOPE1** | 0x00002024 | 0x00000000 | Waveform Generator - Trapezoid Slope 1 Time |
|
||||||
| **WGDELAY2** | 0x00002028 | 0x00000000 | Waveform Generator - Trapezoid Delay 2 Time |
|
| **WGDELAY2** | 0x00002028 | 0x00000000 | Waveform Generator - Trapezoid Delay 2 Time |
|
||||||
| **WGSLOPE2** | 0x0000202C | 0x00000000 | Waveform Generator - Trapezoid Slope 2 Time |
|
| **WGSLOPE2** | 0x0000202C | 0x00000000 | Waveform Generator - Trapezoid Slope 2 Time |
|
||||||
**9.3 ADC, Filters, and Data Path**
|
**10.3 ADC, Filters, and Data Path**
|
||||||
**AFE_ADCFILTERCON**
|
**AFE_ADCFILTERCON**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -613,7 +711,7 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| **TEMPSENSDAT** | 0x00002084 | 0x00000000 | Temperature Sensor Result |
|
| **TEMPSENSDAT** | 0x00002084 | 0x00000000 | Temperature Sensor Result |
|
||||||
| **DATAFIFORD** | 0x0000206C | 0x00000000 | Data FIFO Read |
|
| **DATAFIFORD** | 0x0000206C | 0x00000000 | Data FIFO Read |
|
||||||
| **CMDFIFOWRITE** | 0x00002070 | 0x00000000 | Command FIFO Write |
|
| **CMDFIFOWRITE** | 0x00002070 | 0x00000000 | Command FIFO Write |
|
||||||
**9.4 TIA and DAC Configuration**
|
**10.4 TIA and DAC Configuration**
|
||||||
**High-Speed DAC and TIA**
|
**High-Speed DAC and TIA**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -642,7 +740,7 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| 13-15 | **TIARF** | 0x0000E000 | Set LPF Resistor | 0x00000000 (DISCONRF): Disconnect<br>0x00002000 (BYPRF): Bypass<br>0x00004000 (RF20K): 20 kΩ<br>0x00006000 (RF100K): 100 kΩ<br>0x00008000 (RF200K): 200 kΩ<br>0x0000A000 (RF400K): 400 kΩ<br>0x0000C000 (RF600K): 600 kΩ<br>0x0000E000 (RF1MOHM): 1 MΩ |
|
| 13-15 | **TIARF** | 0x0000E000 | Set LPF Resistor | 0x00000000 (DISCONRF): Disconnect<br>0x00002000 (BYPRF): Bypass<br>0x00004000 (RF20K): 20 kΩ<br>0x00006000 (RF100K): 100 kΩ<br>0x00008000 (RF200K): 200 kΩ<br>0x0000A000 (RF400K): 400 kΩ<br>0x0000C000 (RF600K): 600 kΩ<br>0x0000E000 (RF1MOHM): 1 MΩ |
|
||||||
| 1 | **PAPDEN** | 0x00000002 | PA Power Down | |
|
| 1 | **PAPDEN** | 0x00000002 | PA Power Down | |
|
||||||
| 0 | **TIAPDEN** | 0x00000001 | TIA Power Down | |
|
| 0 | **TIAPDEN** | 0x00000001 | TIA Power Down | |
|
||||||
**9.5 Switch Matrix**
|
**10.5 Switch Matrix**
|
||||||
**AFE_SWCON**
|
**AFE_SWCON**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -665,7 +763,7 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| **PSWSTA** | 0x000021B4 | 0x00006000 | Switch Matrix Status (P) |
|
| **PSWSTA** | 0x000021B4 | 0x00006000 | Switch Matrix Status (P) |
|
||||||
| **NSWSTA** | 0x000021B8 | 0x00000C00 | Switch Matrix Status (N) |
|
| **NSWSTA** | 0x000021B8 | 0x00000C00 | Switch Matrix Status (N) |
|
||||||
| **TSWSTA** | 0x000021BC | 0x00000000 | Switch Matrix Status (T) |
|
| **TSWSTA** | 0x000021BC | 0x00000000 | Switch Matrix Status (T) |
|
||||||
**9.6 Power Management and Calibration**
|
**10.6 Power Management and Calibration**
|
||||||
| Register Name | Address | Reset Value | Description |
|
| Register Name | Address | Reset Value | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **PMBW** | 0x000022F0 | 0x00088800 | Power Mode Configuration |
|
| **PMBW** | 0x000022F0 | 0x00088800 | Power Mode Configuration |
|
||||||
|
|
@ -677,11 +775,11 @@ The AFE block is the heart of the AD594x, integrating all the high-performance a
|
||||||
| **DACOFFSET** | 0x00002268 | 0x00000000 | DAC Offset with Attenuator Disabled (LP Mode) |
|
| **DACOFFSET** | 0x00002268 | 0x00000000 | DAC Offset with Attenuator Disabled (LP Mode) |
|
||||||
| **DACGAIN** | 0x00002260 | 0x00000800 | DACGAIN |
|
| **DACGAIN** | 0x00002260 | 0x00000800 | DACGAIN |
|
||||||
This comprehensive set of registers enables fine-grained control over the AD594x's powerful analog capabilities. To illustrate how these are used in practice, the following section provides a functional example.
|
This comprehensive set of registers enables fine-grained control over the AD594x's powerful analog capabilities. To illustrate how these are used in practice, the following section provides a functional example.
|
||||||
### 10.0 Functional Example: Low Frequency Oscillator (LFOSC) Calibration
|
### 11.0 Functional Example: Low Frequency Oscillator (LFOSC) Calibration
|
||||||
This section provides a practical, high-level example of how the AD594x's features, particularly the sequencer and timers, are used to perform a critical task: calibrating the internal low-frequency oscillator (LFOSC).
|
This section provides a practical, high-level example of how the AD594x's features, particularly the sequencer and timers, are used to perform a critical task: calibrating the internal low-frequency oscillator (LFOSC).
|
||||||
**10.1 Overview**
|
**11.1 Overview**
|
||||||
The LFOSC is the clock source for the sleep/wakeup timer, which controls the measurement frequency of the AD5940 (i.e., how often it wakes up to run a measurement sequence). For applications that require a highly accurate and repeatable measurement sample rate, the LFOSC must be calibrated against a more precise, high-frequency clock source, such as an external 16 MHz crystal. The calibration process uses two measurement sequences to accurately measure the LFOSC period.
|
The LFOSC is the clock source for the sleep/wakeup timer, which controls the measurement frequency of the AD5940 (i.e., how often it wakes up to run a measurement sequence). For applications that require a highly accurate and repeatable measurement sample rate, the LFOSC must be calibrated against a more precise, high-frequency clock source, such as an external 16 MHz crystal. The calibration process uses two measurement sequences to accurately measure the LFOSC period.
|
||||||
**10.2 Calibration Steps**
|
**11.2 Calibration Steps**
|
||||||
The calibration process involves the following sequence of operations:
|
The calibration process involves the following sequence of operations:
|
||||||
**1** **Configure Sequence A:** A sequence is created containing a single command, SEQ_TOUT(0x3fffffff), which starts the high-resolution sequencer timeout counter. This sequence is written to a specific location in the AFE's command SRAM (e.g., Sequence ID 0).
|
**1** **Configure Sequence A:** A sequence is created containing a single command, SEQ_TOUT(0x3fffffff), which starts the high-resolution sequencer timeout counter. This sequence is written to a specific location in the AFE's command SRAM (e.g., Sequence ID 0).
|
||||||
**2** **Configure Sequence B:** A second sequence is created with a single SEQ_STOP() command. This command halts the sequencer and generates an END_SEQ interrupt, signaling the host microcontroller that a measurement period is complete. This sequence is written to another SRAM location (e.g., Sequence ID 1).
|
**2** **Configure Sequence B:** A second sequence is created with a single SEQ_STOP() command. This command halts the sequencer and generates an END_SEQ interrupt, signaling the host microcontroller that a measurement period is complete. This sequence is written to another SRAM location (e.g., Sequence ID 1).
|
||||||
|
|
@ -691,7 +789,7 @@ The calibration process involves the following sequence of operations:
|
||||||
**6** **Measure Read Latency:** Sequence B is reconfigured to reset the timeout counter before generating the END_SEQ interrupt. It is then run again. This time, the value read back by the host (TimerCount2) represents only the time it takes to read the register.
|
**6** **Measure Read Latency:** Sequence B is reconfigured to reset the timeout counter before generating the END_SEQ interrupt. It is then run again. This time, the value read back by the host (TimerCount2) represents only the time it takes to read the register.
|
||||||
**7** **Calculate Frequency:** The final LFOSC frequency is calculated using the difference between TimerCount and TimerCount2, along with the known system clock frequency.
|
**7** **Calculate Frequency:** The final LFOSC frequency is calculated using the difference between TimerCount and TimerCount2, along with the known system clock frequency.
|
||||||
|
|
||||||
⠀**10.3 Frequency Calculation**
|
⠀**11.3 Frequency Calculation**
|
||||||
***Editor's Note:*** *The formula presented in the original source documentation for this calculation appears to be erroneous, simplifying to a physically impossible result. The formula below has been corrected to reflect the logical intent of the calibration procedure.*
|
***Editor's Note:*** *The formula presented in the original source documentation for this calculation appears to be erroneous, simplifying to a physically impossible result. The formula below has been corrected to reflect the logical intent of the calibration procedure.*
|
||||||
The frequency of the low-frequency oscillator is calculated by determining how many system clock cycles occur during a known number of LFOSC cycles (CalDuration_in_ticks). The corrected formula is:
|
The frequency of the low-frequency oscillator is calculated by determining how many system clock cycles occur during a known number of LFOSC cycles (CalDuration_in_ticks). The corrected formula is:
|
||||||
### Frequency = (CalDuration_in_ticks * SystemClkFreq) / (TimerCount - TimerCount2)
|
### Frequency = (CalDuration_in_ticks * SystemClkFreq) / (TimerCount - TimerCount2)
|
||||||
|
|
@ -703,9 +801,11 @@ Where:
|
||||||
* **TimerCount2**: The value read from the **SEQTIMEOUT** register after the second (latency) measurement.
|
* **TimerCount2**: The value read from the **SEQTIMEOUT** register after the second (latency) measurement.
|
||||||
* **(TimerCount - TimerCount2)**: This difference represents the total number of system clock cycles that elapsed during the CalDuration period.
|
* **(TimerCount - TimerCount2)**: This difference represents the total number of system clock cycles that elapsed during the CalDuration period.
|
||||||
|
|
||||||
⠀**10.4 SDK Implementation Note**
|
⠀**11.4 SDK Implementation Note**
|
||||||
The AD5940 Software Development Kit (SDK) provides a ready-to-use function, AD5940_LFOSCMeasure(), which encapsulates this entire calibration logic. This function simplifies the process for the developer and takes key input parameters to control the measurement:
|
The AD5940 Software Development Kit (SDK) provides a ready-to-use function, AD5940_LFOSCMeasure(), which encapsulates this entire calibration logic. This function simplifies the process for the developer and takes key input parameters to control the measurement:
|
||||||
* **CalDuration**: Sets the length of the calibration routine (a value of 1000 ms is advisable for accuracy).
|
* **CalDuration**: Sets the length of the calibration routine (a value of 1000 ms is advisable for accuracy).
|
||||||
* **SystemClkFreq**: Sets the system clock frequency, which should be 16 MHz for best results.
|
* **SystemClkFreq**: Sets the system clock frequency, which should be 16 MHz for best results.
|
||||||
|
|
||||||
⠀By performing this calibration, applications can achieve a high level of accuracy in their measurement sampling frequency, which is critical for time-sensitive electrochemical analysis.
|
⠀By performing this calibration, applications can achieve a high level of accuracy in their measurement sampling frequency, which is critical for time-sensitive electrochemical analysis.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
// File: AD5940_Platform.c
|
||||||
|
#include "App_Common.h"
|
||||||
|
#include "hardware/spi.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
|
// --- Platform Interface Implementation ---
|
||||||
|
void AD5940_CsClr(void) { gpio_put(PIN_CS, 0); }
|
||||||
|
void AD5940_CsSet(void) { gpio_put(PIN_CS, 1); }
|
||||||
|
void AD5940_RstClr(void) { gpio_put(PIN_RST, 0); }
|
||||||
|
void AD5940_RstSet(void) { gpio_put(PIN_RST, 1); }
|
||||||
|
void AD5940_Delay10us(uint32_t time) { sleep_us(time * 10); }
|
||||||
|
|
||||||
|
void AD5940_ReadWriteNBytes(unsigned char *pSendBuffer, unsigned char *pRecvBuff, unsigned long length) {
|
||||||
|
spi_write_read_blocking(spi0, pSendBuffer, pRecvBuff, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AD5940_GetMCUIntFlag(void) {
|
||||||
|
return (gpio_get(PIN_INT) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AD5940_ClrMCUIntFlag(void) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AD5940_MCUResourceInit(void *pCfg) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AD5940_MCUGpioWrite(uint32_t data) { (void)data; }
|
||||||
|
uint32_t AD5940_MCUGpioRead(uint32_t pin) { (void)pin; return 0; }
|
||||||
|
void AD5940_MCUGpioCtrl(uint32_t pin, BoolFlag enable) { (void)pin; (void)enable; }
|
||||||
|
|
||||||
|
// --- Hardware Setup ---
|
||||||
|
void setup_pins(void) {
|
||||||
|
spi_init(spi0, 16000000);
|
||||||
|
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_RST);
|
||||||
|
gpio_set_dir(PIN_RST, GPIO_OUT);
|
||||||
|
gpio_put(PIN_RST, 1);
|
||||||
|
|
||||||
|
gpio_init(PIN_INT);
|
||||||
|
gpio_set_dir(PIN_INT, GPIO_IN);
|
||||||
|
gpio_pull_up(PIN_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t AD5940PlatformCfg(void) {
|
||||||
|
CLKCfg_Type clk_cfg;
|
||||||
|
FIFOCfg_Type fifo_cfg;
|
||||||
|
AGPIOCfg_Type gpio_cfg;
|
||||||
|
|
||||||
|
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
|
||||||
|
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
|
||||||
|
clk_cfg.SysClkDiv = SYSCLKDIV_1;
|
||||||
|
clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
|
||||||
|
clk_cfg.HfOSC32MHzMode = bFALSE;
|
||||||
|
clk_cfg.HFOSCEn = bTRUE;
|
||||||
|
clk_cfg.HFXTALEn = bFALSE;
|
||||||
|
clk_cfg.LFOSCEn = bTRUE;
|
||||||
|
AD5940_CLKCfg(&clk_cfg);
|
||||||
|
|
||||||
|
fifo_cfg.FIFOEn = bFALSE;
|
||||||
|
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
||||||
|
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
||||||
|
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
||||||
|
fifo_cfg.FIFOThresh = 6;
|
||||||
|
AD5940_FIFOCfg(&fifo_cfg);
|
||||||
|
fifo_cfg.FIFOEn = bTRUE;
|
||||||
|
AD5940_FIFOCfg(&fifo_cfg);
|
||||||
|
|
||||||
|
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);
|
||||||
|
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
||||||
|
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH|AFEINTSRC_CUSTOMINT0, bTRUE);
|
||||||
|
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
||||||
|
|
||||||
|
gpio_cfg.FuncSet = GP0_INT;
|
||||||
|
gpio_cfg.InputEnSet = 0;
|
||||||
|
gpio_cfg.OutputEnSet = AGPIO_Pin0;
|
||||||
|
gpio_cfg.OutVal = 0;
|
||||||
|
gpio_cfg.PullEnSet = 0;
|
||||||
|
AD5940_AGPIOCfg(&gpio_cfg);
|
||||||
|
|
||||||
|
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemReset(void) {
|
||||||
|
sleep_ms(100);
|
||||||
|
AD5940_SoftRst();
|
||||||
|
sleep_ms(1);
|
||||||
|
AD5940_Initialize();
|
||||||
|
AD5940PlatformCfg();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helpers ---
|
||||||
|
uint32_t GetHSTIARtia(uint32_t val) {
|
||||||
|
switch(val) {
|
||||||
|
case 200: return HSTIARTIA_200;
|
||||||
|
case 1000: return HSTIARTIA_1K;
|
||||||
|
case 5000: return HSTIARTIA_5K;
|
||||||
|
case 10000: return HSTIARTIA_10K;
|
||||||
|
case 20000: return HSTIARTIA_20K;
|
||||||
|
case 40000: return HSTIARTIA_40K;
|
||||||
|
case 80000: return HSTIARTIA_80K;
|
||||||
|
case 160000: return HSTIARTIA_160K;
|
||||||
|
default: return HSTIARTIA_1K;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetLPTIARtia(uint32_t val) {
|
||||||
|
switch(val) {
|
||||||
|
case 200: return LPTIARTIA_200R;
|
||||||
|
case 1000: return LPTIARTIA_1K;
|
||||||
|
case 2000: return LPTIARTIA_2K;
|
||||||
|
case 3000: return LPTIARTIA_3K;
|
||||||
|
case 4000: return LPTIARTIA_4K;
|
||||||
|
case 6000: return LPTIARTIA_6K;
|
||||||
|
case 8000: return LPTIARTIA_8K;
|
||||||
|
case 10000: return LPTIARTIA_10K;
|
||||||
|
case 12000: return LPTIARTIA_12K;
|
||||||
|
case 16000: return LPTIARTIA_16K;
|
||||||
|
case 20000: return LPTIARTIA_20K;
|
||||||
|
case 24000: return LPTIARTIA_24K;
|
||||||
|
case 30000: return LPTIARTIA_30K;
|
||||||
|
case 32000: return LPTIARTIA_32K;
|
||||||
|
case 40000: return LPTIARTIA_40K;
|
||||||
|
case 48000: return LPTIARTIA_48K;
|
||||||
|
case 64000: return LPTIARTIA_64K;
|
||||||
|
case 85000: return LPTIARTIA_85K;
|
||||||
|
case 96000: return LPTIARTIA_96K;
|
||||||
|
case 100000: return LPTIARTIA_100K;
|
||||||
|
case 120000: return LPTIARTIA_120K;
|
||||||
|
case 128000: return LPTIARTIA_128K;
|
||||||
|
case 160000: return LPTIARTIA_160K;
|
||||||
|
case 196000: return LPTIARTIA_196K;
|
||||||
|
case 256000: return LPTIARTIA_256K;
|
||||||
|
case 512000: return LPTIARTIA_512K;
|
||||||
|
default: return LPTIARTIA_1K;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetLPTIARload(uint32_t val) {
|
||||||
|
if (val <= 200) return LPTIARLOAD_10R;
|
||||||
|
return LPTIARLOAD_100R;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount) {
|
||||||
|
float freq;
|
||||||
|
fImpPol_Type *pImp = (fImpPol_Type*)pData;
|
||||||
|
AppIMPCtrl(IMPCTRL_GETFREQ, &freq);
|
||||||
|
|
||||||
|
for(int i=0;i<DataCount;i++) {
|
||||||
|
float mag = pImp[i].Magnitude;
|
||||||
|
float phase = pImp[i].Phase;
|
||||||
|
float real = mag * cosf(phase);
|
||||||
|
float imag = mag * sinf(phase);
|
||||||
|
printf("DATA,%.2f,%.4f,%.4f,%.4f,%.4f\n", freq, mag, phase * 180.0f / MATH_PI, real, imag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmperometricShowResult(float *pData, uint32_t DataCount) {
|
||||||
|
for(int i=0;i<DataCount;i++) {
|
||||||
|
printf("AMP,%d,%.4f\n", g_AmpIndex++, pData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RampShowResult(float *pData, uint32_t DataCount) {
|
||||||
|
for(int i=0;i<DataCount;i++) {
|
||||||
|
printf("RAMP,%d,%.4f\n", g_RampIndex++, pData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,4 @@
|
||||||
// Amperometric.c
|
// File: Amperometric.c
|
||||||
/*!
|
|
||||||
*****************************************************************************
|
|
||||||
@file: Amperometric.c
|
|
||||||
@author: $Author: mlambe $
|
|
||||||
@brief: Amperometric measurement.
|
|
||||||
@version: $Revision: 766 $
|
|
||||||
@date: $Date: 2018-03-21 14:09:35 +0100 (Wed, 21 Mar 2018) $
|
|
||||||
-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
This software is proprietary to Analog Devices, Inc. and its licensors.
|
|
||||||
By using this software you agree to the terms of the associated
|
|
||||||
Analog Devices Software License Agreement.
|
|
||||||
|
|
||||||
*****************************************************************************/
|
|
||||||
#include "Amperometric.h"
|
#include "Amperometric.h"
|
||||||
|
|
||||||
#define AD5940ERR_STOP 10
|
#define AD5940ERR_STOP 10
|
||||||
|
|
@ -63,6 +47,7 @@ AppAMPCfg_Type AppAMPCfg =
|
||||||
.ADCSinc2Osr = ADCSINC2OSR_22,
|
.ADCSinc2Osr = ADCSINC2OSR_22,
|
||||||
.DataFifoSrc = FIFOSRC_SINC2NOTCH,
|
.DataFifoSrc = FIFOSRC_SINC2NOTCH,
|
||||||
.ADCRefVolt = 1.8162, /* Measure voltage on ADCRefVolt pin and enter here*/
|
.ADCRefVolt = 1.8162, /* Measure voltage on ADCRefVolt pin and enter here*/
|
||||||
|
.ShortRe0Se0 = bFALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -192,6 +177,12 @@ static AD5940Err AppAMPSeqCfgGen(void)
|
||||||
lp_loop.LpAmpCfg.LpTiaRtia = AppAMPCfg.LptiaRtiaSel;
|
lp_loop.LpAmpCfg.LpTiaRtia = AppAMPCfg.LptiaRtiaSel;
|
||||||
lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
|
lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply Short Option
|
||||||
|
if(AppAMPCfg.ShortRe0Se0) {
|
||||||
|
lp_loop.LpAmpCfg.LpTiaSW |= LPTIASW(11);
|
||||||
|
}
|
||||||
|
|
||||||
AD5940_LPLoopCfgS(&lp_loop);
|
AD5940_LPLoopCfgS(&lp_loop);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,4 @@
|
||||||
// File: Amperometric.h
|
// File: Amperometric.h
|
||||||
/*!
|
|
||||||
*****************************************************************************
|
|
||||||
@file: Amperometric.h
|
|
||||||
@author: $Author: mlambe $
|
|
||||||
@brief: Amperometric measurement header file.
|
|
||||||
@version: $Revision: 766 $
|
|
||||||
@date: $Date: 2018-03-21 14:09:35 +0100 (Wed, 21 Mar 2018) $
|
|
||||||
-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
This software is proprietary to Analog Devices, Inc. and its licensors.
|
|
||||||
By using this software you agree to the terms of the associated
|
|
||||||
Analog Devices Software License Agreement.
|
|
||||||
|
|
||||||
*****************************************************************************/
|
|
||||||
#ifndef _AMPEROMETRIC_H_
|
#ifndef _AMPEROMETRIC_H_
|
||||||
#define _AMPEROMETRIC_H_
|
#define _AMPEROMETRIC_H_
|
||||||
#include "ad5940.h"
|
#include "ad5940.h"
|
||||||
|
|
@ -24,10 +8,6 @@ Analog Devices Software License Agreement.
|
||||||
|
|
||||||
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
|
#define DAC12BITVOLT_1LSB (2200.0f/4095) //mV
|
||||||
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
|
#define DAC6BITVOLT_1LSB (DAC12BITVOLT_1LSB*64) //mV
|
||||||
/*
|
|
||||||
Note: this example will use SEQID_0 as measurement sequence, and use SEQID_1 as init sequence.
|
|
||||||
SEQID_3 is used for calibration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -66,6 +46,7 @@ typedef struct
|
||||||
SEQInfo_Type MeasureSeqInfo;
|
SEQInfo_Type MeasureSeqInfo;
|
||||||
BoolFlag StopRequired; /* After FIFO is ready, stop the measurement sequence */
|
BoolFlag StopRequired; /* After FIFO is ready, stop the measurement sequence */
|
||||||
uint32_t FifoDataCount; /* Count how many times impedance have been measured */
|
uint32_t FifoDataCount; /* Count how many times impedance have been measured */
|
||||||
|
BoolFlag ShortRe0Se0; /* Short RE0 to SE0 */
|
||||||
/* End */
|
/* End */
|
||||||
}AppAMPCfg_Type;
|
}AppAMPCfg_Type;
|
||||||
|
|
||||||
|
|
@ -78,8 +59,6 @@ typedef struct
|
||||||
float Voltage;
|
float Voltage;
|
||||||
}fAmpRes_Type;
|
}fAmpRes_Type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define AMPCTRL_START 0
|
#define AMPCTRL_START 0
|
||||||
#define AMPCTRL_STOPNOW 1
|
#define AMPCTRL_STOPNOW 1
|
||||||
#define AMPCTRL_STOPSYNC 2
|
#define AMPCTRL_STOPSYNC 2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
// File: App_Common.h
|
||||||
|
#ifndef _APP_COMMON_H_
|
||||||
|
#define _APP_COMMON_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "ad5940.h"
|
||||||
|
#include "Impedance.h"
|
||||||
|
#include "Amperometric.h"
|
||||||
|
#include "RampTest.h"
|
||||||
|
|
||||||
|
// --- Hardware Definitions ---
|
||||||
|
#define PIN_MISO 0
|
||||||
|
#define PIN_CS 1
|
||||||
|
#define PIN_SCK 2
|
||||||
|
#define PIN_MOSI 3
|
||||||
|
#define PIN_RST 9
|
||||||
|
#define PIN_INT 29
|
||||||
|
|
||||||
|
#define APPBUFF_SIZE 512
|
||||||
|
#define AD5940ERR_STOP 10
|
||||||
|
|
||||||
|
#ifndef LPTIARF_BYPASS
|
||||||
|
#define LPTIARF_BYPASS 0x2000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// --- Application State Enums ---
|
||||||
|
typedef enum {
|
||||||
|
MODE_IDLE,
|
||||||
|
MODE_IMPEDANCE,
|
||||||
|
MODE_AMPEROMETRIC,
|
||||||
|
MODE_RAMP
|
||||||
|
} AppMode;
|
||||||
|
|
||||||
|
// --- Global Variables ---
|
||||||
|
extern uint32_t AppBuff[APPBUFF_SIZE];
|
||||||
|
extern AppMode CurrentMode;
|
||||||
|
extern float LFOSCFreq;
|
||||||
|
extern uint32_t g_AmpIndex;
|
||||||
|
extern uint32_t g_RampIndex;
|
||||||
|
|
||||||
|
// Configuration Globals
|
||||||
|
extern uint32_t ConfigLptiaVal;
|
||||||
|
extern uint32_t ConfigHstiaVal;
|
||||||
|
extern uint32_t CurrentLpTiaRf;
|
||||||
|
extern uint32_t ConfigRLoad;
|
||||||
|
extern float CalibratedLptiaVal;
|
||||||
|
extern float CalibratedHstiaVal;
|
||||||
|
extern BoolFlag GlobalShortRe0Se0;
|
||||||
|
|
||||||
|
// --- Function Prototypes ---
|
||||||
|
|
||||||
|
// From AD5940_Platform.c
|
||||||
|
void setup_pins(void);
|
||||||
|
int32_t AD5940PlatformCfg(void);
|
||||||
|
void SystemReset(void);
|
||||||
|
uint32_t GetHSTIARtia(uint32_t val);
|
||||||
|
uint32_t GetLPTIARtia(uint32_t val);
|
||||||
|
uint32_t GetLPTIARload(uint32_t val);
|
||||||
|
void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount);
|
||||||
|
void AmperometricShowResult(float *pData, uint32_t DataCount);
|
||||||
|
void RampShowResult(float *pData, uint32_t DataCount);
|
||||||
|
|
||||||
|
// From Measurement_Core.c
|
||||||
|
void AD5940ImpedanceStructInit(void);
|
||||||
|
void AD5940AMPStructInit(void);
|
||||||
|
void AD5940RampStructInit(void);
|
||||||
|
void Config_LPLOOP(float bias_mv);
|
||||||
|
void Calibrate_HSDAC(float freq);
|
||||||
|
void Configure_Filters(float freq);
|
||||||
|
void Do_WaveGen(float freq);
|
||||||
|
void AD5941_InitAll(void);
|
||||||
|
|
||||||
|
// From Measurement_Routines.c
|
||||||
|
void Routine_CalibrateLFO(void);
|
||||||
|
void Routine_Measure(float freq);
|
||||||
|
void Routine_Sweep(float start, float end, int steps);
|
||||||
|
void Routine_Amperometric(float bias_mv);
|
||||||
|
void Routine_LSV(float start_mv, float end_mv, int steps, int duration_ms);
|
||||||
|
void Routine_CalibrateSystem(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -18,6 +18,9 @@ add_executable(EIS
|
||||||
Amperometric.c
|
Amperometric.c
|
||||||
RampTest.c
|
RampTest.c
|
||||||
Reset.c
|
Reset.c
|
||||||
|
Measurement_Core.c
|
||||||
|
Measurement_Routines.c
|
||||||
|
AD5940_Platform.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(EIS PRIVATE CHIPSEL_594X)
|
target_compile_definitions(EIS PRIVATE CHIPSEL_594X)
|
||||||
|
|
|
||||||
36
Impedance.c
36
Impedance.c
|
|
@ -43,7 +43,7 @@ AppIMPCfg_Type AppIMPCfg =
|
||||||
.ExtRtia = 0,
|
.ExtRtia = 0,
|
||||||
.ExcitBufGain = EXCITBUFGAIN_0P25,
|
.ExcitBufGain = EXCITBUFGAIN_0P25,
|
||||||
.HsDacGain = HSDACGAIN_0P2,
|
.HsDacGain = HSDACGAIN_0P2,
|
||||||
.HsDacUpdateRate = 0, /* UPDATED: 0 = Max Speed (16MHz) for cleaner high-freq signals */
|
.HsDacUpdateRate = 7,
|
||||||
.DacVoltPP = 600.0, /* Excitation Amplitude (mV peak-to-peak) */
|
.DacVoltPP = 600.0, /* Excitation Amplitude (mV peak-to-peak) */
|
||||||
.BiasVolt = -0.0f, /* DC Bias Voltage */
|
.BiasVolt = -0.0f, /* DC Bias Voltage */
|
||||||
|
|
||||||
|
|
@ -71,6 +71,7 @@ AppIMPCfg_Type AppIMPCfg =
|
||||||
.FifoThresh = 6, /* Threshold: 3 measurements * 2 (Real/Imag) = 6 words */
|
.FifoThresh = 6, /* Threshold: 3 measurements * 2 (Real/Imag) = 6 words */
|
||||||
.IMPInited = bFALSE,
|
.IMPInited = bFALSE,
|
||||||
.StopRequired = bFALSE,
|
.StopRequired = bFALSE,
|
||||||
|
.ShortRe0Se0 = bFALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
@ -316,6 +317,16 @@ static AD5940Err AppIMPSeqCfgGen(void)
|
||||||
HsLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
|
HsLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
|
||||||
AD5940_HSLoopCfgS(&HsLoopCfg);
|
AD5940_HSLoopCfgS(&HsLoopCfg);
|
||||||
|
|
||||||
|
// Handle RE0-SE0 Short (SW11 in LPTIASW0)
|
||||||
|
if(AppIMPCfg.ShortRe0Se0 == bTRUE)
|
||||||
|
{
|
||||||
|
AD5940_SEQGenInsert(SEQ_WR(REG_AFE_LPTIASW0, 0x00000800)); // Close SW11
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AD5940_SEQGenInsert(SEQ_WR(REG_AFE_LPTIASW0, 0x00000000)); // Open all LPTIA switches
|
||||||
|
}
|
||||||
|
|
||||||
// Configure DSP and ADC
|
// Configure DSP and ADC
|
||||||
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
|
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
|
||||||
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
|
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
|
||||||
|
|
@ -499,23 +510,8 @@ AD5940Err AppIMPCheckFreq(float freq)
|
||||||
filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr;
|
filter_cfg.ADCSinc2Osr = freq_params.ADCSinc2Osr;
|
||||||
filter_cfg.ADCSinc3Osr = freq_params.ADCSinc3Osr;
|
filter_cfg.ADCSinc3Osr = freq_params.ADCSinc3Osr;
|
||||||
filter_cfg.BpSinc3 = bFALSE;
|
filter_cfg.BpSinc3 = bFALSE;
|
||||||
|
filter_cfg.BpNotch = bTRUE;
|
||||||
// --- CRITICAL FIX: Disable Notch Filter for High Frequencies ---
|
filter_cfg.Sinc2NotchEnable = bTRUE;
|
||||||
// The Notch filter (50/60Hz) has a long settling time (~60ms).
|
|
||||||
// If enabled at high frequencies (e.g., 100kHz), the sequencer waits only microseconds,
|
|
||||||
// capturing the filter's settling artifacts (noise/hook).
|
|
||||||
if(freq > 10.0f)
|
|
||||||
{
|
|
||||||
filter_cfg.BpNotch = bFALSE;
|
|
||||||
filter_cfg.Sinc2NotchEnable = bFALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter_cfg.BpNotch = bTRUE;
|
|
||||||
filter_cfg.Sinc2NotchEnable = bTRUE;
|
|
||||||
}
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
|
|
||||||
filter_cfg.ADCRate = ADCRATE_800KHZ; // Fixed ADC Rate for stability
|
filter_cfg.ADCRate = ADCRATE_800KHZ; // Fixed ADC Rate for stability
|
||||||
|
|
||||||
dft_cfg.DftNum = freq_params.DftNum;
|
dft_cfg.DftNum = freq_params.DftNum;
|
||||||
|
|
@ -535,8 +531,8 @@ AD5940Err AppIMPCheckFreq(float freq)
|
||||||
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
|
clks_cal.RatioSys2AdcClk = AppIMPCfg.SysClkFreq/AppIMPCfg.AdcClkFreq;
|
||||||
AD5940_ClksCalculate(&clks_cal, &WaitClks);
|
AD5940_ClksCalculate(&clks_cal, &WaitClks);
|
||||||
|
|
||||||
// Add safety margin (Increased from 200 to 2000 to ensure full flush)
|
// Add safety margin
|
||||||
WaitClks += 2000;
|
WaitClks += 200;
|
||||||
|
|
||||||
// Update Wait Times in SRAM for all 3 measurements
|
// Update Wait Times in SRAM for all 3 measurements
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ typedef struct
|
||||||
SEQInfo_Type MeasureSeqInfo;
|
SEQInfo_Type MeasureSeqInfo;
|
||||||
BoolFlag StopRequired; /* After FIFO is ready, stop the measurement sequence */
|
BoolFlag StopRequired; /* After FIFO is ready, stop the measurement sequence */
|
||||||
uint32_t FifoDataCount; /* Count how many times impedance have been measured */
|
uint32_t FifoDataCount; /* Count how many times impedance have been measured */
|
||||||
|
BoolFlag ShortRe0Se0; /* Short RE0 to SE0 */
|
||||||
}AppIMPCfg_Type;
|
}AppIMPCfg_Type;
|
||||||
|
|
||||||
#define IMPCTRL_START 0
|
#define IMPCTRL_START 0
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
// File: Measurement_Core.c
|
||||||
|
#include "App_Common.h"
|
||||||
|
|
||||||
|
void AD5941_InitAll(void) {
|
||||||
|
AD5940_HWReset();
|
||||||
|
AD5940_MCUResourceInit(0);
|
||||||
|
AD5940_Initialize();
|
||||||
|
// CRITICAL: Re-enable Platform Interrupts/GPIOs after HW Reset
|
||||||
|
AD5940PlatformCfg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AD5940ImpedanceStructInit(void) {
|
||||||
|
AppIMPCfg_Type *pImpedanceCfg;
|
||||||
|
AppIMPGetCfg(&pImpedanceCfg);
|
||||||
|
|
||||||
|
pImpedanceCfg->IMPInited = bFALSE;
|
||||||
|
pImpedanceCfg->SeqStartAddr = 0;
|
||||||
|
pImpedanceCfg->MaxSeqLen = 512;
|
||||||
|
pImpedanceCfg->RcalVal = 100.0;
|
||||||
|
pImpedanceCfg->RtiaVal = CalibratedHstiaVal;
|
||||||
|
pImpedanceCfg->SinFreq = 1000.0;
|
||||||
|
pImpedanceCfg->FifoThresh = 6;
|
||||||
|
pImpedanceCfg->DacVoltPP = 600.0;
|
||||||
|
pImpedanceCfg->ExcitBufGain = EXCITBUFGAIN_0P25;
|
||||||
|
pImpedanceCfg->HsDacGain = HSDACGAIN_0P2;
|
||||||
|
pImpedanceCfg->HsDacUpdateRate = 7;
|
||||||
|
|
||||||
|
// --- Switch Matrix Configuration ---
|
||||||
|
// D-Switch: Connect DAC to CE0 (Force +)
|
||||||
|
pImpedanceCfg->DswitchSel = SWD_CE0;
|
||||||
|
|
||||||
|
// P-Switch: Connect CE0 to P-Bus (Optional, but standard)
|
||||||
|
pImpedanceCfg->PswitchSel = SWP_CE0;
|
||||||
|
|
||||||
|
// N-Switch: Connect SE0 to N-Bus (Force - / Current Input)
|
||||||
|
// This is the CRITICAL path for HSTIA current measurement.
|
||||||
|
pImpedanceCfg->NswitchSel = SWN_SE0;
|
||||||
|
|
||||||
|
// T-Switch: Only connect the Feedback Resistor (TRTIA).
|
||||||
|
// DO NOT connect SWT_SE0 (T11), as it may short SE0 to RE0/LPTIA nodes.
|
||||||
|
pImpedanceCfg->TswitchSel = 0; // Impedance.c adds SWT_TRTIA automatically
|
||||||
|
|
||||||
|
pImpedanceCfg->HstiaRtiaSel = GetHSTIARtia(ConfigHstiaVal);
|
||||||
|
pImpedanceCfg->BiasVolt = 0.0;
|
||||||
|
pImpedanceCfg->SweepCfg.SweepEn = bFALSE;
|
||||||
|
pImpedanceCfg->SweepCfg.SweepStart = 100.0f;
|
||||||
|
pImpedanceCfg->SweepCfg.SweepStop = 100000.0f;
|
||||||
|
pImpedanceCfg->SweepCfg.SweepPoints = 50;
|
||||||
|
pImpedanceCfg->SweepCfg.SweepLog = bTRUE;
|
||||||
|
pImpedanceCfg->PwrMod = AFEPWR_LP;
|
||||||
|
pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
pImpedanceCfg->DftNum = DFTNUM_16384;
|
||||||
|
pImpedanceCfg->DftSrc = DFTSRC_SINC3;
|
||||||
|
pImpedanceCfg->ShortRe0Se0 = GlobalShortRe0Se0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AD5940AMPStructInit(void) {
|
||||||
|
AppAMPCfg_Type *pAMPCfg;
|
||||||
|
AppAMPGetCfg(&pAMPCfg);
|
||||||
|
|
||||||
|
pAMPCfg->AMPInited = bFALSE;
|
||||||
|
pAMPCfg->WuptClkFreq = LFOSCFreq;
|
||||||
|
pAMPCfg->SeqStartAddr = 0;
|
||||||
|
pAMPCfg->MaxSeqLen = 512;
|
||||||
|
pAMPCfg->RcalVal = 100.0;
|
||||||
|
pAMPCfg->NumOfData = -1;
|
||||||
|
pAMPCfg->AmpODR = 1.0;
|
||||||
|
pAMPCfg->FifoThresh = 4;
|
||||||
|
pAMPCfg->SensorBias = 0;
|
||||||
|
pAMPCfg->LptiaRtiaSel = GetLPTIARtia(ConfigLptiaVal);
|
||||||
|
pAMPCfg->LpTiaRl = ConfigRLoad;
|
||||||
|
pAMPCfg->LpTiaRf = CurrentLpTiaRf;
|
||||||
|
pAMPCfg->Vzero = 1100;
|
||||||
|
pAMPCfg->ADCRefVolt = 1.82;
|
||||||
|
pAMPCfg->RtiaCalValue.Magnitude = CalibratedLptiaVal;
|
||||||
|
pAMPCfg->ShortRe0Se0 = GlobalShortRe0Se0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AD5940RampStructInit(void) {
|
||||||
|
AppRAMPCfg_Type *pRampCfg;
|
||||||
|
AppRAMPGetCfg(&pRampCfg);
|
||||||
|
|
||||||
|
pRampCfg->RAMPInited = bFALSE;
|
||||||
|
pRampCfg->SeqStartAddr = 0;
|
||||||
|
pRampCfg->MaxSeqLen = 1024;
|
||||||
|
pRampCfg->RcalVal = 100.0;
|
||||||
|
pRampCfg->ADCRefVolt = 1820.0f;
|
||||||
|
pRampCfg->FifoThresh = 4;
|
||||||
|
pRampCfg->SysClkFreq = 16000000.0f;
|
||||||
|
pRampCfg->LFOSCClkFreq = LFOSCFreq;
|
||||||
|
|
||||||
|
pRampCfg->RampStartVolt = -500.0f;
|
||||||
|
pRampCfg->RampPeakVolt = +500.0f;
|
||||||
|
pRampCfg->VzeroStart = 1100.0f;
|
||||||
|
pRampCfg->VzeroPeak = 1100.0f;
|
||||||
|
pRampCfg->StepNumber = 100;
|
||||||
|
pRampCfg->RampDuration = 10000;
|
||||||
|
pRampCfg->SampleDelay = 1.0f;
|
||||||
|
|
||||||
|
pRampCfg->LPTIARtiaSel = GetLPTIARtia(ConfigLptiaVal);
|
||||||
|
pRampCfg->LPTIARloadSel = ConfigRLoad;
|
||||||
|
pRampCfg->LpTiaRf = CurrentLpTiaRf;
|
||||||
|
pRampCfg->AdcPgaGain = ADCPGA_1P5;
|
||||||
|
pRampCfg->RtiaValue.Magnitude = CalibratedLptiaVal;
|
||||||
|
pRampCfg->ShortRe0Se0 = GlobalShortRe0Se0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config_LPLOOP(float bias_mv) {
|
||||||
|
uint32_t vzero_code = 32;
|
||||||
|
float vzero_volts = vzero_code * (2200.0f / 64.0f);
|
||||||
|
float vbias_volts = vzero_volts + bias_mv;
|
||||||
|
uint32_t vbias_code = (uint32_t)(vbias_volts / (2200.0f / 4095.0f));
|
||||||
|
if(vbias_code > 4095) vbias_code = 4095;
|
||||||
|
|
||||||
|
LPDACCfg_Type lpdac_cfg;
|
||||||
|
lpdac_cfg.LpdacSel = LPDAC0;
|
||||||
|
lpdac_cfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
|
||||||
|
lpdac_cfg.LpDacVzeroMux = LPDACVZERO_6BIT;
|
||||||
|
lpdac_cfg.DacData6Bit = vzero_code;
|
||||||
|
lpdac_cfg.DacData12Bit = vbias_code;
|
||||||
|
lpdac_cfg.DataRst = bFALSE;
|
||||||
|
lpdac_cfg.LpDacSW = LPDACSW_VBIAS2LPPA | LPDACSW_VBIAS2PIN | LPDACSW_VZERO2LPTIA | LPDACSW_VZERO2PIN;
|
||||||
|
lpdac_cfg.LpDacRef = LPDACREF_2P5;
|
||||||
|
lpdac_cfg.LpDacSrc = LPDACSRC_MMR;
|
||||||
|
lpdac_cfg.PowerEn = bTRUE;
|
||||||
|
AD5940_LPDACCfgS(&lpdac_cfg);
|
||||||
|
|
||||||
|
LPLoopCfg_Type lp_loop;
|
||||||
|
lp_loop.LpAmpCfg.LpAmpSel = LPAMP0;
|
||||||
|
lp_loop.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
|
||||||
|
lp_loop.LpAmpCfg.LpPaPwrEn = bTRUE;
|
||||||
|
lp_loop.LpAmpCfg.LpTiaPwrEn = bTRUE;
|
||||||
|
lp_loop.LpAmpCfg.LpTiaRf = CurrentLpTiaRf;
|
||||||
|
lp_loop.LpAmpCfg.LpTiaRload = ConfigRLoad;
|
||||||
|
lp_loop.LpAmpCfg.LpTiaRtia = GetLPTIARtia(ConfigLptiaVal);
|
||||||
|
|
||||||
|
// Base Switches
|
||||||
|
lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
|
||||||
|
|
||||||
|
// Apply Short Option
|
||||||
|
if(GlobalShortRe0Se0) {
|
||||||
|
lp_loop.LpAmpCfg.LpTiaSW |= LPTIASW(11);
|
||||||
|
}
|
||||||
|
|
||||||
|
AD5940_LPLoopCfgS(&lp_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Calibrate_HSDAC(float freq) {
|
||||||
|
HSDACCal_Type hsdac_cal;
|
||||||
|
ADCPGACal_Type adcpga_cal;
|
||||||
|
CLKCfg_Type clk_cfg;
|
||||||
|
|
||||||
|
// Configure Clock based on frequency (matches Do_WaveGen logic)
|
||||||
|
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
|
||||||
|
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
|
||||||
|
clk_cfg.SysClkDiv = SYSCLKDIV_1;
|
||||||
|
clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
|
||||||
|
clk_cfg.HFXTALEn = bFALSE;
|
||||||
|
clk_cfg.LFOSCEn = bTRUE;
|
||||||
|
|
||||||
|
if(freq > 80000) {
|
||||||
|
clk_cfg.HfOSC32MHzMode = bTRUE;
|
||||||
|
clk_cfg.HFOSCEn = bTRUE;
|
||||||
|
AD5940_CLKCfg(&clk_cfg);
|
||||||
|
AD5940_HPModeEn(bTRUE);
|
||||||
|
} else {
|
||||||
|
clk_cfg.HfOSC32MHzMode = bFALSE;
|
||||||
|
clk_cfg.HFOSCEn = bTRUE;
|
||||||
|
AD5940_CLKCfg(&clk_cfg);
|
||||||
|
AD5940_HPModeEn(bFALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
|
||||||
|
|
||||||
|
adcpga_cal.AdcClkFreq = 16000000;
|
||||||
|
adcpga_cal.ADCPga = ADCPGA_1P5;
|
||||||
|
adcpga_cal.ADCSinc2Osr = ADCSINC2OSR_1333;
|
||||||
|
adcpga_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
adcpga_cal.PGACalType = PGACALTYPE_OFFSET;
|
||||||
|
adcpga_cal.TimeOut10us = 1000;
|
||||||
|
adcpga_cal.VRef1p11 = 1.11;
|
||||||
|
adcpga_cal.VRef1p82 = 1.82;
|
||||||
|
AD5940_ADCPGACal(&adcpga_cal);
|
||||||
|
|
||||||
|
hsdac_cal.ExcitBufGain = EXCITBUFGAIN_2;
|
||||||
|
hsdac_cal.HsDacGain = HSDACGAIN_1;
|
||||||
|
hsdac_cal.AfePwrMode = AFEPWR_LP;
|
||||||
|
hsdac_cal.ADCSinc2Osr = ADCSINC2OSR_1333;
|
||||||
|
hsdac_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
AD5940_HSDACCal(&hsdac_cal);
|
||||||
|
|
||||||
|
hsdac_cal.ExcitBufGain = EXCITBUFGAIN_2;
|
||||||
|
hsdac_cal.HsDacGain = HSDACGAIN_0P2;
|
||||||
|
AD5940_HSDACCal(&hsdac_cal);
|
||||||
|
|
||||||
|
hsdac_cal.ExcitBufGain = EXCITBUFGAIN_0P25;
|
||||||
|
hsdac_cal.HsDacGain = HSDACGAIN_1;
|
||||||
|
AD5940_HSDACCal(&hsdac_cal);
|
||||||
|
|
||||||
|
hsdac_cal.ExcitBufGain = EXCITBUFGAIN_0P25;
|
||||||
|
hsdac_cal.HsDacGain = HSDACGAIN_0P2;
|
||||||
|
AD5940_HSDACCal(&hsdac_cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Configure_Filters(float freq) {
|
||||||
|
ADCFilterCfg_Type adc_filter;
|
||||||
|
DFTCfg_Type dft_cfg;
|
||||||
|
FIFOCfg_Type fifo_cfg;
|
||||||
|
|
||||||
|
fifo_cfg.FIFOEn = bFALSE;
|
||||||
|
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
||||||
|
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
||||||
|
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
||||||
|
fifo_cfg.FIFOThresh = 2;
|
||||||
|
AD5940_FIFOCfg(&fifo_cfg);
|
||||||
|
fifo_cfg.FIFOEn = bTRUE;
|
||||||
|
AD5940_FIFOCfg(&fifo_cfg);
|
||||||
|
|
||||||
|
AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
|
||||||
|
AD5940_StructInit(&adc_filter, sizeof(adc_filter));
|
||||||
|
AD5940_StructInit(&dft_cfg, sizeof(dft_cfg));
|
||||||
|
|
||||||
|
if (freq < 0.51f) {
|
||||||
|
adc_filter.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
adc_filter.ADCSinc2Osr = ADCSINC2OSR_267;
|
||||||
|
adc_filter.ADCSinc3Osr = ADCSINC3OSR_5;
|
||||||
|
adc_filter.BpNotch = bTRUE;
|
||||||
|
adc_filter.BpSinc3 = bFALSE;
|
||||||
|
adc_filter.Sinc2NotchEnable = bTRUE;
|
||||||
|
adc_filter.ADCRate = ADCRATE_800KHZ;
|
||||||
|
dft_cfg.DftNum = DFTNUM_8192;
|
||||||
|
dft_cfg.DftSrc = DFTSRC_SINC2NOTCH;
|
||||||
|
} else if(freq < 5.0f) {
|
||||||
|
adc_filter.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
adc_filter.ADCSinc2Osr = ADCSINC2OSR_178;
|
||||||
|
adc_filter.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
adc_filter.BpNotch = bTRUE;
|
||||||
|
adc_filter.BpSinc3 = bFALSE;
|
||||||
|
adc_filter.Sinc2NotchEnable = bTRUE;
|
||||||
|
adc_filter.ADCRate = ADCRATE_800KHZ;
|
||||||
|
dft_cfg.DftNum = DFTNUM_8192;
|
||||||
|
dft_cfg.DftSrc = DFTSRC_SINC2NOTCH;
|
||||||
|
} else if(freq < 450.0f) {
|
||||||
|
adc_filter.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
adc_filter.ADCSinc2Osr = ADCSINC2OSR_44;
|
||||||
|
adc_filter.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
adc_filter.BpNotch = bTRUE;
|
||||||
|
adc_filter.BpSinc3 = bFALSE;
|
||||||
|
adc_filter.Sinc2NotchEnable = bTRUE;
|
||||||
|
adc_filter.ADCRate = ADCRATE_800KHZ;
|
||||||
|
dft_cfg.DftNum = DFTNUM_4096;
|
||||||
|
dft_cfg.DftSrc = DFTSRC_SINC2NOTCH;
|
||||||
|
} else if(freq < 80000.0f) {
|
||||||
|
adc_filter.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
adc_filter.ADCSinc2Osr = ADCSINC2OSR_178;
|
||||||
|
adc_filter.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
adc_filter.BpNotch = bTRUE;
|
||||||
|
adc_filter.BpSinc3 = bFALSE;
|
||||||
|
adc_filter.Sinc2NotchEnable = bFALSE;
|
||||||
|
adc_filter.ADCRate = ADCRATE_800KHZ;
|
||||||
|
dft_cfg.DftNum = DFTNUM_16384;
|
||||||
|
dft_cfg.DftSrc = DFTSRC_SINC3;
|
||||||
|
} else {
|
||||||
|
adc_filter.ADCAvgNum = ADCAVGNUM_16;
|
||||||
|
adc_filter.ADCSinc2Osr = ADCSINC2OSR_178;
|
||||||
|
adc_filter.ADCSinc3Osr = ADCSINC3OSR_2;
|
||||||
|
adc_filter.BpNotch = bTRUE;
|
||||||
|
adc_filter.BpSinc3 = bFALSE;
|
||||||
|
adc_filter.Sinc2NotchEnable = bFALSE;
|
||||||
|
adc_filter.ADCRate = ADCRATE_1P6MHZ;
|
||||||
|
dft_cfg.DftNum = DFTNUM_16384;
|
||||||
|
dft_cfg.DftSrc = DFTSRC_SINC3;
|
||||||
|
}
|
||||||
|
dft_cfg.HanWinEn = bTRUE;
|
||||||
|
|
||||||
|
AD5940_ADCFilterCfgS(&adc_filter);
|
||||||
|
AD5940_DFTCfgS(&dft_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Do_WaveGen(float freq) {
|
||||||
|
CLKCfg_Type clk_cfg;
|
||||||
|
AFERefCfg_Type aferef_cfg;
|
||||||
|
HSLoopCfg_Type HpLoopCfg;
|
||||||
|
ADCBaseCfg_Type adc_base;
|
||||||
|
|
||||||
|
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
|
||||||
|
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
|
||||||
|
clk_cfg.SysClkDiv = SYSCLKDIV_1;
|
||||||
|
clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
|
||||||
|
clk_cfg.HFXTALEn = bFALSE;
|
||||||
|
clk_cfg.LFOSCEn = bTRUE;
|
||||||
|
|
||||||
|
if(freq > 80000) {
|
||||||
|
clk_cfg.HfOSC32MHzMode = bTRUE;
|
||||||
|
clk_cfg.HFOSCEn = bTRUE;
|
||||||
|
AD5940_CLKCfg(&clk_cfg);
|
||||||
|
AD5940_HPModeEn(bTRUE);
|
||||||
|
} else {
|
||||||
|
clk_cfg.HfOSC32MHzMode = bFALSE;
|
||||||
|
clk_cfg.HFOSCEn = bTRUE;
|
||||||
|
AD5940_CLKCfg(&clk_cfg);
|
||||||
|
AD5940_HPModeEn(bFALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
aferef_cfg.HpBandgapEn = bTRUE;
|
||||||
|
aferef_cfg.Hp1V1BuffEn = bTRUE;
|
||||||
|
aferef_cfg.Hp1V8BuffEn = bTRUE;
|
||||||
|
aferef_cfg.Disc1V1Cap = bFALSE;
|
||||||
|
aferef_cfg.Disc1V8Cap = bFALSE;
|
||||||
|
aferef_cfg.Hp1V8ThemBuff = bFALSE;
|
||||||
|
aferef_cfg.Hp1V8Ilimit = bFALSE;
|
||||||
|
aferef_cfg.Lp1V1BuffEn = bFALSE;
|
||||||
|
aferef_cfg.Lp1V8BuffEn = bFALSE;
|
||||||
|
aferef_cfg.LpBandgapEn = bTRUE;
|
||||||
|
aferef_cfg.LpRefBufEn = bTRUE;
|
||||||
|
aferef_cfg.LpRefBoostEn = bFALSE;
|
||||||
|
AD5940_REFCfgS(&aferef_cfg);
|
||||||
|
|
||||||
|
HpLoopCfg.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2;
|
||||||
|
HpLoopCfg.HsDacCfg.HsDacGain = HSDACGAIN_1;
|
||||||
|
HpLoopCfg.HsDacCfg.HsDacUpdateRate = (freq > 80000) ? 0x07 : 0x1B;
|
||||||
|
|
||||||
|
HpLoopCfg.HsTiaCfg.DiodeClose = bFALSE;
|
||||||
|
HpLoopCfg.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
|
||||||
|
HpLoopCfg.HsTiaCfg.HstiaCtia = 16;
|
||||||
|
HpLoopCfg.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
|
||||||
|
HpLoopCfg.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_TODE;
|
||||||
|
HpLoopCfg.HsTiaCfg.HstiaRtiaSel = GetHSTIARtia(ConfigHstiaVal);
|
||||||
|
|
||||||
|
adc_base.ADCPga = ADCPGA_1P5;
|
||||||
|
AD5940_ADCBaseCfgS(&adc_base);
|
||||||
|
|
||||||
|
// --- Switch Matrix Configuration (Manual Sweep) ---
|
||||||
|
HpLoopCfg.SWMatCfg.Dswitch = SWD_CE0;
|
||||||
|
HpLoopCfg.SWMatCfg.Pswitch = SWP_CE0;
|
||||||
|
HpLoopCfg.SWMatCfg.Nswitch = SWN_SE0;
|
||||||
|
// FIX: Only use TRTIA (Feedback). Do NOT use SWT_SE0.
|
||||||
|
HpLoopCfg.SWMatCfg.Tswitch = SWT_TRTIA;
|
||||||
|
|
||||||
|
AD5940_AFECtrlS(AFECTRL_WG, bFALSE);
|
||||||
|
|
||||||
|
HpLoopCfg.WgCfg.WgType = WGTYPE_SIN;
|
||||||
|
HpLoopCfg.WgCfg.GainCalEn = bFALSE;
|
||||||
|
HpLoopCfg.WgCfg.OffsetCalEn = bFALSE;
|
||||||
|
HpLoopCfg.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(freq, (freq > 80000) ? 32000000.0 : 16000000.0);
|
||||||
|
|
||||||
|
// Reduced Amplitude: 50mV peak (100mV pp) to prevent saturation
|
||||||
|
// Range is +/- 607mV with Gain 2/1.
|
||||||
|
// 50 / 607 * 2047 = ~168
|
||||||
|
HpLoopCfg.WgCfg.SinCfg.SinAmplitudeWord = (uint32_t)(50.0f/607.0f*2047 + 0.5f);
|
||||||
|
HpLoopCfg.WgCfg.SinCfg.SinOffsetWord = 0;
|
||||||
|
HpLoopCfg.WgCfg.SinCfg.SinPhaseWord = 0;
|
||||||
|
AD5940_HSLoopCfgS(&HpLoopCfg);
|
||||||
|
|
||||||
|
AD5940_AFECtrlS(AFECTRL_DACREFPWR, bTRUE);
|
||||||
|
AD5940_AFECtrlS(AFECTRL_EXTBUFPWR | AFECTRL_INAMPPWR | AFECTRL_HSTIAPWR | AFECTRL_HSDACPWR, bTRUE);
|
||||||
|
AD5940_AFECtrlS(AFECTRL_WG, bTRUE);
|
||||||
|
AD5940_AFECtrlS(AFECTRL_DCBUFPWR, bTRUE);
|
||||||
|
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
// File: Measurement_Routines.c
|
||||||
|
#include "App_Common.h"
|
||||||
|
|
||||||
|
void Routine_CalibrateLFO(void) {
|
||||||
|
printf(">> Calibrating LFOSC...\n");
|
||||||
|
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
|
||||||
|
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
|
|
||||||
|
LFOSCMeasure_Type cal_cfg;
|
||||||
|
cal_cfg.CalDuration = 1000.0;
|
||||||
|
cal_cfg.CalSeqAddr = 0;
|
||||||
|
cal_cfg.SystemClkFreq = 16000000.0;
|
||||||
|
|
||||||
|
if(AD5940_LFOSCMeasure(&cal_cfg, &LFOSCFreq) == AD5940ERR_OK) {
|
||||||
|
printf(">> LFOSC Calibrated: %.2f Hz\n", LFOSCFreq);
|
||||||
|
} else {
|
||||||
|
printf(">> LFOSC Calibration Failed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Routine_Measure(float freq) {
|
||||||
|
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
|
CurrentMode = MODE_IMPEDANCE;
|
||||||
|
|
||||||
|
AppIMPCfg_Type *pCfg;
|
||||||
|
AppIMPGetCfg(&pCfg);
|
||||||
|
AppIMPCleanup();
|
||||||
|
|
||||||
|
AD5940ImpedanceStructInit(); // Reload config with current HP settings
|
||||||
|
|
||||||
|
pCfg->WuptClkFreq = LFOSCFreq;
|
||||||
|
pCfg->SweepCfg.SweepEn = bFALSE;
|
||||||
|
pCfg->SinFreq = freq;
|
||||||
|
pCfg->NumOfData = -1;
|
||||||
|
pCfg->RealDataCount = -1;
|
||||||
|
pCfg->bParaChanged = bTRUE;
|
||||||
|
|
||||||
|
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
||||||
|
AppIMPCtrl(IMPCTRL_START, 0);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: Init Failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Routine_Sweep(float start, float end, int steps) {
|
||||||
|
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
|
CurrentMode = MODE_IMPEDANCE;
|
||||||
|
|
||||||
|
AppIMPCfg_Type *pCfg;
|
||||||
|
AppIMPGetCfg(&pCfg);
|
||||||
|
AppIMPCleanup();
|
||||||
|
|
||||||
|
AD5940ImpedanceStructInit(); // Reload config with current HP settings
|
||||||
|
|
||||||
|
pCfg->WuptClkFreq = LFOSCFreq;
|
||||||
|
pCfg->SweepCfg.SweepEn = bTRUE;
|
||||||
|
pCfg->SweepCfg.SweepStart = start;
|
||||||
|
pCfg->SweepCfg.SweepStop = end;
|
||||||
|
pCfg->SweepCfg.SweepPoints = steps + 1;
|
||||||
|
pCfg->NumOfData = steps + 1;
|
||||||
|
pCfg->RealDataCount = steps;
|
||||||
|
pCfg->SweepCfg.SweepLog = bTRUE;
|
||||||
|
pCfg->bParaChanged = bTRUE;
|
||||||
|
|
||||||
|
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
||||||
|
AppIMPCtrl(IMPCTRL_START, 0);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: Init Failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Routine_Amperometric(float bias_mv) {
|
||||||
|
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
|
||||||
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
|
CurrentMode = MODE_AMPEROMETRIC;
|
||||||
|
|
||||||
|
printf(">> Starting Amperometry (Bias: %.1f mV, LP Range: %d)...\n", bias_mv, ConfigLptiaVal);
|
||||||
|
|
||||||
|
AppAMPCfg_Type *pCfg;
|
||||||
|
AppAMPGetCfg(&pCfg);
|
||||||
|
AD5940AMPStructInit(); // Reload config with current LP settings
|
||||||
|
pCfg->SensorBias = bias_mv;
|
||||||
|
pCfg->ReDoRtiaCal = bFALSE; // Use pre-calibrated value
|
||||||
|
|
||||||
|
if(AppAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
||||||
|
AppAMPCtrl(AMPCTRL_START, 0);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: AMP Init Failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Routine_LSV(float start_mv, float end_mv, int steps, int duration_ms) {
|
||||||
|
if (CurrentMode == MODE_IMPEDANCE) AppIMPCleanup();
|
||||||
|
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
|
CurrentMode = MODE_RAMP;
|
||||||
|
|
||||||
|
printf(">> Starting LSV (%.1f to %.1f mV, %d steps, %d ms, LP Range: %d)...\n", start_mv, end_mv, steps, duration_ms, ConfigLptiaVal);
|
||||||
|
|
||||||
|
AppRAMPCfg_Type *pCfg;
|
||||||
|
AppRAMPGetCfg(&pCfg);
|
||||||
|
AD5940RampStructInit(); // Reload config with current LP settings
|
||||||
|
|
||||||
|
pCfg->RampStartVolt = start_mv;
|
||||||
|
pCfg->RampPeakVolt = end_mv;
|
||||||
|
pCfg->StepNumber = steps;
|
||||||
|
pCfg->RampDuration = duration_ms;
|
||||||
|
pCfg->bRampOneDir = bTRUE;
|
||||||
|
pCfg->bParaChanged = bTRUE;
|
||||||
|
|
||||||
|
if(AppRAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
||||||
|
AppRAMPCtrl(APPCTRL_START, 0);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: RAMP Init Failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Routine_CalibrateSystem(void) {
|
||||||
|
if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
|
CurrentMode = MODE_IMPEDANCE;
|
||||||
|
|
||||||
|
AppIMPCfg_Type *pCfg;
|
||||||
|
AppIMPGetCfg(&pCfg);
|
||||||
|
AppIMPCleanup();
|
||||||
|
|
||||||
|
ADCPGACal_Type adcpga_cal;
|
||||||
|
adcpga_cal.AdcClkFreq = 16000000.0;
|
||||||
|
adcpga_cal.SysClkFreq = 16000000.0;
|
||||||
|
adcpga_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
adcpga_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
||||||
|
adcpga_cal.ADCPga = ADCPGA_1P5;
|
||||||
|
adcpga_cal.PGACalType = PGACALTYPE_OFFSET;
|
||||||
|
adcpga_cal.TimeOut10us = 1000;
|
||||||
|
adcpga_cal.VRef1p11 = 1.11;
|
||||||
|
adcpga_cal.VRef1p82 = 1.82;
|
||||||
|
printf(">> Calibrating ADC Offset...\n");
|
||||||
|
AD5940_ADCPGACal(&adcpga_cal);
|
||||||
|
|
||||||
|
// --- 1. Calibrate LPTIA (Low Power Loop) ---
|
||||||
|
LPRTIACal_Type lprtia_cal;
|
||||||
|
fImpPol_Type LpRes;
|
||||||
|
memset(&lprtia_cal, 0, sizeof(lprtia_cal));
|
||||||
|
lprtia_cal.AdcClkFreq = 16000000.0;
|
||||||
|
lprtia_cal.SysClkFreq = 16000000.0;
|
||||||
|
lprtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
lprtia_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
||||||
|
lprtia_cal.bPolarResult = bTRUE;
|
||||||
|
lprtia_cal.fRcal = 100.0;
|
||||||
|
lprtia_cal.LpTiaRtia = GetLPTIARtia(ConfigLptiaVal);
|
||||||
|
lprtia_cal.LpAmpPwrMod = LPAMPPWR_NORM;
|
||||||
|
lprtia_cal.bWithCtia = bFALSE;
|
||||||
|
// Use a low frequency for LPTIA calibration
|
||||||
|
lprtia_cal.fFreq = 100.0f;
|
||||||
|
lprtia_cal.DftCfg.DftNum = DFTNUM_2048;
|
||||||
|
lprtia_cal.DftCfg.DftSrc = DFTSRC_SINC3;
|
||||||
|
lprtia_cal.DftCfg.HanWinEn = bTRUE;
|
||||||
|
|
||||||
|
printf(">> Calibrating LPTIA %d Ohm...\n", ConfigLptiaVal);
|
||||||
|
if (AD5940_LPRtiaCal(&lprtia_cal, &LpRes) == AD5940ERR_OK) {
|
||||||
|
printf("Calibrated LPTIA: Mag = %f Ohm, Phase = %f\n", LpRes.Magnitude, LpRes.Phase);
|
||||||
|
CalibratedLptiaVal = LpRes.Magnitude;
|
||||||
|
} else {
|
||||||
|
printf("LPTIA Calibration Failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. Calibrate HSTIA (High Speed Loop) ---
|
||||||
|
HSDACCfg_Type hsdac_cfg;
|
||||||
|
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_0P25;
|
||||||
|
hsdac_cfg.HsDacGain = HSDACGAIN_0P2;
|
||||||
|
hsdac_cfg.HsDacUpdateRate = 7;
|
||||||
|
AD5940_HSDacCfgS(&hsdac_cfg);
|
||||||
|
|
||||||
|
HSRTIACal_Type hsrtia_cal;
|
||||||
|
fImpPol_Type HsRes;
|
||||||
|
memset(&hsrtia_cal, 0, sizeof(hsrtia_cal));
|
||||||
|
hsrtia_cal.fFreq = 1000.0f;
|
||||||
|
hsrtia_cal.AdcClkFreq = 16000000.0;
|
||||||
|
hsrtia_cal.SysClkFreq = 16000000.0;
|
||||||
|
hsrtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
||||||
|
hsrtia_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
||||||
|
hsrtia_cal.bPolarResult = bTRUE;
|
||||||
|
hsrtia_cal.fRcal = 100.0;
|
||||||
|
hsrtia_cal.HsTiaCfg.DiodeClose = bFALSE;
|
||||||
|
hsrtia_cal.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
|
||||||
|
hsrtia_cal.HsTiaCfg.HstiaCtia = 31;
|
||||||
|
hsrtia_cal.HsTiaCfg.HstiaRtiaSel = GetHSTIARtia(ConfigHstiaVal);
|
||||||
|
hsrtia_cal.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
|
||||||
|
hsrtia_cal.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
|
||||||
|
hsrtia_cal.DftCfg.DftNum = DFTNUM_16384;
|
||||||
|
hsrtia_cal.DftCfg.DftSrc = DFTSRC_SINC3;
|
||||||
|
|
||||||
|
printf(">> Calibrating HSTIA %d Ohm...\n", ConfigHstiaVal);
|
||||||
|
if (AD5940_HSRtiaCal(&hsrtia_cal, &HsRes) == AD5940ERR_OK) {
|
||||||
|
printf("Calibrated HSTIA: Mag = %f Ohm, Phase = %f\n", HsRes.Magnitude, HsRes.Phase);
|
||||||
|
CalibratedHstiaVal = HsRes.Magnitude;
|
||||||
|
} else {
|
||||||
|
printf("HSTIA Calibration Failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// RampTest.c
|
// File: RampTest.c
|
||||||
#include "ad5940.h"
|
#include "ad5940.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
@ -42,6 +42,7 @@ AppRAMPCfg_Type AppRAMPCfg =
|
||||||
.RampState = RAMP_STATE0,
|
.RampState = RAMP_STATE0,
|
||||||
.bFirstDACSeq = bTRUE,
|
.bFirstDACSeq = bTRUE,
|
||||||
.bRampOneDir = bFALSE,
|
.bRampOneDir = bFALSE,
|
||||||
|
.ShortRe0Se0 = bFALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
AD5940Err AppRAMPGetCfg(void *pCfg)
|
AD5940Err AppRAMPGetCfg(void *pCfg)
|
||||||
|
|
@ -147,6 +148,11 @@ static AD5940Err AppRAMPSeqInitGen(void)
|
||||||
lploop_cfg.LpAmpCfg.LpTiaRtia = AppRAMPCfg.LPTIARtiaSel;
|
lploop_cfg.LpAmpCfg.LpTiaRtia = AppRAMPCfg.LPTIARtiaSel;
|
||||||
|
|
||||||
lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
|
lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(5)|LPTIASW(2)|LPTIASW(4)|LPTIASW(12)|LPTIASW(13);
|
||||||
|
|
||||||
|
// Apply Short Option
|
||||||
|
if(AppRAMPCfg.ShortRe0Se0) {
|
||||||
|
lploop_cfg.LpAmpCfg.LpTiaSW |= LPTIASW(11);
|
||||||
|
}
|
||||||
|
|
||||||
lploop_cfg.LpDacCfg.LpdacSel = LPDAC0;
|
lploop_cfg.LpDacCfg.LpdacSel = LPDAC0;
|
||||||
lploop_cfg.LpDacCfg.DacData12Bit = 0x800;
|
lploop_cfg.LpDacCfg.DacData12Bit = 0x800;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// RampTest.h
|
// File: RampTest.h
|
||||||
#ifndef _RAMPTEST_H_
|
#ifndef _RAMPTEST_H_
|
||||||
#define _RAMPTEST_H_
|
#define _RAMPTEST_H_
|
||||||
#include "ad5940.h"
|
#include "ad5940.h"
|
||||||
|
|
@ -51,6 +51,7 @@ typedef struct
|
||||||
BoolFlag StopRequired;
|
BoolFlag StopRequired;
|
||||||
enum _RampState{RAMP_STATE0 = 0, RAMP_STATE1, RAMP_STATE2, RAMP_STATE3, RAMP_STATE4, RAMP_STOP} RampState;
|
enum _RampState{RAMP_STATE0 = 0, RAMP_STATE1, RAMP_STATE2, RAMP_STATE3, RAMP_STATE4, RAMP_STOP} RampState;
|
||||||
BoolFlag bRampOneDir;
|
BoolFlag bRampOneDir;
|
||||||
|
BoolFlag ShortRe0Se0; /* Short RE0 to SE0 */
|
||||||
}AppRAMPCfg_Type;
|
}AppRAMPCfg_Type;
|
||||||
|
|
||||||
#define APPCTRL_START 0
|
#define APPCTRL_START 0
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ option(BUILD_IOS "Build for iOS" OFF)
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets SerialPort PrintSupport)
|
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets SerialPort PrintSupport)
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS AndroidExtras)
|
||||||
|
endif()
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# --- FFTW3 CONFIGURATION ---
|
# --- FFTW3 CONFIGURATION ---
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
@ -197,16 +201,20 @@ endif()
|
||||||
|
|
||||||
target_include_directories(EISConfigurator PRIVATE ${qcustomplot_SOURCE_DIR})
|
target_include_directories(EISConfigurator PRIVATE ${qcustomplot_SOURCE_DIR})
|
||||||
|
|
||||||
target_link_libraries(EISConfigurator PRIVATE
|
|
||||||
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::SerialPort Qt6::PrintSupport
|
|
||||||
${FFTW_TARGET}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(BUILD_ANDROID)
|
if(BUILD_ANDROID)
|
||||||
target_link_libraries(EISConfigurator PRIVATE log m)
|
target_link_libraries(EISConfigurator PRIVATE log m)
|
||||||
set_property(TARGET EISConfigurator PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
set_property(TARGET EISConfigurator PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||||
|
|
||||||
|
target_link_libraries(EISConfigurator PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::AndroidExtras)
|
||||||
|
else()
|
||||||
|
add_executable(EISConfigurator ${PROJECT_SOURCES})
|
||||||
|
target_link_libraries(EISConfigurator PRIVATE Qt6::Widgets Qt6::SerialPort)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(BUILD_IOS)
|
if(BUILD_IOS)
|
||||||
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist")
|
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist")
|
||||||
message(FATAL_ERROR "Missing ios/Info.plist. Please create it before building.")
|
message(FATAL_ERROR "Missing ios/Info.plist. Please create it before building.")
|
||||||
|
|
@ -237,6 +245,12 @@ elseif(WIN32)
|
||||||
set_target_properties(EISConfigurator PROPERTIES
|
set_target_properties(EISConfigurator PROPERTIES
|
||||||
WIN32_EXECUTABLE TRUE
|
WIN32_EXECUTABLE TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(EISConfigurator PRIVATE
|
||||||
|
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::SerialPort Qt6::PrintSupport
|
||||||
|
${FFTW_TARGET}
|
||||||
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# CRITICAL: Triggers androiddeployqt to build the APK
|
# CRITICAL: Triggers androiddeployqt to build the APK
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,19 @@
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<!-- USB Host is required for Serial Port communication -->
|
||||||
|
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
|
||||||
<application android:label="EIS Configurator"
|
<application android:label="EIS Configurator"
|
||||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:extractNativeLibs="true">
|
||||||
|
|
||||||
<activity android:name="org.qtproject.qt.android.bindings.QtActivity"
|
<activity android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||||
android:label="EIS Configurator"
|
android:label="EIS Configurator"
|
||||||
|
|
@ -26,6 +31,11 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- USB Device Attachment Intent (Optional: Auto-open app on plug) -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="EISConfigurator"/>
|
<meta-data android:name="android.app.lib_name" android:value="EISConfigurator"/>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
# scripts/generate_icons.sh
|
# host/scripts/generate_icons.sh
|
||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Argument 1: Path to magick executable
|
# Resolve the directory where the script is located
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
# Project Root is two levels up (host/scripts -> host -> root)
|
||||||
|
PROJECT_ROOT="$SCRIPT_DIR/../.."
|
||||||
|
|
||||||
MAGICK_BIN="$1"
|
MAGICK_BIN="$1"
|
||||||
|
if [ -z "$MAGICK_BIN" ]; then MAGICK_BIN="magick"; fi
|
||||||
|
|
||||||
# Fallback if not provided
|
SOURCE="$PROJECT_ROOT/assets/icon_source.png"
|
||||||
if [ -z "$MAGICK_BIN" ]; then
|
OUT_DIR="$PROJECT_ROOT/assets/icons"
|
||||||
MAGICK_BIN="magick"
|
ANDROID_RES_DIR="$PROJECT_ROOT/host/android/res"
|
||||||
fi
|
|
||||||
|
|
||||||
# Assumes running from Project Root
|
|
||||||
SOURCE="assets/icon_source.png"
|
|
||||||
OUT_DIR="assets/icons"
|
|
||||||
|
|
||||||
if [ ! -f "$SOURCE" ]; then
|
if [ ! -f "$SOURCE" ]; then
|
||||||
echo "Error: Source image '$SOURCE' not found in $(pwd)"
|
echo "Error: Source image '$SOURCE' not found."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -57,22 +57,22 @@ rm -rf "$ICONSET"
|
||||||
-delete 0 -alpha off -colors 256 "$OUT_DIR/app_icon.ico"
|
-delete 0 -alpha off -colors 256 "$OUT_DIR/app_icon.ico"
|
||||||
|
|
||||||
# Android
|
# Android
|
||||||
ANDROID_DIR="$OUT_DIR/android/res"
|
echo "Generating Android Icons into $ANDROID_RES_DIR..."
|
||||||
|
|
||||||
mkdir -p "$ANDROID_DIR/mipmap-mdpi"
|
mkdir -p "$ANDROID_RES_DIR/mipmap-mdpi"
|
||||||
"$MAGICK_BIN" "$SOURCE" -scale 48x48 "$ANDROID_DIR/mipmap-mdpi/ic_launcher.png"
|
"$MAGICK_BIN" "$SOURCE" -scale 48x48 "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher.png"
|
||||||
|
|
||||||
mkdir -p "$ANDROID_DIR/mipmap-hdpi"
|
mkdir -p "$ANDROID_RES_DIR/mipmap-hdpi"
|
||||||
"$MAGICK_BIN" "$SOURCE" -scale 72x72 "$ANDROID_DIR/mipmap-hdpi/ic_launcher.png"
|
"$MAGICK_BIN" "$SOURCE" -scale 72x72 "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher.png"
|
||||||
|
|
||||||
mkdir -p "$ANDROID_DIR/mipmap-xhdpi"
|
mkdir -p "$ANDROID_RES_DIR/mipmap-xhdpi"
|
||||||
"$MAGICK_BIN" "$SOURCE" -scale 96x96 "$ANDROID_DIR/mipmap-xhdpi/ic_launcher.png"
|
"$MAGICK_BIN" "$SOURCE" -scale 96x96 "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher.png"
|
||||||
|
|
||||||
mkdir -p "$ANDROID_DIR/mipmap-xxhdpi"
|
mkdir -p "$ANDROID_RES_DIR/mipmap-xxhdpi"
|
||||||
"$MAGICK_BIN" "$SOURCE" -scale 144x144 "$ANDROID_DIR/mipmap-xxhdpi/ic_launcher.png"
|
"$MAGICK_BIN" "$SOURCE" -scale 144x144 "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher.png"
|
||||||
|
|
||||||
mkdir -p "$ANDROID_DIR/mipmap-xxxhdpi"
|
mkdir -p "$ANDROID_RES_DIR/mipmap-xxxhdpi"
|
||||||
"$MAGICK_BIN" "$SOURCE" -scale 192x192 "$ANDROID_DIR/mipmap-xxxhdpi/ic_launcher.png"
|
"$MAGICK_BIN" "$SOURCE" -scale 192x192 "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher.png"
|
||||||
|
|
||||||
# iOS
|
# iOS
|
||||||
XCASSETS_DIR="$OUT_DIR/ios/Assets.xcassets"
|
XCASSETS_DIR="$OUT_DIR/ios/Assets.xcassets"
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@ bool MainWindow::event(QEvent *event) {
|
||||||
void MainWindow::handleSwipe(QSwipeGesture *gesture) {
|
void MainWindow::handleSwipe(QSwipeGesture *gesture) {
|
||||||
if (gesture->state() == Qt::GestureFinished) {
|
if (gesture->state() == Qt::GestureFinished) {
|
||||||
if (gesture->horizontalDirection() == QSwipeGesture::Left) {
|
if (gesture->horizontalDirection() == QSwipeGesture::Left) {
|
||||||
if (tabWidget->currentIndex() < tabWidget->count() - 1)
|
if (mainTabWidget->currentIndex() < mainTabWidget->count() - 1)
|
||||||
tabWidget->setCurrentIndex(tabWidget->currentIndex() + 1);
|
mainTabWidget->setCurrentIndex(mainTabWidget->currentIndex() + 1);
|
||||||
} else if (gesture->horizontalDirection() == QSwipeGesture::Right) {
|
} else if (gesture->horizontalDirection() == QSwipeGesture::Right) {
|
||||||
if (tabWidget->currentIndex() > 0)
|
if (mainTabWidget->currentIndex() > 0)
|
||||||
tabWidget->setCurrentIndex(tabWidget->currentIndex() - 1);
|
mainTabWidget->setCurrentIndex(mainTabWidget->currentIndex() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QGroupBox>
|
||||||
#include "GraphWidget.h"
|
#include "GraphWidget.h"
|
||||||
|
|
||||||
class MainWindow : public QMainWindow {
|
class MainWindow : public QMainWindow {
|
||||||
|
|
@ -38,6 +39,8 @@ private slots:
|
||||||
// UI Slots
|
// UI Slots
|
||||||
void onBlinkTimer();
|
void onBlinkTimer();
|
||||||
void onLPFChanged(int index);
|
void onLPFChanged(int index);
|
||||||
|
void onRLoadChanged(int index); // New Slot
|
||||||
|
void onShortRe0Se0Toggled(bool checked);
|
||||||
|
|
||||||
// Action Slots
|
// Action Slots
|
||||||
void checkDeviceId();
|
void checkDeviceId();
|
||||||
|
|
@ -84,33 +87,43 @@ private:
|
||||||
GraphWidget *lsvGraph;
|
GraphWidget *lsvGraph;
|
||||||
QTextEdit *logWidget;
|
QTextEdit *logWidget;
|
||||||
|
|
||||||
// Layout
|
// Layout Containers
|
||||||
QTabWidget *tabWidget;
|
QTabWidget *mainTabWidget;
|
||||||
|
QTabWidget *impGraphTabs;
|
||||||
|
|
||||||
|
// --- Global Controls ---
|
||||||
QComboBox *portSelector;
|
QComboBox *portSelector;
|
||||||
QPushButton *connectBtn;
|
QPushButton *connectBtn;
|
||||||
QPushButton *checkIdBtn;
|
QPushButton *checkIdBtn;
|
||||||
QPushButton *calibrateBtn;
|
QPushButton *calibrateBtn;
|
||||||
QPushButton *sweepBtn;
|
QComboBox *comboRangeLP;
|
||||||
QPushButton *measureBtn;
|
QComboBox *comboRangeHP;
|
||||||
QDoubleSpinBox *spinFreq;
|
QComboBox *comboRLoad; // New Control
|
||||||
|
QCheckBox *checkShortRe0Se0;
|
||||||
|
|
||||||
// EIS Configuration
|
// --- Impedance Controls ---
|
||||||
QDoubleSpinBox *spinSweepStart;
|
QDoubleSpinBox *spinSweepStart;
|
||||||
QDoubleSpinBox *spinSweepStop;
|
QDoubleSpinBox *spinSweepStop;
|
||||||
QSpinBox *spinSweepPPD;
|
QSpinBox *spinSweepPPD;
|
||||||
QComboBox *comboRangeLP; // New LP Range
|
QPushButton *sweepBtn;
|
||||||
QComboBox *comboRangeHP; // New HP Range
|
|
||||||
|
QDoubleSpinBox *spinFreq;
|
||||||
|
QPushButton *measureBtn;
|
||||||
|
|
||||||
// Shunt / De-embedding Configuration
|
|
||||||
QCheckBox *checkShunt;
|
QCheckBox *checkShunt;
|
||||||
QDoubleSpinBox *spinShuntRes;
|
QDoubleSpinBox *spinShuntRes;
|
||||||
|
|
||||||
|
QDoubleSpinBox *spinCondStd;
|
||||||
|
QPushButton *btnCalCond;
|
||||||
|
QLabel *lblResultRs;
|
||||||
|
QLabel *lblResultCond;
|
||||||
|
|
||||||
// Amperometry Configuration
|
// --- Amperometry Controls ---
|
||||||
QDoubleSpinBox *spinAmpBias;
|
QDoubleSpinBox *spinAmpBias;
|
||||||
QPushButton *ampBtn;
|
QPushButton *ampBtn;
|
||||||
QComboBox *comboLPF;
|
QComboBox *comboLPF;
|
||||||
|
|
||||||
// LSV Configuration
|
// --- LSV Controls ---
|
||||||
QDoubleSpinBox *spinLsvStart;
|
QDoubleSpinBox *spinLsvStart;
|
||||||
QDoubleSpinBox *spinLsvStop;
|
QDoubleSpinBox *spinLsvStop;
|
||||||
QSpinBox *spinLsvSteps;
|
QSpinBox *spinLsvSteps;
|
||||||
|
|
@ -118,19 +131,12 @@ private:
|
||||||
QPushButton *lsvBlankBtn;
|
QPushButton *lsvBlankBtn;
|
||||||
QPushButton *lsvSampleBtn;
|
QPushButton *lsvSampleBtn;
|
||||||
|
|
||||||
// Conductivity Calibration
|
// State
|
||||||
QDoubleSpinBox *spinCondStd;
|
|
||||||
QPushButton *btnCalCond;
|
|
||||||
QLabel *lblResultRs;
|
|
||||||
QLabel *lblResultCond;
|
|
||||||
|
|
||||||
double cellConstant = 1.0;
|
double cellConstant = 1.0;
|
||||||
|
|
||||||
bool isMeasuringImp = false;
|
bool isMeasuringImp = false;
|
||||||
bool isMeasuringAmp = false;
|
bool isMeasuringAmp = false;
|
||||||
bool isSweeping = false;
|
bool isSweeping = false;
|
||||||
|
|
||||||
// LSV State
|
|
||||||
enum LSVState { LSV_IDLE, LSV_RUNNING_BLANK, LSV_RUNNING_SAMPLE };
|
enum LSVState { LSV_IDLE, LSV_RUNNING_BLANK, LSV_RUNNING_SAMPLE };
|
||||||
LSVState lsvState = LSV_IDLE;
|
LSVState lsvState = LSV_IDLE;
|
||||||
|
|
||||||
|
|
@ -139,7 +145,6 @@ private:
|
||||||
QVector<double> sweepReals;
|
QVector<double> sweepReals;
|
||||||
QVector<double> sweepImags;
|
QVector<double> sweepImags;
|
||||||
|
|
||||||
// LSV Data Storage for Diff Calculation
|
|
||||||
struct LSVPoint { double voltage; double current; };
|
struct LSVPoint { double voltage; double current; };
|
||||||
QVector<LSVPoint> lsvBlankData;
|
QVector<LSVPoint> lsvBlankData;
|
||||||
QVector<LSVPoint> lsvSampleData;
|
QVector<LSVPoint> lsvSampleData;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,20 @@ void MainWindow::onLPFChanged(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onRLoadChanged(int index) {
|
||||||
|
if (serial->isOpen()) {
|
||||||
|
int val = comboRLoad->itemData(index).toInt();
|
||||||
|
serial->write(QString("L %1\n").arg(val).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onShortRe0Se0Toggled(bool checked) {
|
||||||
|
if (serial->isOpen()) {
|
||||||
|
serial->write(QString("t %1\n").arg(checked ? 1 : 0).toUtf8());
|
||||||
|
logWidget->append(QString(">> RE0-SE0 Short: %1").arg(checked ? "ON" : "OFF"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::initiateSequence(const QString &cmd) {
|
void MainWindow::initiateSequence(const QString &cmd) {
|
||||||
if (!serial->isOpen()) return;
|
if (!serial->isOpen()) return;
|
||||||
|
|
||||||
|
|
@ -64,7 +78,6 @@ void MainWindow::startSweep() {
|
||||||
isSweeping = true;
|
isSweeping = true;
|
||||||
sweepBtn->setText("Stop Sweep");
|
sweepBtn->setText("Stop Sweep");
|
||||||
setButtonBlinking(sweepBtn, true);
|
setButtonBlinking(sweepBtn, true);
|
||||||
tabWidget->setCurrentIndex(1);
|
|
||||||
|
|
||||||
initiateSequence(cmd);
|
initiateSequence(cmd);
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +100,6 @@ void MainWindow::toggleMeasurement() {
|
||||||
measureBtn->setText("Stop");
|
measureBtn->setText("Stop");
|
||||||
setButtonBlinking(measureBtn, true);
|
setButtonBlinking(measureBtn, true);
|
||||||
isMeasuringImp = true;
|
isMeasuringImp = true;
|
||||||
tabWidget->setCurrentIndex(0);
|
|
||||||
|
|
||||||
initiateSequence(cmd);
|
initiateSequence(cmd);
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +124,6 @@ void MainWindow::toggleAmperometry() {
|
||||||
setButtonBlinking(ampBtn, true);
|
setButtonBlinking(ampBtn, true);
|
||||||
isMeasuringAmp = true;
|
isMeasuringAmp = true;
|
||||||
ampGraph->clear();
|
ampGraph->clear();
|
||||||
tabWidget->setCurrentIndex(2);
|
|
||||||
|
|
||||||
initiateSequence(cmd);
|
initiateSequence(cmd);
|
||||||
}
|
}
|
||||||
|
|
@ -148,8 +159,6 @@ void MainWindow::startLSVBlank() {
|
||||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
||||||
lsvBlankData.clear();
|
lsvBlankData.clear();
|
||||||
|
|
||||||
tabWidget->setCurrentIndex(3);
|
|
||||||
|
|
||||||
initiateSequence(cmd);
|
initiateSequence(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,8 +192,6 @@ void MainWindow::startLSVSample() {
|
||||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
||||||
lsvSampleData.clear();
|
lsvSampleData.clear();
|
||||||
|
|
||||||
tabWidget->setCurrentIndex(3);
|
|
||||||
|
|
||||||
initiateSequence(cmd);
|
initiateSequence(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,28 +30,19 @@ void MainWindow::connectToPort() {
|
||||||
serial->close();
|
serial->close();
|
||||||
connectBtn->setText("Connect");
|
connectBtn->setText("Connect");
|
||||||
logWidget->append("--- Disconnected ---");
|
logWidget->append("--- Disconnected ---");
|
||||||
|
|
||||||
|
// 1. Disable Global Controls
|
||||||
checkIdBtn->setEnabled(false);
|
checkIdBtn->setEnabled(false);
|
||||||
calibrateBtn->setEnabled(false);
|
calibrateBtn->setEnabled(false);
|
||||||
sweepBtn->setEnabled(false);
|
|
||||||
measureBtn->setEnabled(false);
|
|
||||||
spinSweepStart->setEnabled(false);
|
|
||||||
spinSweepStop->setEnabled(false);
|
|
||||||
spinSweepPPD->setEnabled(false);
|
|
||||||
ampBtn->setEnabled(false);
|
|
||||||
spinAmpBias->setEnabled(false);
|
|
||||||
btnCalCond->setEnabled(false);
|
|
||||||
|
|
||||||
// Updated Range Controls
|
|
||||||
comboRangeLP->setEnabled(false);
|
comboRangeLP->setEnabled(false);
|
||||||
comboRangeHP->setEnabled(false);
|
comboRangeHP->setEnabled(false);
|
||||||
|
comboRLoad->setEnabled(false);
|
||||||
|
checkShortRe0Se0->setEnabled(false);
|
||||||
|
|
||||||
comboLPF->setEnabled(false);
|
// 2. Disable Tabs
|
||||||
lsvBlankBtn->setEnabled(false);
|
mainTabWidget->widget(0)->setEnabled(false);
|
||||||
lsvSampleBtn->setEnabled(false);
|
mainTabWidget->widget(1)->setEnabled(false);
|
||||||
spinLsvStart->setEnabled(false);
|
mainTabWidget->widget(2)->setEnabled(false);
|
||||||
spinLsvStop->setEnabled(false);
|
|
||||||
spinLsvSteps->setEnabled(false);
|
|
||||||
spinLsvDuration->setEnabled(false);
|
|
||||||
|
|
||||||
isMeasuringImp = false;
|
isMeasuringImp = false;
|
||||||
isMeasuringAmp = false;
|
isMeasuringAmp = false;
|
||||||
|
|
@ -75,31 +66,23 @@ void MainWindow::connectToPort() {
|
||||||
if (serial->open(QIODevice::ReadWrite)) {
|
if (serial->open(QIODevice::ReadWrite)) {
|
||||||
connectBtn->setText("Disconnect");
|
connectBtn->setText("Disconnect");
|
||||||
logWidget->append("--- Connected and Synchronized ---");
|
logWidget->append("--- Connected and Synchronized ---");
|
||||||
|
|
||||||
|
// 1. Enable Global Controls
|
||||||
checkIdBtn->setEnabled(true);
|
checkIdBtn->setEnabled(true);
|
||||||
calibrateBtn->setEnabled(true);
|
calibrateBtn->setEnabled(true);
|
||||||
sweepBtn->setEnabled(true);
|
|
||||||
measureBtn->setEnabled(true);
|
|
||||||
spinSweepStart->setEnabled(true);
|
|
||||||
spinSweepStop->setEnabled(true);
|
|
||||||
spinSweepPPD->setEnabled(true);
|
|
||||||
ampBtn->setEnabled(true);
|
|
||||||
spinAmpBias->setEnabled(true);
|
|
||||||
btnCalCond->setEnabled(true);
|
|
||||||
|
|
||||||
// Updated Range Controls
|
|
||||||
comboRangeLP->setEnabled(true);
|
comboRangeLP->setEnabled(true);
|
||||||
comboRangeHP->setEnabled(true);
|
comboRangeHP->setEnabled(true);
|
||||||
|
comboRLoad->setEnabled(true);
|
||||||
|
checkShortRe0Se0->setEnabled(true);
|
||||||
|
|
||||||
comboLPF->setEnabled(true);
|
// 2. Enable Tabs
|
||||||
lsvBlankBtn->setEnabled(true);
|
mainTabWidget->widget(0)->setEnabled(true);
|
||||||
lsvSampleBtn->setEnabled(true);
|
mainTabWidget->widget(1)->setEnabled(true);
|
||||||
spinLsvStart->setEnabled(true);
|
mainTabWidget->widget(2)->setEnabled(true);
|
||||||
spinLsvStop->setEnabled(true);
|
|
||||||
spinLsvSteps->setEnabled(true);
|
|
||||||
spinLsvDuration->setEnabled(true);
|
|
||||||
|
|
||||||
// Sync LPF
|
// Sync LPF and RLoad
|
||||||
onLPFChanged(comboLPF->currentIndex());
|
onLPFChanged(comboLPF->currentIndex());
|
||||||
|
onRLoadChanged(comboRLoad->currentIndex());
|
||||||
} else {
|
} else {
|
||||||
logWidget->append(">> Connection Error: " + serial->errorString());
|
logWidget->append(">> Connection Error: " + serial->errorString());
|
||||||
}
|
}
|
||||||
|
|
@ -110,28 +93,18 @@ void MainWindow::onPortError(QSerialPort::SerialPortError error) {
|
||||||
logWidget->append(">> Critical Error: Connection Lost.");
|
logWidget->append(">> Critical Error: Connection Lost.");
|
||||||
serial->close();
|
serial->close();
|
||||||
connectBtn->setText("Connect");
|
connectBtn->setText("Connect");
|
||||||
|
|
||||||
checkIdBtn->setEnabled(false);
|
checkIdBtn->setEnabled(false);
|
||||||
calibrateBtn->setEnabled(false);
|
calibrateBtn->setEnabled(false);
|
||||||
sweepBtn->setEnabled(false);
|
|
||||||
measureBtn->setEnabled(false);
|
|
||||||
spinSweepStart->setEnabled(false);
|
|
||||||
spinSweepStop->setEnabled(false);
|
|
||||||
spinSweepPPD->setEnabled(false);
|
|
||||||
ampBtn->setEnabled(false);
|
|
||||||
spinAmpBias->setEnabled(false);
|
|
||||||
btnCalCond->setEnabled(false);
|
|
||||||
|
|
||||||
// Updated Range Controls
|
|
||||||
comboRangeLP->setEnabled(false);
|
comboRangeLP->setEnabled(false);
|
||||||
comboRangeHP->setEnabled(false);
|
comboRangeHP->setEnabled(false);
|
||||||
|
comboRLoad->setEnabled(false);
|
||||||
|
checkShortRe0Se0->setEnabled(false);
|
||||||
|
|
||||||
|
mainTabWidget->widget(0)->setEnabled(false);
|
||||||
|
mainTabWidget->widget(1)->setEnabled(false);
|
||||||
|
mainTabWidget->widget(2)->setEnabled(false);
|
||||||
|
|
||||||
comboLPF->setEnabled(false);
|
|
||||||
lsvBlankBtn->setEnabled(false);
|
|
||||||
lsvSampleBtn->setEnabled(false);
|
|
||||||
spinLsvStart->setEnabled(false);
|
|
||||||
spinLsvStop->setEnabled(false);
|
|
||||||
spinLsvSteps->setEnabled(false);
|
|
||||||
spinLsvDuration->setEnabled(false);
|
|
||||||
setButtonBlinking(nullptr, false);
|
setButtonBlinking(nullptr, false);
|
||||||
currentSequence = SEQ_IDLE;
|
currentSequence = SEQ_IDLE;
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +125,7 @@ void MainWindow::handleSerialData() {
|
||||||
if (str.contains("AD5940LIB Version:v0.2.1")) {
|
if (str.contains("AD5940LIB Version:v0.2.1")) {
|
||||||
logWidget->append(">> Sequence: Boot detected. Configuring...");
|
logWidget->append(">> Sequence: Boot detected. Configuring...");
|
||||||
|
|
||||||
// 1. Restore Settings (Range & Filter)
|
// 1. Restore Settings
|
||||||
int lpVal = comboRangeLP->currentData().toInt();
|
int lpVal = comboRangeLP->currentData().toInt();
|
||||||
int hpVal = comboRangeHP->currentData().toInt();
|
int hpVal = comboRangeHP->currentData().toInt();
|
||||||
serial->write(QString("r %1 %2\n").arg(lpVal).arg(hpVal).toUtf8());
|
serial->write(QString("r %1 %2\n").arg(lpVal).arg(hpVal).toUtf8());
|
||||||
|
|
@ -161,6 +134,12 @@ void MainWindow::handleSerialData() {
|
||||||
int lpfVal = comboLPF->itemData(lpfIdx).toInt();
|
int lpfVal = comboLPF->itemData(lpfIdx).toInt();
|
||||||
serial->write(QString("f %1\n").arg(lpfVal).toUtf8());
|
serial->write(QString("f %1\n").arg(lpfVal).toUtf8());
|
||||||
|
|
||||||
|
int rloadIdx = comboRLoad->currentIndex();
|
||||||
|
int rloadVal = comboRLoad->itemData(rloadIdx).toInt();
|
||||||
|
serial->write(QString("L %1\n").arg(rloadVal).toUtf8());
|
||||||
|
|
||||||
|
serial->write(QString("t %1\n").arg(checkShortRe0Se0->isChecked() ? 1 : 0).toUtf8());
|
||||||
|
|
||||||
// 2. Start Calibration
|
// 2. Start Calibration
|
||||||
logWidget->append(">> Sequence: Calibrating...");
|
logWidget->append(">> Sequence: Calibrating...");
|
||||||
serial->write("c\n");
|
serial->write("c\n");
|
||||||
|
|
|
||||||
|
|
@ -5,105 +5,44 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QScroller>
|
#include <QScroller>
|
||||||
|
#include <QGroupBox>
|
||||||
|
|
||||||
void MainWindow::setupUi() {
|
void MainWindow::setupUi() {
|
||||||
QWidget *central = new QWidget(this);
|
QWidget *central = new QWidget(this);
|
||||||
setCentralWidget(central);
|
setCentralWidget(central);
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout(central);
|
QVBoxLayout *mainLayout = new QVBoxLayout(central);
|
||||||
mainLayout->setContentsMargins(5, 5, 5, 5);
|
mainLayout->setContentsMargins(2, 2, 2, 2);
|
||||||
mainLayout->setSpacing(5);
|
mainLayout->setSpacing(2);
|
||||||
|
|
||||||
// --- Controls Container ---
|
// ========================================================================
|
||||||
QWidget *controlsContainer = new QWidget(this);
|
// 1. Global Toolbar (Connection & Hardware Settings)
|
||||||
controlsContainer->setStyleSheet("background-color: #3A3A3A; border-radius: 5px;");
|
// ========================================================================
|
||||||
QVBoxLayout *ctrlLayout = new QVBoxLayout(controlsContainer);
|
QGroupBox *globalGroup = new QGroupBox("Connection & Hardware", this);
|
||||||
ctrlLayout->setContentsMargins(5, 5, 5, 5);
|
globalGroup->setStyleSheet("QGroupBox { font-weight: bold; border: 1px solid #555; border-radius: 4px; margin-top: 6px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; }");
|
||||||
ctrlLayout->setSpacing(5);
|
QVBoxLayout *globalLayout = new QVBoxLayout(globalGroup);
|
||||||
|
globalLayout->setContentsMargins(4, 12, 4, 4);
|
||||||
// Row 1
|
globalLayout->setSpacing(4);
|
||||||
QHBoxLayout *row1 = new QHBoxLayout();
|
|
||||||
row1->setSpacing(8);
|
|
||||||
|
|
||||||
|
// Row 1: Connection
|
||||||
|
QHBoxLayout *connLayout = new QHBoxLayout();
|
||||||
portSelector = new QComboBox(this);
|
portSelector = new QComboBox(this);
|
||||||
portSelector->setMinimumWidth(120);
|
portSelector->setMinimumWidth(120);
|
||||||
|
|
||||||
connectBtn = new QPushButton("Connect", this);
|
connectBtn = new QPushButton("Connect", this);
|
||||||
// Default Connect Color (Greenish)
|
|
||||||
connectBtn->setStyleSheet("background-color: rgba(0, 128, 0, 128); color: white; font-weight: bold;");
|
connectBtn->setStyleSheet("background-color: rgba(0, 128, 0, 128); color: white; font-weight: bold;");
|
||||||
|
|
||||||
QPushButton *refreshBtn = new QPushButton("Refresh", this);
|
QPushButton *refreshBtn = new QPushButton("Refresh", this);
|
||||||
refreshBtn->setStyleSheet("background-color: rgba(255, 255, 255, 178); color: black; font-weight: bold;");
|
|
||||||
|
|
||||||
row1->addWidget(portSelector);
|
|
||||||
row1->addWidget(connectBtn);
|
|
||||||
row1->addWidget(refreshBtn);
|
|
||||||
|
|
||||||
QFrame *sep1 = new QFrame(); sep1->setFrameShape(QFrame::VLine); sep1->setFrameShadow(QFrame::Sunken);
|
|
||||||
row1->addWidget(sep1);
|
|
||||||
|
|
||||||
checkIdBtn = new QPushButton("Check ID", this);
|
checkIdBtn = new QPushButton("Check ID", this);
|
||||||
checkIdBtn->setStyleSheet("background-color: rgba(255, 255, 255, 166); color: black; font-weight: bold;");
|
|
||||||
|
|
||||||
row1->addWidget(checkIdBtn);
|
connLayout->addWidget(portSelector, 1);
|
||||||
|
connLayout->addWidget(connectBtn);
|
||||||
|
connLayout->addWidget(refreshBtn);
|
||||||
|
connLayout->addWidget(checkIdBtn);
|
||||||
|
globalLayout->addLayout(connLayout);
|
||||||
|
|
||||||
QFrame *sep2 = new QFrame(); sep2->setFrameShape(QFrame::VLine); sep2->setFrameShadow(QFrame::Sunken);
|
// Row 2: Hardware Config
|
||||||
row1->addWidget(sep2);
|
QHBoxLayout *hwLayout = new QHBoxLayout();
|
||||||
|
|
||||||
row1->addWidget(new QLabel("Start:"));
|
|
||||||
spinSweepStart = new QDoubleSpinBox(this);
|
|
||||||
spinSweepStart->setRange(0.1, 200000.0); spinSweepStart->setValue(1000.0); spinSweepStart->setSuffix(" Hz");
|
|
||||||
row1->addWidget(spinSweepStart);
|
|
||||||
|
|
||||||
row1->addWidget(new QLabel("Stop:"));
|
|
||||||
spinSweepStop = new QDoubleSpinBox(this);
|
|
||||||
spinSweepStop->setRange(0.1, 200000.0); spinSweepStop->setValue(200000.0); spinSweepStop->setSuffix(" Hz");
|
|
||||||
row1->addWidget(spinSweepStop);
|
|
||||||
|
|
||||||
row1->addWidget(new QLabel("PPD:"));
|
|
||||||
spinSweepPPD = new QSpinBox(this);
|
|
||||||
spinSweepPPD->setRange(1, 1000); spinSweepPPD->setValue(200); spinSweepPPD->setSuffix(" pts/dec");
|
|
||||||
row1->addWidget(spinSweepPPD);
|
|
||||||
|
|
||||||
sweepBtn = new QPushButton("Sweep", this);
|
|
||||||
sweepBtn->setStyleSheet("background-color: rgba(173, 216, 230, 76); color: white; font-weight: bold;");
|
|
||||||
row1->addWidget(sweepBtn);
|
|
||||||
|
|
||||||
QFrame *sep3 = new QFrame(); sep3->setFrameShape(QFrame::VLine); sep3->setFrameShadow(QFrame::Sunken);
|
|
||||||
row1->addWidget(sep3);
|
|
||||||
|
|
||||||
row1->addWidget(new QLabel("Shunt:"));
|
|
||||||
checkShunt = new QCheckBox("Enable", this);
|
|
||||||
row1->addWidget(checkShunt);
|
|
||||||
spinShuntRes = new QDoubleSpinBox(this);
|
|
||||||
spinShuntRes->setRange(1.0, 1000000.0); spinShuntRes->setValue(466.0); spinShuntRes->setSuffix(" Ω");
|
|
||||||
row1->addWidget(spinShuntRes);
|
|
||||||
|
|
||||||
QFrame *sep4 = new QFrame(); sep4->setFrameShape(QFrame::VLine); sep4->setFrameShadow(QFrame::Sunken);
|
|
||||||
row1->addWidget(sep4);
|
|
||||||
|
|
||||||
row1->addWidget(new QLabel("Std Cond:"));
|
|
||||||
spinCondStd = new QDoubleSpinBox(this);
|
|
||||||
spinCondStd->setRange(0.0, 1000000.0); spinCondStd->setValue(1413.0); spinCondStd->setSuffix(" µS/cm");
|
|
||||||
row1->addWidget(spinCondStd);
|
|
||||||
|
|
||||||
btnCalCond = new QPushButton("Calibrate K", this);
|
|
||||||
btnCalCond->setStyleSheet("background-color: rgba(255, 165, 0, 102); color: white; font-weight: bold;");
|
|
||||||
row1->addWidget(btnCalCond);
|
|
||||||
|
|
||||||
lblResultRs = new QLabel(" Rs: -- Ω", this);
|
|
||||||
lblResultRs->setStyleSheet("font-weight: bold; color: #FFD700; font-size: 14px; margin-left: 10px;");
|
|
||||||
row1->addWidget(lblResultRs);
|
|
||||||
|
|
||||||
row1->addStretch();
|
|
||||||
ctrlLayout->addLayout(row1);
|
|
||||||
|
|
||||||
// Row 2
|
|
||||||
QHBoxLayout *row2 = new QHBoxLayout();
|
|
||||||
row2->setSpacing(8);
|
|
||||||
|
|
||||||
// LP Range (Full List)
|
|
||||||
row2->addWidget(new QLabel("LP Range:"));
|
|
||||||
comboRangeLP = new QComboBox(this);
|
comboRangeLP = new QComboBox(this);
|
||||||
|
comboRangeLP->setToolTip("Low Power TIA Range");
|
||||||
comboRangeLP->addItem("200 Ω", 200);
|
comboRangeLP->addItem("200 Ω", 200);
|
||||||
comboRangeLP->addItem("1 kΩ", 1000);
|
comboRangeLP->addItem("1 kΩ", 1000);
|
||||||
comboRangeLP->addItem("2 kΩ", 2000);
|
comboRangeLP->addItem("2 kΩ", 2000);
|
||||||
|
|
@ -131,11 +70,9 @@ void MainWindow::setupUi() {
|
||||||
comboRangeLP->addItem("256 kΩ", 256000);
|
comboRangeLP->addItem("256 kΩ", 256000);
|
||||||
comboRangeLP->addItem("512 kΩ", 512000);
|
comboRangeLP->addItem("512 kΩ", 512000);
|
||||||
comboRangeLP->setCurrentIndex(1); // Default 1k
|
comboRangeLP->setCurrentIndex(1); // Default 1k
|
||||||
row2->addWidget(comboRangeLP);
|
|
||||||
|
|
||||||
// HP Range (Limited List)
|
|
||||||
row2->addWidget(new QLabel("HP Range:"));
|
|
||||||
comboRangeHP = new QComboBox(this);
|
comboRangeHP = new QComboBox(this);
|
||||||
|
comboRangeHP->setToolTip("High Speed TIA Range");
|
||||||
comboRangeHP->addItem("200 Ω", 200);
|
comboRangeHP->addItem("200 Ω", 200);
|
||||||
comboRangeHP->addItem("1 kΩ", 1000);
|
comboRangeHP->addItem("1 kΩ", 1000);
|
||||||
comboRangeHP->addItem("5 kΩ", 5000);
|
comboRangeHP->addItem("5 kΩ", 5000);
|
||||||
|
|
@ -145,41 +82,117 @@ void MainWindow::setupUi() {
|
||||||
comboRangeHP->addItem("80 kΩ", 80000);
|
comboRangeHP->addItem("80 kΩ", 80000);
|
||||||
comboRangeHP->addItem("160 kΩ", 160000);
|
comboRangeHP->addItem("160 kΩ", 160000);
|
||||||
comboRangeHP->setCurrentIndex(1); // Default 1k
|
comboRangeHP->setCurrentIndex(1); // Default 1k
|
||||||
row2->addWidget(comboRangeHP);
|
|
||||||
|
comboRLoad = new QComboBox(this);
|
||||||
|
comboRLoad->setToolTip("LPTIA Load Resistor (Rload)");
|
||||||
|
comboRLoad->addItem("10 Ω", 10);
|
||||||
|
comboRLoad->addItem("30 Ω", 30);
|
||||||
|
comboRLoad->addItem("50 Ω", 50);
|
||||||
|
comboRLoad->addItem("100 Ω", 100);
|
||||||
|
comboRLoad->setCurrentIndex(3); // Default 100R
|
||||||
|
|
||||||
calibrateBtn = new QPushButton("Calibrate HW", this);
|
calibrateBtn = new QPushButton("Calibrate HW", this);
|
||||||
calibrateBtn->setStyleSheet("background-color: rgba(255, 255, 0, 102); color: white; font-weight: bold;");
|
calibrateBtn->setStyleSheet("background-color: rgba(255, 255, 0, 102); color: white; font-weight: bold;");
|
||||||
row2->addWidget(calibrateBtn);
|
|
||||||
|
|
||||||
QFrame *sepRange = new QFrame(); sepRange->setFrameShape(QFrame::VLine); sepRange->setFrameShadow(QFrame::Sunken);
|
|
||||||
row2->addWidget(sepRange);
|
|
||||||
|
|
||||||
lblResultCond = new QLabel("Cond: -- µS/cm", this);
|
|
||||||
lblResultCond->setStyleSheet("font-weight: bold; color: #00FFFF; font-size: 14px; margin-right: 10px;");
|
|
||||||
row2->addWidget(lblResultCond);
|
|
||||||
|
|
||||||
QFrame *sep5 = new QFrame(); sep5->setFrameShape(QFrame::VLine); sep5->setFrameShadow(QFrame::Sunken);
|
|
||||||
row2->addWidget(sep5);
|
|
||||||
|
|
||||||
row2->addWidget(new QLabel("Freq:"));
|
|
||||||
spinFreq = new QDoubleSpinBox(this);
|
|
||||||
spinFreq->setRange(0.1, 200000.0); spinFreq->setValue(1000.0); spinFreq->setSuffix(" Hz");
|
|
||||||
row2->addWidget(spinFreq);
|
|
||||||
|
|
||||||
measureBtn = new QPushButton("Measure", this);
|
checkShortRe0Se0 = new QCheckBox("Short RE0-SE0", this);
|
||||||
|
checkShortRe0Se0->setToolTip("Internally short Reference and Sense electrodes (2-wire mode)");
|
||||||
|
|
||||||
|
hwLayout->addWidget(new QLabel("LP:"));
|
||||||
|
hwLayout->addWidget(comboRangeLP, 1);
|
||||||
|
hwLayout->addWidget(new QLabel("HP:"));
|
||||||
|
hwLayout->addWidget(comboRangeHP, 1);
|
||||||
|
hwLayout->addWidget(new QLabel("Rload:"));
|
||||||
|
hwLayout->addWidget(comboRLoad);
|
||||||
|
hwLayout->addWidget(checkShortRe0Se0);
|
||||||
|
hwLayout->addWidget(calibrateBtn);
|
||||||
|
globalLayout->addLayout(hwLayout);
|
||||||
|
|
||||||
|
mainLayout->addWidget(globalGroup);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 2. Main Tab Widget (Modes)
|
||||||
|
// ========================================================================
|
||||||
|
mainTabWidget = new QTabWidget(this);
|
||||||
|
|
||||||
|
// --- Tab 1: Impedance (Sweep & Single) ---
|
||||||
|
QWidget *impTab = new QWidget();
|
||||||
|
QVBoxLayout *impLayout = new QVBoxLayout(impTab);
|
||||||
|
impLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
|
||||||
|
// Controls Area
|
||||||
|
QGroupBox *impCtrlGroup = new QGroupBox("Impedance Controls", impTab);
|
||||||
|
QVBoxLayout *impCtrlLayout = new QVBoxLayout(impCtrlGroup);
|
||||||
|
|
||||||
|
// Sweep Row
|
||||||
|
QHBoxLayout *sweepLayout = new QHBoxLayout();
|
||||||
|
spinSweepStart = new QDoubleSpinBox(); spinSweepStart->setRange(0.1, 200000.0); spinSweepStart->setValue(1000.0); spinSweepStart->setSuffix(" Hz");
|
||||||
|
spinSweepStop = new QDoubleSpinBox(); spinSweepStop->setRange(0.1, 200000.0); spinSweepStop->setValue(200000.0); spinSweepStop->setSuffix(" Hz");
|
||||||
|
spinSweepPPD = new QSpinBox(); spinSweepPPD->setRange(1, 1000); spinSweepPPD->setValue(200); spinSweepPPD->setSuffix(" pts/dec");
|
||||||
|
sweepBtn = new QPushButton("Sweep");
|
||||||
|
sweepBtn->setStyleSheet("background-color: rgba(173, 216, 230, 76); color: white; font-weight: bold;");
|
||||||
|
|
||||||
|
sweepLayout->addWidget(new QLabel("Start:")); sweepLayout->addWidget(spinSweepStart);
|
||||||
|
sweepLayout->addWidget(new QLabel("Stop:")); sweepLayout->addWidget(spinSweepStop);
|
||||||
|
sweepLayout->addWidget(new QLabel("PPD:")); sweepLayout->addWidget(spinSweepPPD);
|
||||||
|
sweepLayout->addWidget(sweepBtn);
|
||||||
|
impCtrlLayout->addLayout(sweepLayout);
|
||||||
|
|
||||||
|
// Single Freq & Calibration Row
|
||||||
|
QHBoxLayout *singleLayout = new QHBoxLayout();
|
||||||
|
spinFreq = new QDoubleSpinBox(); spinFreq->setRange(0.1, 200000.0); spinFreq->setValue(1000.0); spinFreq->setSuffix(" Hz");
|
||||||
|
measureBtn = new QPushButton("Measure");
|
||||||
measureBtn->setStyleSheet("background-color: rgba(0, 100, 0, 76); color: white; font-weight: bold;");
|
measureBtn->setStyleSheet("background-color: rgba(0, 100, 0, 76); color: white; font-weight: bold;");
|
||||||
row2->addWidget(measureBtn);
|
|
||||||
|
checkShunt = new QCheckBox("Shunt");
|
||||||
|
spinShuntRes = new QDoubleSpinBox(); spinShuntRes->setRange(1.0, 1000000.0); spinShuntRes->setValue(466.0); spinShuntRes->setSuffix(" Ω");
|
||||||
|
|
||||||
|
spinCondStd = new QDoubleSpinBox(); spinCondStd->setRange(0.0, 1000000.0); spinCondStd->setValue(1413.0); spinCondStd->setSuffix(" µS/cm");
|
||||||
|
btnCalCond = new QPushButton("Cal K");
|
||||||
|
btnCalCond->setStyleSheet("background-color: rgba(255, 165, 0, 102); color: white; font-weight: bold;");
|
||||||
|
|
||||||
|
singleLayout->addWidget(new QLabel("Freq:")); singleLayout->addWidget(spinFreq);
|
||||||
|
singleLayout->addWidget(measureBtn);
|
||||||
|
singleLayout->addWidget(checkShunt); singleLayout->addWidget(spinShuntRes);
|
||||||
|
singleLayout->addWidget(new QLabel("Std:")); singleLayout->addWidget(spinCondStd);
|
||||||
|
singleLayout->addWidget(btnCalCond);
|
||||||
|
impCtrlLayout->addLayout(singleLayout);
|
||||||
|
|
||||||
|
// Results Row
|
||||||
|
QHBoxLayout *resLayout = new QHBoxLayout();
|
||||||
|
lblResultRs = new QLabel(" Rs: -- Ω");
|
||||||
|
lblResultRs->setStyleSheet("font-weight: bold; color: #FFD700; font-size: 14px;");
|
||||||
|
lblResultCond = new QLabel("Cond: -- µS/cm");
|
||||||
|
lblResultCond->setStyleSheet("font-weight: bold; color: #00FFFF; font-size: 14px;");
|
||||||
|
resLayout->addWidget(lblResultRs);
|
||||||
|
resLayout->addWidget(lblResultCond);
|
||||||
|
resLayout->addStretch();
|
||||||
|
impCtrlLayout->addLayout(resLayout);
|
||||||
|
|
||||||
QFrame *sep6 = new QFrame(); sep6->setFrameShape(QFrame::VLine); sep6->setFrameShadow(QFrame::Sunken);
|
impLayout->addWidget(impCtrlGroup);
|
||||||
row2->addWidget(sep6);
|
|
||||||
|
|
||||||
row2->addWidget(new QLabel("Bias:"));
|
// Graphs (Nested Tab)
|
||||||
spinAmpBias = new QDoubleSpinBox(this);
|
impGraphTabs = new QTabWidget();
|
||||||
spinAmpBias->setRange(-1100.0, 1100.0); spinAmpBias->setValue(0.0); spinAmpBias->setSuffix(" mV");
|
rawGraph = new GraphWidget(this);
|
||||||
row2->addWidget(spinAmpBias);
|
rawGraph->configureRawPlot();
|
||||||
|
nyquistGraph = new GraphWidget(this);
|
||||||
|
nyquistGraph->configureNyquistPlot();
|
||||||
|
|
||||||
|
impGraphTabs->addTab(rawGraph, "Bode Plot");
|
||||||
|
impGraphTabs->addTab(nyquistGraph, "Nyquist Plot");
|
||||||
|
impLayout->addWidget(impGraphTabs);
|
||||||
|
|
||||||
|
mainTabWidget->addTab(impTab, "Impedance");
|
||||||
|
|
||||||
row2->addWidget(new QLabel("LPF:"));
|
// --- Tab 2: Amperometry ---
|
||||||
comboLPF = new QComboBox(this);
|
QWidget *ampTab = new QWidget();
|
||||||
|
QVBoxLayout *ampLayout = new QVBoxLayout(ampTab);
|
||||||
|
ampLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
|
||||||
|
QGroupBox *ampCtrlGroup = new QGroupBox("Amperometry Controls", ampTab);
|
||||||
|
QHBoxLayout *ampCtrlLayout = new QHBoxLayout(ampCtrlGroup);
|
||||||
|
|
||||||
|
spinAmpBias = new QDoubleSpinBox(); spinAmpBias->setRange(-1100.0, 1100.0); spinAmpBias->setValue(0.0); spinAmpBias->setSuffix(" mV");
|
||||||
|
comboLPF = new QComboBox();
|
||||||
comboLPF->addItem("Bypass", 0);
|
comboLPF->addItem("Bypass", 0);
|
||||||
comboLPF->addItem("20kΩ (8Hz)", 1);
|
comboLPF->addItem("20kΩ (8Hz)", 1);
|
||||||
comboLPF->addItem("100kΩ (1.6Hz)", 2);
|
comboLPF->addItem("100kΩ (1.6Hz)", 2);
|
||||||
|
|
@ -187,106 +200,98 @@ void MainWindow::setupUi() {
|
||||||
comboLPF->addItem("400kΩ (0.4Hz)", 4);
|
comboLPF->addItem("400kΩ (0.4Hz)", 4);
|
||||||
comboLPF->addItem("600kΩ (0.26Hz)", 5);
|
comboLPF->addItem("600kΩ (0.26Hz)", 5);
|
||||||
comboLPF->addItem("1MΩ (0.16Hz)", 6);
|
comboLPF->addItem("1MΩ (0.16Hz)", 6);
|
||||||
comboLPF->setCurrentIndex(1); // Default 20k
|
comboLPF->setCurrentIndex(1);
|
||||||
row2->addWidget(comboLPF);
|
|
||||||
|
ampBtn = new QPushButton("Start Amp");
|
||||||
ampBtn = new QPushButton("Start Amp", this);
|
|
||||||
ampBtn->setStyleSheet("background-color: rgba(238, 130, 238, 51); color: white; font-weight: bold;");
|
ampBtn->setStyleSheet("background-color: rgba(238, 130, 238, 51); color: white; font-weight: bold;");
|
||||||
row2->addWidget(ampBtn);
|
|
||||||
|
|
||||||
QFrame *sep7 = new QFrame(); sep7->setFrameShape(QFrame::VLine); sep7->setFrameShadow(QFrame::Sunken);
|
|
||||||
row2->addWidget(sep7);
|
|
||||||
|
|
||||||
// LSV Controls
|
|
||||||
row2->addWidget(new QLabel("LSV:"));
|
|
||||||
spinLsvStart = new QDoubleSpinBox(this);
|
|
||||||
spinLsvStart->setRange(-1100.0, 1100.0); spinLsvStart->setValue(800.0); spinLsvStart->setSuffix(" mV");
|
|
||||||
row2->addWidget(spinLsvStart);
|
|
||||||
|
|
||||||
spinLsvStop = new QDoubleSpinBox(this);
|
ampCtrlLayout->addWidget(new QLabel("Bias:")); ampCtrlLayout->addWidget(spinAmpBias);
|
||||||
spinLsvStop->setRange(-1100.0, 1100.0); spinLsvStop->setValue(-200.0); spinLsvStop->setSuffix(" mV");
|
ampCtrlLayout->addWidget(new QLabel("LPF:")); ampCtrlLayout->addWidget(comboLPF);
|
||||||
row2->addWidget(spinLsvStop);
|
ampCtrlLayout->addWidget(ampBtn);
|
||||||
|
|
||||||
spinLsvSteps = new QSpinBox(this);
|
|
||||||
spinLsvSteps->setRange(10, 4000); spinLsvSteps->setValue(200); spinLsvSteps->setSuffix(" pts");
|
|
||||||
row2->addWidget(spinLsvSteps);
|
|
||||||
|
|
||||||
spinLsvDuration = new QSpinBox(this);
|
|
||||||
spinLsvDuration->setRange(100, 600000); spinLsvDuration->setValue(10000); spinLsvDuration->setSuffix(" ms");
|
|
||||||
row2->addWidget(spinLsvDuration);
|
|
||||||
|
|
||||||
lsvBlankBtn = new QPushButton("Run Blank", this);
|
|
||||||
lsvBlankBtn->setStyleSheet("background-color: rgba(0, 0, 0, 255); color: white; font-weight: bold;");
|
|
||||||
row2->addWidget(lsvBlankBtn);
|
|
||||||
|
|
||||||
lsvSampleBtn = new QPushButton("Run Sample", this);
|
ampLayout->addWidget(ampCtrlGroup);
|
||||||
lsvSampleBtn->setStyleSheet("background-color: rgba(75, 0, 130, 76); color: white; font-weight: bold;");
|
|
||||||
row2->addWidget(lsvSampleBtn);
|
|
||||||
|
|
||||||
row2->addStretch();
|
|
||||||
ctrlLayout->addLayout(row2);
|
|
||||||
|
|
||||||
mainLayout->addWidget(controlsContainer);
|
|
||||||
|
|
||||||
// --- Splitter for Graphs and Log ---
|
|
||||||
QSplitter *splitter = new QSplitter(Qt::Vertical, this);
|
|
||||||
|
|
||||||
tabWidget = new QTabWidget(this);
|
|
||||||
rawGraph = new GraphWidget(this);
|
|
||||||
nyquistGraph = new GraphWidget(this);
|
|
||||||
ampGraph = new GraphWidget(this);
|
ampGraph = new GraphWidget(this);
|
||||||
lsvGraph = new GraphWidget(this);
|
|
||||||
|
|
||||||
rawGraph->configureRawPlot();
|
|
||||||
nyquistGraph->configureNyquistPlot();
|
|
||||||
ampGraph->configureAmperometricPlot();
|
ampGraph->configureAmperometricPlot();
|
||||||
|
ampLayout->addWidget(ampGraph);
|
||||||
|
|
||||||
|
mainTabWidget->addTab(ampTab, "Amperometry");
|
||||||
|
|
||||||
|
// --- Tab 3: Voltammetry (LSV) ---
|
||||||
|
QWidget *lsvTab = new QWidget();
|
||||||
|
QVBoxLayout *lsvLayout = new QVBoxLayout(lsvTab);
|
||||||
|
lsvLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
|
||||||
|
QGroupBox *lsvCtrlGroup = new QGroupBox("LSV Controls", lsvTab);
|
||||||
|
QHBoxLayout *lsvCtrlLayout = new QHBoxLayout(lsvCtrlGroup);
|
||||||
|
|
||||||
|
spinLsvStart = new QDoubleSpinBox(); spinLsvStart->setRange(-1100.0, 1100.0); spinLsvStart->setValue(800.0); spinLsvStart->setSuffix(" mV");
|
||||||
|
spinLsvStop = new QDoubleSpinBox(); spinLsvStop->setRange(-1100.0, 1100.0); spinLsvStop->setValue(-200.0); spinLsvStop->setSuffix(" mV");
|
||||||
|
spinLsvSteps = new QSpinBox(); spinLsvSteps->setRange(10, 4000); spinLsvSteps->setValue(200); spinLsvSteps->setSuffix(" pts");
|
||||||
|
spinLsvDuration = new QSpinBox(); spinLsvDuration->setRange(100, 600000); spinLsvDuration->setValue(10000); spinLsvDuration->setSuffix(" ms");
|
||||||
|
|
||||||
|
lsvBlankBtn = new QPushButton("Run Blank");
|
||||||
|
lsvBlankBtn->setStyleSheet("background-color: rgba(0, 0, 0, 255); color: white; font-weight: bold;");
|
||||||
|
lsvSampleBtn = new QPushButton("Run Sample");
|
||||||
|
lsvSampleBtn->setStyleSheet("background-color: rgba(75, 0, 130, 76); color: white; font-weight: bold;");
|
||||||
|
|
||||||
|
lsvCtrlLayout->addWidget(new QLabel("Start:")); lsvCtrlLayout->addWidget(spinLsvStart);
|
||||||
|
lsvCtrlLayout->addWidget(new QLabel("Stop:")); lsvCtrlLayout->addWidget(spinLsvStop);
|
||||||
|
lsvCtrlLayout->addWidget(new QLabel("Steps:")); lsvCtrlLayout->addWidget(spinLsvSteps);
|
||||||
|
lsvCtrlLayout->addWidget(new QLabel("Time:")); lsvCtrlLayout->addWidget(spinLsvDuration);
|
||||||
|
lsvCtrlLayout->addWidget(lsvBlankBtn);
|
||||||
|
lsvCtrlLayout->addWidget(lsvSampleBtn);
|
||||||
|
|
||||||
|
lsvLayout->addWidget(lsvCtrlGroup);
|
||||||
|
|
||||||
|
lsvGraph = new GraphWidget(this);
|
||||||
lsvGraph->configureLSVPlot();
|
lsvGraph->configureLSVPlot();
|
||||||
|
lsvLayout->addWidget(lsvGraph);
|
||||||
|
|
||||||
|
mainTabWidget->addTab(lsvTab, "Voltammetry");
|
||||||
|
|
||||||
tabWidget->addTab(rawGraph, "Raw Data");
|
// ========================================================================
|
||||||
tabWidget->addTab(nyquistGraph, "Nyquist Plot");
|
// 3. Log Widget (Bottom)
|
||||||
tabWidget->addTab(ampGraph, "Amperometry");
|
// ========================================================================
|
||||||
tabWidget->addTab(lsvGraph, "Voltammetry");
|
|
||||||
|
|
||||||
logWidget = new QTextEdit(this);
|
logWidget = new QTextEdit(this);
|
||||||
logWidget->setReadOnly(true);
|
logWidget->setReadOnly(true);
|
||||||
logWidget->setFont(QFont("Monospace"));
|
logWidget->setFont(QFont("Monospace"));
|
||||||
logWidget->setPlaceholderText("Scanning for 0xCAFE EIS Device...");
|
logWidget->setPlaceholderText("Scanning for 0xCAFE EIS Device...");
|
||||||
logWidget->setStyleSheet("background-color: #1E1E1E; color: #00FF00; border: 1px solid #444;");
|
logWidget->setStyleSheet("background-color: #1E1E1E; color: #00FF00; border: 1px solid #444;");
|
||||||
|
logWidget->setMaximumHeight(150);
|
||||||
QScroller::grabGesture(logWidget->viewport(), QScroller::TouchGesture);
|
QScroller::grabGesture(logWidget->viewport(), QScroller::TouchGesture);
|
||||||
|
|
||||||
splitter->addWidget(tabWidget);
|
// Add to Main Layout
|
||||||
|
QSplitter *splitter = new QSplitter(Qt::Vertical, this);
|
||||||
|
splitter->addWidget(mainTabWidget);
|
||||||
splitter->addWidget(logWidget);
|
splitter->addWidget(logWidget);
|
||||||
splitter->setStretchFactor(0, 3);
|
splitter->setStretchFactor(0, 4);
|
||||||
splitter->setStretchFactor(1, 1);
|
splitter->setStretchFactor(1, 1);
|
||||||
|
|
||||||
mainLayout->addWidget(splitter);
|
mainLayout->addWidget(splitter);
|
||||||
|
|
||||||
// Initial State
|
// ========================================================================
|
||||||
|
// Initial State & Connections
|
||||||
|
// ========================================================================
|
||||||
checkIdBtn->setEnabled(false);
|
checkIdBtn->setEnabled(false);
|
||||||
calibrateBtn->setEnabled(false);
|
calibrateBtn->setEnabled(false);
|
||||||
sweepBtn->setEnabled(false);
|
checkShortRe0Se0->setEnabled(false);
|
||||||
measureBtn->setEnabled(false);
|
|
||||||
spinSweepStart->setEnabled(false);
|
|
||||||
spinSweepStop->setEnabled(false);
|
|
||||||
spinSweepPPD->setEnabled(false);
|
|
||||||
ampBtn->setEnabled(false);
|
|
||||||
spinAmpBias->setEnabled(false);
|
|
||||||
btnCalCond->setEnabled(false);
|
|
||||||
comboRangeLP->setEnabled(false);
|
comboRangeLP->setEnabled(false);
|
||||||
comboRangeHP->setEnabled(false);
|
comboRangeHP->setEnabled(false);
|
||||||
comboLPF->setEnabled(false);
|
comboRLoad->setEnabled(false);
|
||||||
lsvBlankBtn->setEnabled(false);
|
|
||||||
lsvSampleBtn->setEnabled(false);
|
// Disable all mode controls initially
|
||||||
spinLsvStart->setEnabled(false);
|
impTab->setEnabled(false);
|
||||||
spinLsvStop->setEnabled(false);
|
ampTab->setEnabled(false);
|
||||||
spinLsvSteps->setEnabled(false);
|
lsvTab->setEnabled(false);
|
||||||
spinLsvDuration->setEnabled(false);
|
|
||||||
|
|
||||||
// Connections
|
// Connections
|
||||||
connect(connectBtn, &QPushButton::clicked, this, &MainWindow::connectToPort);
|
connect(connectBtn, &QPushButton::clicked, this, &MainWindow::connectToPort);
|
||||||
connect(refreshBtn, &QPushButton::clicked, this, &MainWindow::refreshPorts);
|
connect(refreshBtn, &QPushButton::clicked, this, &MainWindow::refreshPorts);
|
||||||
connect(checkIdBtn, &QPushButton::clicked, this, &MainWindow::checkDeviceId);
|
connect(checkIdBtn, &QPushButton::clicked, this, &MainWindow::checkDeviceId);
|
||||||
connect(calibrateBtn, &QPushButton::clicked, this, &MainWindow::runCalibration);
|
connect(calibrateBtn, &QPushButton::clicked, this, &MainWindow::runCalibration);
|
||||||
|
connect(checkShortRe0Se0, &QCheckBox::toggled, this, &MainWindow::onShortRe0Se0Toggled);
|
||||||
|
|
||||||
connect(sweepBtn, &QPushButton::clicked, this, &MainWindow::startSweep);
|
connect(sweepBtn, &QPushButton::clicked, this, &MainWindow::startSweep);
|
||||||
connect(measureBtn, &QPushButton::clicked, this, &MainWindow::toggleMeasurement);
|
connect(measureBtn, &QPushButton::clicked, this, &MainWindow::toggleMeasurement);
|
||||||
connect(ampBtn, &QPushButton::clicked, this, &MainWindow::toggleAmperometry);
|
connect(ampBtn, &QPushButton::clicked, this, &MainWindow::toggleAmperometry);
|
||||||
|
|
@ -294,18 +299,18 @@ void MainWindow::setupUi() {
|
||||||
connect(lsvSampleBtn, &QPushButton::clicked, this, &MainWindow::startLSVSample);
|
connect(lsvSampleBtn, &QPushButton::clicked, this, &MainWindow::startLSVSample);
|
||||||
connect(btnCalCond, &QPushButton::clicked, this, &MainWindow::calibrateCellConstant);
|
connect(btnCalCond, &QPushButton::clicked, this, &MainWindow::calibrateCellConstant);
|
||||||
connect(comboLPF, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onLPFChanged);
|
connect(comboLPF, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onLPFChanged);
|
||||||
|
connect(comboRLoad, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onRLoadChanged);
|
||||||
connect(tabWidget, &QTabWidget::currentChanged, this, [this](int index){
|
|
||||||
|
connect(mainTabWidget, &QTabWidget::currentChanged, this, [this](int index){
|
||||||
if (index == 0) rawGraph->configureRawPlot();
|
if (index == 0) rawGraph->configureRawPlot();
|
||||||
else if (index == 1) nyquistGraph->configureNyquistPlot();
|
else if (index == 1) ampGraph->configureAmperometricPlot();
|
||||||
else if (index == 2) ampGraph->configureAmperometricPlot();
|
else if (index == 2) lsvGraph->configureLSVPlot();
|
||||||
else if (index == 3) lsvGraph->configureLSVPlot();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setButtonBlinking(QPushButton *btn, bool blinking) {
|
void MainWindow::setButtonBlinking(QPushButton *btn, bool blinking) {
|
||||||
if (activeButton && activeButton != btn) {
|
if (activeButton && activeButton != btn) {
|
||||||
// Restore original colors manually
|
// Restore original colors manually based on button type
|
||||||
if (activeButton == sweepBtn) activeButton->setStyleSheet("background-color: rgba(173, 216, 230, 76); color: white; font-weight: bold;");
|
if (activeButton == sweepBtn) activeButton->setStyleSheet("background-color: rgba(173, 216, 230, 76); color: white; font-weight: bold;");
|
||||||
else if (activeButton == measureBtn) activeButton->setStyleSheet("background-color: rgba(0, 100, 0, 76); color: white; font-weight: bold;");
|
else if (activeButton == measureBtn) activeButton->setStyleSheet("background-color: rgba(0, 100, 0, 76); color: white; font-weight: bold;");
|
||||||
else if (activeButton == ampBtn) activeButton->setStyleSheet("background-color: rgba(238, 130, 238, 51); color: white; font-weight: bold;");
|
else if (activeButton == ampBtn) activeButton->setStyleSheet("background-color: rgba(238, 130, 238, 51); color: white; font-weight: bold;");
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,28 @@
|
||||||
// host/src/main.cpp
|
// host/src/main.cpp
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
|
#include <QPermissions>
|
||||||
|
#include <QDebug>
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
// Helper to request permissions sequentially
|
||||||
|
void requestAndroidPermissions() {
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
// Request Location Permission
|
||||||
|
// Required for hardware discovery (BLE/WiFi) and sometimes USB device enumeration on Android.
|
||||||
|
// Note: Storage permissions are handled via the Manifest and Scoped Storage;
|
||||||
|
// explicit runtime requests for storage are often not needed for AppData folders.
|
||||||
|
|
||||||
|
qApp->requestPermission(QLocationPermission{}, [](const QPermission &permission) {
|
||||||
|
if (permission.status() == Qt::PermissionStatus::Granted) {
|
||||||
|
qDebug() << "Location Permission Granted";
|
||||||
|
} else {
|
||||||
|
qWarning() << "Location Permission Denied";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// High DPI Scaling
|
// High DPI Scaling
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
@ -62,6 +82,9 @@ int main(int argc, char *argv[]) {
|
||||||
font.setPointSize(12);
|
font.setPointSize(12);
|
||||||
app.setFont(font);
|
app.setFont(font);
|
||||||
|
|
||||||
|
// Request Android Permissions at startup
|
||||||
|
requestAndroidPermissions();
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
|
|
|
||||||
594
main.c
594
main.c
|
|
@ -1,568 +1,20 @@
|
||||||
// File: main.c
|
// File: main.c
|
||||||
#include <stdio.h>
|
#include "App_Common.h"
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include "hardware/spi.h"
|
|
||||||
#include "hardware/gpio.h"
|
|
||||||
#include "hardware/watchdog.h"
|
|
||||||
#include "ad5940.h"
|
|
||||||
#include "Impedance.h"
|
|
||||||
#include "Amperometric.h"
|
|
||||||
#include "RampTest.h"
|
|
||||||
#include "Reset.h"
|
|
||||||
|
|
||||||
#define AD5940ERR_STOP 10
|
// --- Global Variables ---
|
||||||
|
|
||||||
// Fix for missing definition in some SDK versions
|
|
||||||
#ifndef LPTIARF_BYPASS
|
|
||||||
#define LPTIARF_BYPASS 0x2000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Hardware Definitions
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
#define PIN_MISO 0
|
|
||||||
#define PIN_CS 1
|
|
||||||
#define PIN_SCK 2
|
|
||||||
#define PIN_MOSI 3
|
|
||||||
#define PIN_RST 9
|
|
||||||
#define PIN_INT 29
|
|
||||||
|
|
||||||
#define APPBUFF_SIZE 512
|
|
||||||
uint32_t AppBuff[APPBUFF_SIZE];
|
uint32_t AppBuff[APPBUFF_SIZE];
|
||||||
|
|
||||||
// Application State
|
|
||||||
typedef enum {
|
|
||||||
MODE_IDLE,
|
|
||||||
MODE_IMPEDANCE,
|
|
||||||
MODE_AMPEROMETRIC,
|
|
||||||
MODE_RAMP
|
|
||||||
} AppMode;
|
|
||||||
|
|
||||||
AppMode CurrentMode = MODE_IDLE;
|
AppMode CurrentMode = MODE_IDLE;
|
||||||
float LFOSCFreq = 32000.0;
|
float LFOSCFreq = 32000.0;
|
||||||
|
|
||||||
// Global Indices for Data Reporting (Reset on new run)
|
|
||||||
uint32_t g_AmpIndex = 0;
|
uint32_t g_AmpIndex = 0;
|
||||||
uint32_t g_RampIndex = 0;
|
uint32_t g_RampIndex = 0;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
uint32_t ConfigLptiaVal = 1000;
|
||||||
// Range / RTIA Management
|
uint32_t ConfigHstiaVal = 1000;
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Current Configuration
|
|
||||||
uint32_t ConfigLptiaVal = 1000; // Default LP: 1k
|
|
||||||
uint32_t ConfigHstiaVal = 1000; // Default HP: 1k
|
|
||||||
uint32_t CurrentLpTiaRf = LPTIARF_20K;
|
uint32_t CurrentLpTiaRf = LPTIARF_20K;
|
||||||
|
uint32_t ConfigRLoad = LPTIARLOAD_100R; // Default 100R
|
||||||
// Calibrated Values
|
|
||||||
float CalibratedLptiaVal = 1000.0f;
|
float CalibratedLptiaVal = 1000.0f;
|
||||||
float CalibratedHstiaVal = 1000.0f;
|
float CalibratedHstiaVal = 1000.0f;
|
||||||
|
BoolFlag GlobalShortRe0Se0 = bFALSE;
|
||||||
// Map integer value to HSTIA Enum (Impedance) - Limited Selection
|
|
||||||
uint32_t GetHSTIARtia(uint32_t val) {
|
|
||||||
switch(val) {
|
|
||||||
case 200: return HSTIARTIA_200;
|
|
||||||
case 1000: return HSTIARTIA_1K;
|
|
||||||
case 5000: return HSTIARTIA_5K;
|
|
||||||
case 10000: return HSTIARTIA_10K;
|
|
||||||
case 20000: return HSTIARTIA_20K;
|
|
||||||
case 40000: return HSTIARTIA_40K;
|
|
||||||
case 80000: return HSTIARTIA_80K;
|
|
||||||
case 160000: return HSTIARTIA_160K;
|
|
||||||
default: return HSTIARTIA_1K;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map integer value to LPTIA Enum (Amperometric/Ramp) - Massive Selection
|
|
||||||
uint32_t GetLPTIARtia(uint32_t val) {
|
|
||||||
switch(val) {
|
|
||||||
case 200: return LPTIARTIA_200R;
|
|
||||||
case 1000: return LPTIARTIA_1K;
|
|
||||||
case 2000: return LPTIARTIA_2K;
|
|
||||||
case 3000: return LPTIARTIA_3K;
|
|
||||||
case 4000: return LPTIARTIA_4K;
|
|
||||||
case 6000: return LPTIARTIA_6K;
|
|
||||||
case 8000: return LPTIARTIA_8K;
|
|
||||||
case 10000: return LPTIARTIA_10K;
|
|
||||||
case 12000: return LPTIARTIA_12K;
|
|
||||||
case 16000: return LPTIARTIA_16K;
|
|
||||||
case 20000: return LPTIARTIA_20K;
|
|
||||||
case 24000: return LPTIARTIA_24K;
|
|
||||||
case 30000: return LPTIARTIA_30K;
|
|
||||||
case 32000: return LPTIARTIA_32K;
|
|
||||||
case 40000: return LPTIARTIA_40K;
|
|
||||||
case 48000: return LPTIARTIA_48K;
|
|
||||||
case 64000: return LPTIARTIA_64K;
|
|
||||||
case 85000: return LPTIARTIA_85K;
|
|
||||||
case 96000: return LPTIARTIA_96K;
|
|
||||||
case 100000: return LPTIARTIA_100K;
|
|
||||||
case 120000: return LPTIARTIA_120K;
|
|
||||||
case 128000: return LPTIARTIA_128K;
|
|
||||||
case 160000: return LPTIARTIA_160K;
|
|
||||||
case 196000: return LPTIARTIA_196K;
|
|
||||||
case 256000: return LPTIARTIA_256K;
|
|
||||||
case 512000: return LPTIARTIA_512K;
|
|
||||||
default: return LPTIARTIA_1K;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine correct internal RLOAD based on RTIA selection
|
|
||||||
uint32_t GetLPTIARload(uint32_t val) {
|
|
||||||
if (val <= 200) return LPTIARLOAD_10R;
|
|
||||||
return LPTIARLOAD_100R;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Platform Interface Implementation
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
void AD5940_CsClr(void) { gpio_put(PIN_CS, 0); }
|
|
||||||
void AD5940_CsSet(void) { gpio_put(PIN_CS, 1); }
|
|
||||||
void AD5940_RstClr(void) { gpio_put(PIN_RST, 0); }
|
|
||||||
void AD5940_RstSet(void) { gpio_put(PIN_RST, 1); }
|
|
||||||
void AD5940_Delay10us(uint32_t time) { sleep_us(time * 10); }
|
|
||||||
void AD5940_ReadWriteNBytes(unsigned char *pSendBuffer, unsigned char *pRecvBuff, unsigned long length) {
|
|
||||||
spi_write_read_blocking(spi0, pSendBuffer, pRecvBuff, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t AD5940_GetMCUIntFlag(void) {
|
|
||||||
return (gpio_get(PIN_INT) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t AD5940_ClrMCUIntFlag(void) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t AD5940_MCUResourceInit(void *pCfg) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AD5940_MCUGpioWrite(uint32_t data) { (void)data; }
|
|
||||||
uint32_t AD5940_MCUGpioRead(uint32_t pin) { (void)pin; return 0; }
|
|
||||||
void AD5940_MCUGpioCtrl(uint32_t pin, BoolFlag enable) { (void)pin; (void)enable; }
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Application Logic
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void setup_pins(void) {
|
|
||||||
// Run SPI at 16 MHz
|
|
||||||
spi_init(spi0, 16000000);
|
|
||||||
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_RST);
|
|
||||||
gpio_set_dir(PIN_RST, GPIO_OUT);
|
|
||||||
gpio_put(PIN_RST, 1);
|
|
||||||
|
|
||||||
gpio_init(PIN_INT);
|
|
||||||
gpio_set_dir(PIN_INT, GPIO_IN);
|
|
||||||
gpio_pull_up(PIN_INT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AD5940ImpedanceStructInit(void)
|
|
||||||
{
|
|
||||||
AppIMPCfg_Type *pImpedanceCfg;
|
|
||||||
AppIMPGetCfg(&pImpedanceCfg);
|
|
||||||
|
|
||||||
// --- CRITICAL: Force re-initialization of sequences ---
|
|
||||||
pImpedanceCfg->IMPInited = bFALSE;
|
|
||||||
// ------------------------------------------------------
|
|
||||||
|
|
||||||
pImpedanceCfg->SeqStartAddr = 0;
|
|
||||||
pImpedanceCfg->MaxSeqLen = 512;
|
|
||||||
pImpedanceCfg->RcalVal = 100.0;
|
|
||||||
pImpedanceCfg->RtiaVal = CalibratedHstiaVal;
|
|
||||||
pImpedanceCfg->SinFreq = 1000.0;
|
|
||||||
pImpedanceCfg->FifoThresh = 6;
|
|
||||||
pImpedanceCfg->DacVoltPP = 600.0;
|
|
||||||
pImpedanceCfg->ExcitBufGain = EXCITBUFGAIN_0P25;
|
|
||||||
pImpedanceCfg->HsDacGain = HSDACGAIN_0P2;
|
|
||||||
pImpedanceCfg->HsDacUpdateRate = 0;
|
|
||||||
pImpedanceCfg->DswitchSel = SWD_CE0;
|
|
||||||
pImpedanceCfg->PswitchSel = SWP_CE0;
|
|
||||||
pImpedanceCfg->NswitchSel = SWN_SE0;
|
|
||||||
pImpedanceCfg->TswitchSel = SWT_SE0LOAD;
|
|
||||||
pImpedanceCfg->HstiaRtiaSel = GetHSTIARtia(ConfigHstiaVal);
|
|
||||||
pImpedanceCfg->BiasVolt = 0.0;
|
|
||||||
pImpedanceCfg->SweepCfg.SweepEn = bFALSE;
|
|
||||||
pImpedanceCfg->SweepCfg.SweepStart = 100.0f;
|
|
||||||
pImpedanceCfg->SweepCfg.SweepStop = 100000.0f;
|
|
||||||
pImpedanceCfg->SweepCfg.SweepPoints = 50;
|
|
||||||
pImpedanceCfg->SweepCfg.SweepLog = bTRUE;
|
|
||||||
pImpedanceCfg->PwrMod = AFEPWR_LP;
|
|
||||||
pImpedanceCfg->ADCSinc3Osr = ADCSINC3OSR_4;
|
|
||||||
pImpedanceCfg->DftNum = DFTNUM_16384;
|
|
||||||
pImpedanceCfg->DftSrc = DFTSRC_SINC3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AD5940AMPStructInit(void)
|
|
||||||
{
|
|
||||||
AppAMPCfg_Type *pAMPCfg;
|
|
||||||
AppAMPGetCfg(&pAMPCfg);
|
|
||||||
|
|
||||||
// --- CRITICAL: Force re-initialization of sequences ---
|
|
||||||
pAMPCfg->AMPInited = bFALSE;
|
|
||||||
// ------------------------------------------------------
|
|
||||||
|
|
||||||
pAMPCfg->WuptClkFreq = LFOSCFreq;
|
|
||||||
pAMPCfg->SeqStartAddr = 0;
|
|
||||||
pAMPCfg->MaxSeqLen = 512;
|
|
||||||
pAMPCfg->RcalVal = 100.0;
|
|
||||||
pAMPCfg->NumOfData = -1;
|
|
||||||
pAMPCfg->AmpODR = 1.0;
|
|
||||||
pAMPCfg->FifoThresh = 4;
|
|
||||||
pAMPCfg->SensorBias = 0;
|
|
||||||
pAMPCfg->LptiaRtiaSel = GetLPTIARtia(ConfigLptiaVal);
|
|
||||||
pAMPCfg->LpTiaRl = GetLPTIARload(ConfigLptiaVal);
|
|
||||||
pAMPCfg->LpTiaRf = CurrentLpTiaRf;
|
|
||||||
pAMPCfg->Vzero = 1100;
|
|
||||||
pAMPCfg->ADCRefVolt = 1.82;
|
|
||||||
pAMPCfg->RtiaCalValue.Magnitude = CalibratedLptiaVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AD5940RampStructInit(void)
|
|
||||||
{
|
|
||||||
AppRAMPCfg_Type *pRampCfg;
|
|
||||||
AppRAMPGetCfg(&pRampCfg);
|
|
||||||
|
|
||||||
// --- CRITICAL: Force re-initialization of sequences ---
|
|
||||||
pRampCfg->RAMPInited = bFALSE;
|
|
||||||
// ------------------------------------------------------
|
|
||||||
|
|
||||||
pRampCfg->SeqStartAddr = 0;
|
|
||||||
pRampCfg->MaxSeqLen = 1024;
|
|
||||||
pRampCfg->RcalVal = 100.0;
|
|
||||||
pRampCfg->ADCRefVolt = 1820.0f;
|
|
||||||
pRampCfg->FifoThresh = 4;
|
|
||||||
pRampCfg->SysClkFreq = 16000000.0f;
|
|
||||||
pRampCfg->LFOSCClkFreq = LFOSCFreq;
|
|
||||||
|
|
||||||
pRampCfg->RampStartVolt = -500.0f;
|
|
||||||
pRampCfg->RampPeakVolt = +500.0f;
|
|
||||||
pRampCfg->VzeroStart = 1100.0f;
|
|
||||||
pRampCfg->VzeroPeak = 1100.0f;
|
|
||||||
pRampCfg->StepNumber = 100;
|
|
||||||
pRampCfg->RampDuration = 10000;
|
|
||||||
pRampCfg->SampleDelay = 1.0f;
|
|
||||||
|
|
||||||
pRampCfg->LPTIARtiaSel = GetLPTIARtia(ConfigLptiaVal);
|
|
||||||
pRampCfg->LPTIARloadSel = GetLPTIARload(ConfigLptiaVal);
|
|
||||||
pRampCfg->LpTiaRf = CurrentLpTiaRf;
|
|
||||||
pRampCfg->AdcPgaGain = ADCPGA_1P5;
|
|
||||||
pRampCfg->RtiaValue.Magnitude = CalibratedLptiaVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Configures the AD5940 Platform (Clocks, FIFO, GPIO).
|
|
||||||
* This must be called after every Reset.
|
|
||||||
*/
|
|
||||||
static int32_t AD5940PlatformCfg(void)
|
|
||||||
{
|
|
||||||
CLKCfg_Type clk_cfg;
|
|
||||||
FIFOCfg_Type fifo_cfg;
|
|
||||||
AGPIOCfg_Type gpio_cfg;
|
|
||||||
|
|
||||||
// Note: HWReset and Initialize are now handled by the caller (System Init or SoftReset)
|
|
||||||
|
|
||||||
clk_cfg.ADCClkDiv = ADCCLKDIV_1;
|
|
||||||
clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
|
|
||||||
clk_cfg.SysClkDiv = SYSCLKDIV_1;
|
|
||||||
clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
|
|
||||||
clk_cfg.HfOSC32MHzMode = bFALSE;
|
|
||||||
clk_cfg.HFOSCEn = bTRUE;
|
|
||||||
clk_cfg.HFXTALEn = bFALSE;
|
|
||||||
clk_cfg.LFOSCEn = bTRUE;
|
|
||||||
AD5940_CLKCfg(&clk_cfg);
|
|
||||||
|
|
||||||
fifo_cfg.FIFOEn = bFALSE;
|
|
||||||
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
|
|
||||||
fifo_cfg.FIFOSize = FIFOSIZE_4KB;
|
|
||||||
fifo_cfg.FIFOSrc = FIFOSRC_DFT;
|
|
||||||
fifo_cfg.FIFOThresh = 6;
|
|
||||||
AD5940_FIFOCfg(&fifo_cfg);
|
|
||||||
fifo_cfg.FIFOEn = bTRUE;
|
|
||||||
AD5940_FIFOCfg(&fifo_cfg);
|
|
||||||
|
|
||||||
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);
|
|
||||||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
|
||||||
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH|AFEINTSRC_CUSTOMINT0, bTRUE);
|
|
||||||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
|
||||||
|
|
||||||
gpio_cfg.FuncSet = GP0_INT;
|
|
||||||
gpio_cfg.InputEnSet = 0;
|
|
||||||
gpio_cfg.OutputEnSet = AGPIO_Pin0;
|
|
||||||
gpio_cfg.OutVal = 0;
|
|
||||||
gpio_cfg.PullEnSet = 0;
|
|
||||||
AD5940_AGPIOCfg(&gpio_cfg);
|
|
||||||
|
|
||||||
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Performs a full system reset and re-configuration.
|
|
||||||
* Call this after a measurement sequence finishes to park the device in a clean state.
|
|
||||||
*/
|
|
||||||
void SystemReset(void) {
|
|
||||||
sleep_ms(100); // Allow printf buffers to flush
|
|
||||||
AD5940_SoftReset();
|
|
||||||
AD5940PlatformCfg();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImpedanceShowResult(uint32_t *pData, uint32_t DataCount)
|
|
||||||
{
|
|
||||||
float freq;
|
|
||||||
fImpPol_Type *pImp = (fImpPol_Type*)pData;
|
|
||||||
AppIMPCtrl(IMPCTRL_GETFREQ, &freq);
|
|
||||||
|
|
||||||
for(int i=0;i<DataCount;i++)
|
|
||||||
{
|
|
||||||
float mag = pImp[i].Magnitude;
|
|
||||||
float phase = pImp[i].Phase;
|
|
||||||
float real = mag * cosf(phase);
|
|
||||||
float imag = mag * sinf(phase);
|
|
||||||
|
|
||||||
printf("DATA,%.2f,%.4f,%.4f,%.4f,%.4f\n", freq, mag, phase * 180.0f / MATH_PI, real, imag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AmperometricShowResult(float *pData, uint32_t DataCount)
|
|
||||||
{
|
|
||||||
for(int i=0;i<DataCount;i++)
|
|
||||||
{
|
|
||||||
printf("AMP,%d,%.4f\n", g_AmpIndex++, pData[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RampShowResult(float *pData, uint32_t DataCount)
|
|
||||||
{
|
|
||||||
for(int i=0;i<DataCount;i++)
|
|
||||||
{
|
|
||||||
printf("RAMP,%d,%.4f\n", g_RampIndex++, pData[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// High-Level Routines
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void Routine_CalibrateLFO(void) {
|
|
||||||
printf(">> Calibrating LFOSC...\n");
|
|
||||||
|
|
||||||
// Reset before calibration to ensure clean state
|
|
||||||
SystemReset();
|
|
||||||
|
|
||||||
LFOSCMeasure_Type cal_cfg;
|
|
||||||
cal_cfg.CalDuration = 1000.0;
|
|
||||||
cal_cfg.CalSeqAddr = 0;
|
|
||||||
cal_cfg.SystemClkFreq = 16000000.0;
|
|
||||||
|
|
||||||
if(AD5940_LFOSCMeasure(&cal_cfg, &LFOSCFreq) == AD5940ERR_OK) {
|
|
||||||
printf(">> LFOSC Calibrated: %.2f Hz\n", LFOSCFreq);
|
|
||||||
} else {
|
|
||||||
printf(">> LFOSC Calibration Failed.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: We do NOT reset here. We want to keep the state for the next step.
|
|
||||||
}
|
|
||||||
|
|
||||||
void Routine_Measure(float freq) {
|
|
||||||
CurrentMode = MODE_IMPEDANCE;
|
|
||||||
|
|
||||||
// NO RESET HERE. We assume the device is ready (possibly calibrated).
|
|
||||||
|
|
||||||
AppIMPCfg_Type *pCfg;
|
|
||||||
AppIMPGetCfg(&pCfg);
|
|
||||||
|
|
||||||
AD5940ImpedanceStructInit();
|
|
||||||
|
|
||||||
pCfg->WuptClkFreq = LFOSCFreq;
|
|
||||||
pCfg->SweepCfg.SweepEn = bFALSE;
|
|
||||||
pCfg->SinFreq = freq;
|
|
||||||
pCfg->NumOfData = -1;
|
|
||||||
pCfg->RealDataCount = -1;
|
|
||||||
pCfg->bParaChanged = bTRUE;
|
|
||||||
|
|
||||||
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
|
||||||
AppIMPCtrl(IMPCTRL_START, 0);
|
|
||||||
} else {
|
|
||||||
printf("ERROR: Init Failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Routine_Sweep(float start, float end, int steps) {
|
|
||||||
CurrentMode = MODE_IMPEDANCE;
|
|
||||||
|
|
||||||
// NO RESET HERE. We assume the device is ready (possibly calibrated).
|
|
||||||
|
|
||||||
AppIMPCfg_Type *pCfg;
|
|
||||||
AppIMPGetCfg(&pCfg);
|
|
||||||
|
|
||||||
AD5940ImpedanceStructInit();
|
|
||||||
|
|
||||||
pCfg->WuptClkFreq = LFOSCFreq;
|
|
||||||
pCfg->SweepCfg.SweepEn = bTRUE;
|
|
||||||
pCfg->SweepCfg.SweepStart = start;
|
|
||||||
pCfg->SweepCfg.SweepStop = end;
|
|
||||||
pCfg->SweepCfg.SweepPoints = steps + 1;
|
|
||||||
pCfg->NumOfData = steps + 1;
|
|
||||||
pCfg->RealDataCount = steps;
|
|
||||||
pCfg->SweepCfg.SweepLog = bTRUE;
|
|
||||||
pCfg->bParaChanged = bTRUE;
|
|
||||||
|
|
||||||
if(AppIMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
|
||||||
AppIMPCtrl(IMPCTRL_START, 0);
|
|
||||||
} else {
|
|
||||||
printf("ERROR: Init Failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Routine_Amperometric(float bias_mv) {
|
|
||||||
CurrentMode = MODE_AMPEROMETRIC;
|
|
||||||
g_AmpIndex = 0; // Reset Index
|
|
||||||
|
|
||||||
// NO RESET HERE.
|
|
||||||
|
|
||||||
printf(">> Starting Amperometry (Bias: %.1f mV, LP Range: %d)...\n", bias_mv, ConfigLptiaVal);
|
|
||||||
|
|
||||||
AppAMPCfg_Type *pCfg;
|
|
||||||
AppAMPGetCfg(&pCfg);
|
|
||||||
AD5940AMPStructInit();
|
|
||||||
pCfg->SensorBias = bias_mv;
|
|
||||||
pCfg->ReDoRtiaCal = bFALSE;
|
|
||||||
|
|
||||||
if(AppAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
|
||||||
AppAMPCtrl(AMPCTRL_START, 0);
|
|
||||||
} else {
|
|
||||||
printf("ERROR: AMP Init Failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Routine_LSV(float start_mv, float end_mv, int steps, int duration_ms) {
|
|
||||||
CurrentMode = MODE_RAMP;
|
|
||||||
g_RampIndex = 0; // Reset Index
|
|
||||||
|
|
||||||
// NO RESET HERE.
|
|
||||||
|
|
||||||
printf(">> Starting LSV (%.1f to %.1f mV, %d steps, %d ms, LP Range: %d)...\n", start_mv, end_mv, steps, duration_ms, ConfigLptiaVal);
|
|
||||||
|
|
||||||
AppRAMPCfg_Type *pCfg;
|
|
||||||
AppRAMPGetCfg(&pCfg);
|
|
||||||
AD5940RampStructInit();
|
|
||||||
|
|
||||||
pCfg->RampStartVolt = start_mv;
|
|
||||||
pCfg->RampPeakVolt = end_mv;
|
|
||||||
pCfg->StepNumber = steps;
|
|
||||||
pCfg->RampDuration = duration_ms;
|
|
||||||
pCfg->bRampOneDir = bTRUE;
|
|
||||||
pCfg->bParaChanged = bTRUE;
|
|
||||||
|
|
||||||
if(AppRAMPInit(AppBuff, APPBUFF_SIZE) == AD5940ERR_OK) {
|
|
||||||
AppRAMPCtrl(APPCTRL_START, 0);
|
|
||||||
} else {
|
|
||||||
printf("ERROR: RAMP Init Failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Routine_CalibrateSystem(void) {
|
|
||||||
CurrentMode = MODE_IMPEDANCE;
|
|
||||||
|
|
||||||
// NO RESET HERE. We want to keep the state from LFO calibration (if any)
|
|
||||||
// and definitely NOT reset after we are done.
|
|
||||||
|
|
||||||
AppIMPCfg_Type *pCfg;
|
|
||||||
AppIMPGetCfg(&pCfg);
|
|
||||||
|
|
||||||
ADCPGACal_Type adcpga_cal;
|
|
||||||
adcpga_cal.AdcClkFreq = 16000000.0;
|
|
||||||
adcpga_cal.SysClkFreq = 16000000.0;
|
|
||||||
adcpga_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
|
||||||
adcpga_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
|
||||||
adcpga_cal.ADCPga = ADCPGA_1P5;
|
|
||||||
adcpga_cal.PGACalType = PGACALTYPE_OFFSET;
|
|
||||||
adcpga_cal.TimeOut10us = 1000;
|
|
||||||
adcpga_cal.VRef1p11 = 1.11;
|
|
||||||
adcpga_cal.VRef1p82 = 1.82;
|
|
||||||
printf(">> Calibrating ADC Offset...\n");
|
|
||||||
AD5940_ADCPGACal(&adcpga_cal);
|
|
||||||
|
|
||||||
// --- 1. Calibrate LPTIA (Low Power Loop) ---
|
|
||||||
LPRTIACal_Type lprtia_cal;
|
|
||||||
fImpPol_Type LpRes;
|
|
||||||
memset(&lprtia_cal, 0, sizeof(lprtia_cal));
|
|
||||||
lprtia_cal.AdcClkFreq = 16000000.0;
|
|
||||||
lprtia_cal.SysClkFreq = 16000000.0;
|
|
||||||
lprtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
|
||||||
lprtia_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
|
||||||
lprtia_cal.bPolarResult = bTRUE;
|
|
||||||
lprtia_cal.fRcal = 100.0;
|
|
||||||
lprtia_cal.LpTiaRtia = GetLPTIARtia(ConfigLptiaVal);
|
|
||||||
lprtia_cal.LpAmpPwrMod = LPAMPPWR_NORM;
|
|
||||||
lprtia_cal.bWithCtia = bFALSE;
|
|
||||||
lprtia_cal.fFreq = 100.0f;
|
|
||||||
lprtia_cal.DftCfg.DftNum = DFTNUM_2048;
|
|
||||||
lprtia_cal.DftCfg.DftSrc = DFTSRC_SINC3;
|
|
||||||
lprtia_cal.DftCfg.HanWinEn = bTRUE;
|
|
||||||
|
|
||||||
printf(">> Calibrating LPTIA %d Ohm...\n", ConfigLptiaVal);
|
|
||||||
if (AD5940_LPRtiaCal(&lprtia_cal, &LpRes) == AD5940ERR_OK) {
|
|
||||||
printf("Calibrated LPTIA: Mag = %f Ohm, Phase = %f\n", LpRes.Magnitude, LpRes.Phase);
|
|
||||||
CalibratedLptiaVal = LpRes.Magnitude;
|
|
||||||
} else {
|
|
||||||
printf("LPTIA Calibration Failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 2. Calibrate HSTIA (High Speed Loop) ---
|
|
||||||
HSDACCfg_Type hsdac_cfg;
|
|
||||||
hsdac_cfg.ExcitBufGain = EXCITBUFGAIN_0P25;
|
|
||||||
hsdac_cfg.HsDacGain = HSDACGAIN_0P2;
|
|
||||||
hsdac_cfg.HsDacUpdateRate = 7;
|
|
||||||
AD5940_HSDacCfgS(&hsdac_cfg);
|
|
||||||
|
|
||||||
HSRTIACal_Type hsrtia_cal;
|
|
||||||
fImpPol_Type HsRes;
|
|
||||||
memset(&hsrtia_cal, 0, sizeof(hsrtia_cal));
|
|
||||||
hsrtia_cal.fFreq = 1000.0f;
|
|
||||||
hsrtia_cal.AdcClkFreq = 16000000.0;
|
|
||||||
hsrtia_cal.SysClkFreq = 16000000.0;
|
|
||||||
hsrtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
|
|
||||||
hsrtia_cal.ADCSinc2Osr = ADCSINC2OSR_22;
|
|
||||||
hsrtia_cal.bPolarResult = bTRUE;
|
|
||||||
hsrtia_cal.fRcal = 100.0;
|
|
||||||
hsrtia_cal.HsTiaCfg.DiodeClose = bFALSE;
|
|
||||||
hsrtia_cal.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
|
|
||||||
hsrtia_cal.HsTiaCfg.HstiaCtia = 31;
|
|
||||||
hsrtia_cal.HsTiaCfg.HstiaRtiaSel = GetHSTIARtia(ConfigHstiaVal);
|
|
||||||
hsrtia_cal.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
|
|
||||||
hsrtia_cal.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
|
|
||||||
hsrtia_cal.DftCfg.DftNum = DFTNUM_16384;
|
|
||||||
hsrtia_cal.DftCfg.DftSrc = DFTSRC_SINC3;
|
|
||||||
|
|
||||||
printf(">> Calibrating HSTIA %d Ohm...\n", ConfigHstiaVal);
|
|
||||||
if (AD5940_HSRtiaCal(&hsrtia_cal, &HsRes) == AD5940ERR_OK) {
|
|
||||||
printf("Calibrated HSTIA: Mag = %f Ohm, Phase = %f\n", HsRes.Magnitude, HsRes.Phase);
|
|
||||||
CalibratedHstiaVal = HsRes.Magnitude;
|
|
||||||
} else {
|
|
||||||
printf("HSTIA Calibration Failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: NO RESET HERE. We must preserve the calibration registers.
|
|
||||||
// Explicitly disable sequencer and interrupts to leave a clean state for the next routine
|
|
||||||
AD5940_SEQCtrlS(bFALSE);
|
|
||||||
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Main Loop
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
char input_buffer[64];
|
char input_buffer[64];
|
||||||
int input_pos = 0;
|
int input_pos = 0;
|
||||||
|
|
@ -576,7 +28,6 @@ void process_command() {
|
||||||
printf("CHIP_ID:0x%04X\n", id);
|
printf("CHIP_ID:0x%04X\n", id);
|
||||||
}
|
}
|
||||||
else if (cmd == 'r') {
|
else if (cmd == 'r') {
|
||||||
// r <lp_val> <hp_val>
|
|
||||||
if (strlen(input_buffer) > 2) {
|
if (strlen(input_buffer) > 2) {
|
||||||
int lp = 0, hp = 0;
|
int lp = 0, hp = 0;
|
||||||
int count = sscanf(input_buffer + 2, "%d %d", &lp, &hp);
|
int count = sscanf(input_buffer + 2, "%d %d", &lp, &hp);
|
||||||
|
|
@ -601,6 +52,26 @@ void process_command() {
|
||||||
printf("LPF_SET:%d\n", idx);
|
printf("LPF_SET:%d\n", idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (cmd == 'L') { // Set RLoad
|
||||||
|
if (strlen(input_buffer) > 2) {
|
||||||
|
int val = atoi(input_buffer + 2);
|
||||||
|
switch(val) {
|
||||||
|
case 10: ConfigRLoad = LPTIARLOAD_10R; break;
|
||||||
|
case 30: ConfigRLoad = LPTIARLOAD_30R; break;
|
||||||
|
case 50: ConfigRLoad = LPTIARLOAD_50R; break;
|
||||||
|
case 100: ConfigRLoad = LPTIARLOAD_100R; break;
|
||||||
|
default: ConfigRLoad = LPTIARLOAD_100R; break;
|
||||||
|
}
|
||||||
|
printf("RLOAD_SET:%d\n", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cmd == 't') {
|
||||||
|
if (strlen(input_buffer) > 2) {
|
||||||
|
int val = atoi(input_buffer + 2);
|
||||||
|
GlobalShortRe0Se0 = (val > 0) ? bTRUE : bFALSE;
|
||||||
|
printf("SHORT_RE0_SE0:%d\n", GlobalShortRe0Se0);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (cmd == 'c') {
|
else if (cmd == 'c') {
|
||||||
Routine_CalibrateLFO();
|
Routine_CalibrateLFO();
|
||||||
Routine_CalibrateSystem();
|
Routine_CalibrateSystem();
|
||||||
|
|
@ -622,7 +93,6 @@ void process_command() {
|
||||||
Routine_Amperometric(bias);
|
Routine_Amperometric(bias);
|
||||||
}
|
}
|
||||||
else if (cmd == 'l') {
|
else if (cmd == 'l') {
|
||||||
// l <start> <end> <steps> <duration>
|
|
||||||
float start = -500.0f, end = 500.0f;
|
float start = -500.0f, end = 500.0f;
|
||||||
int steps = 100, duration = 10000;
|
int steps = 100, duration = 10000;
|
||||||
if (strlen(input_buffer) > 2) sscanf(input_buffer + 2, "%f %f %d %d", &start, &end, &steps, &duration);
|
if (strlen(input_buffer) > 2) sscanf(input_buffer + 2, "%f %f %d %d", &start, &end, &steps, &duration);
|
||||||
|
|
@ -634,10 +104,9 @@ void process_command() {
|
||||||
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
CurrentMode = MODE_IDLE;
|
CurrentMode = MODE_IDLE;
|
||||||
printf("STOPPED\n");
|
printf("STOPPED\n");
|
||||||
SystemReset(); // Reset here to clean up
|
SystemReset();
|
||||||
}
|
}
|
||||||
else if (cmd == 'z') {
|
else if (cmd == 'z') {
|
||||||
// Explicit Reset Command
|
|
||||||
SystemReset();
|
SystemReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -648,7 +117,6 @@ int main() {
|
||||||
|
|
||||||
setup_pins();
|
setup_pins();
|
||||||
|
|
||||||
// Initial Hardware Reset and Configuration
|
|
||||||
AD5940_HWReset();
|
AD5940_HWReset();
|
||||||
AD5940_Initialize();
|
AD5940_Initialize();
|
||||||
AD5940PlatformCfg();
|
AD5940PlatformCfg();
|
||||||
|
|
@ -679,7 +147,7 @@ int main() {
|
||||||
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
||||||
AppIMPCleanup();
|
AppIMPCleanup();
|
||||||
CurrentMode = MODE_IDLE;
|
CurrentMode = MODE_IDLE;
|
||||||
SystemReset(); // Reset on error
|
SystemReset();
|
||||||
} else if(temp > 0) {
|
} else if(temp > 0) {
|
||||||
ImpedanceShowResult(AppBuff, temp);
|
ImpedanceShowResult(AppBuff, temp);
|
||||||
}
|
}
|
||||||
|
|
@ -690,7 +158,7 @@ int main() {
|
||||||
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
||||||
AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
CurrentMode = MODE_IDLE;
|
CurrentMode = MODE_IDLE;
|
||||||
SystemReset(); // Reset on error
|
SystemReset();
|
||||||
} else if(temp > 0) {
|
} else if(temp > 0) {
|
||||||
AmperometricShowResult((float*)AppBuff, temp);
|
AmperometricShowResult((float*)AppBuff, temp);
|
||||||
}
|
}
|
||||||
|
|
@ -701,7 +169,7 @@ int main() {
|
||||||
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
printf("ERROR: FIFO Overflow/Underflow. Stopping.\n");
|
||||||
AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
CurrentMode = MODE_IDLE;
|
CurrentMode = MODE_IDLE;
|
||||||
SystemReset(); // Reset on error
|
SystemReset();
|
||||||
} else if(temp > 0) {
|
} else if(temp > 0) {
|
||||||
RampShowResult((float*)AppBuff, temp);
|
RampShowResult((float*)AppBuff, temp);
|
||||||
}
|
}
|
||||||
|
|
@ -713,7 +181,7 @@ int main() {
|
||||||
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
else if (CurrentMode == MODE_AMPEROMETRIC) AppAMPCtrl(AMPCTRL_SHUTDOWN, 0);
|
||||||
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
else if (CurrentMode == MODE_RAMP) AppRAMPCtrl(APPCTRL_SHUTDOWN, 0);
|
||||||
CurrentMode = MODE_IDLE;
|
CurrentMode = MODE_IDLE;
|
||||||
SystemReset(); // Reset here to clean up
|
SystemReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue