STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
SensorSelfTest.h
Go to the documentation of this file.
1#pragma once
2
3#include <Arduino.h>
4
5#include "STAR_ADS126X.h"
6
7namespace SensorSelfTest {
8
9// Thresholds (ADC codes, full-scale = 2^31)
10// ADC TDAC test: VDD reference. TDACP = 0.6*AVDD, TDACN = 0.5*AVDD.
11// Differential = 0.1*AVDD. Reference = AVDD. So code = 0.1 * 2^31.
12// We allow ±1% of the expected code.
13constexpr int32_t ADC_TDAC_EXPECTED_CODE = 214748364; // 0.1 * 2^31
14constexpr int32_t ADC_TDAC_TOLERANCE = 2147484; // 1% of expected code
15
16// Sensor bias test (SBMAG = 0b110 → 10MΩ pull resistor via MODE1):
17// Open circuit → bias pulls pin to rail → code saturates at +FS (~2^31)
18// Connected → sensor loads the bias → small-to-moderate code
19// Two thresholds with an AMBIGUOUS band in between:
20// |code| < 40% FS → CONNECTED
21// |code| > 60% FS → DISCONNECTED
22// between → AMBIGUOUS
23constexpr int32_t SENSOR_BIAS_CLOSED_THRESHOLD = 858993459; // 40% of 2^31
24constexpr int32_t SENSOR_BIAS_OPEN_THRESHOLD = 1288490188; // 60% of 2^31
25
26// Analog settling delay (ms) after mux switch during bias test.
27// The 10MΩ bias resistor × parasitic capacitance (traces, connectors)
28// forms an RC that needs ~3τ to reach the rail on open pins.
29// 50ms covers ~3τ for up to ~1.6nF parasitic capacitance.
30constexpr unsigned long BIAS_SETTLE_MS = 50;
31
32// Number of ADC samples to average per channel during the bias test.
33constexpr uint8_t BIAS_AVG_SAMPLES = 4;
34
35enum class BiasResult : uint8_t {
36 CONNECTED = 0, // |code| < closed threshold — sensor is present
37 AMBIGUOUS = 1, // between thresholds — uncertain
38 DISCONNECTED = 2, // |code| > open threshold — no sensor
39};
40
41/**
42 * ADC self-test via internal TDAC.
43 * Sets TDACP to 0.6*VDD and TDACN to 0.5*VDD.
44 * Muxes TDAC vs TDAC, uses VDD reference (5V).
45 * Reads and checks that the code is near ADC_TDAC_EXPECTED_CODE.
46 * Restores original reference, mux, and turns off TDAC after test.
47 */
49 bool passed;
50 int32_t code; // raw ADC code read
52};
53
55 uint8_t original_ref_neg,
56 uint8_t original_ref_pos) {
57 adc.bypassPGA();
58 adc.setInputMux(ADS126X_TDAC, ADS126X_TDAC);
59
60 adc.setOutputTDACP(1);
61 adc.setOutputTDACN(1);
62 adc.setOutputmagnitudeTDACP(ADS126X_TDAC_DIV_0_6);
63 adc.setOutputmagnitudeTDACN(ADS126X_TDAC_DIV_0_5);
64
66
67 // Flush 5 conversions so SINC5 filter settles after mux/ref change
68 for (uint8_t i = 0; i < 5; i++) {
70 while (digitalRead(drdy_pin) != LOW)
72 adc.readADC1();
73 }
74
75 // Real measurement
77 while (digitalRead(drdy_pin) != LOW)
79
80 const auto reading = adc.readADC1();
81 int32_t code = reading.value;
82
83 adc.bypassPGA();
84 adc.setOutputTDACP(0);
85 adc.setOutputTDACN(0);
86 adc.setReference(original_ref_neg, original_ref_pos);
87
88 if (!reading.checksumValid) {
89 return {false, code, false};
90 }
91
92 int32_t diff = code - ADC_TDAC_EXPECTED_CODE;
93 if (diff < 0)
94 diff = -diff;
95
96 return {diff <= ADC_TDAC_TOLERANCE, code, true};
97}
98
99/** Call once before the connector loop. Configures sensor bias on ADC1 with
100 * 10MΩ pull-up, bypasses PGA (bias voltages can exceed PGA input range),
101 * and turns off IDACs. */
103 adc.bypassPGA();
104 adc.setIDAC1Mag(ADS126X_IDAC_MAG_0);
105 adc.setIDAC2Mag(ADS126X_IDAC_MAG_0);
106 adc.setBiasADC(ADS126X_BIAS_ADC1);
107 adc.setBiasPolarity(ADS126X_BIAS_PULLUP);
108 adc.setBiasMagnitude(ADS126X_BIAS_MAG_10M);
110}
111
114 int32_t code; // raw signed ADC code
116};
117
118/**
119 * Read one connector with sensor bias already active.
120 * After switching the mux, flushes `settle_cycles` conversions so the
121 * digital filter (e.g. SINC5 needs 5) fully settles on the new channel
122 * before taking the real measurement.
123 *
124 * Returns CONNECTED if |code| < SENSOR_BIAS_CLOSED_THRESHOLD,
125 * DISCONNECTED if |code| >= SENSOR_BIAS_OPEN_THRESHOLD,
126 * AMBIGUOUS otherwise (or on checksum failure).
127 */
128inline BiasReadResult read_sensor_bias(ADS126X& adc, uint8_t drdy_pin,
129 uint8_t adc_channel, uint8_t aincom,
130 uint8_t settle_cycles = 5) {
131 // Stop ADC so the filter doesn't fill with mid-transition data
132 adc.stopADC1();
133 adc.setInputMux(adc_channel, aincom);
134
135 // Let the 10MΩ bias resistor charge/discharge parasitic capacitance
137
138 // Restart ADC — filter pipeline starts fresh on the settled signal
139 adc.startADC1();
140
141 // Flush conversions so the digital filter (SINC5) fully settles
142 for (uint8_t i = 0; i < settle_cycles; i++) {
144 while (digitalRead(drdy_pin) != LOW)
146 adc.readADC1();
147 }
148
149 // Take multiple samples and average for noise rejection
150 int64_t sum = 0;
151 uint8_t good_count = 0;
152 for (uint8_t s = 0; s < BIAS_AVG_SAMPLES; s++) {
154 while (digitalRead(drdy_pin) != LOW)
156 auto reading = adc.readADC1();
157 if (reading.checksumValid) {
158 sum += reading.value;
159 good_count++;
160 }
161 }
162
163 if (good_count == 0)
164 return {BiasResult::AMBIGUOUS, 0, false};
165
166 int32_t val = static_cast<int32_t>(sum / good_count);
167 int32_t abs_val = (val < 0) ? -val : val;
168
170 if (abs_val < SENSOR_BIAS_CLOSED_THRESHOLD)
172 else if (abs_val >= SENSOR_BIAS_OPEN_THRESHOLD)
174 else
176
177 return {r, val, true};
178}
179
180/** Call once after the connector loop. Restores SBMAG = 0. */
182 adc.setBiasMagnitude(ADS126X_BIAS_MAG_0);
183}
184
185} // namespace SensorSelfTest
int digitalRead(int)
Definition Arduino.h:50
#define LOW
Definition Arduino.h:20
void delayMicroseconds(unsigned int)
Definition Arduino.h:58
#define ADS126X_TDAC_DIV_0_5
Definition STAR_ADS126X.h:7
#define ADS126X_BIAS_PULLUP
#define ADS126X_REF_POS_VDD
Definition STAR_ADS126X.h:9
#define ADS126X_TDAC_DIV_0_6
Definition STAR_ADS126X.h:6
#define ADS126X_BIAS_MAG_0
#define ADS126X_IDAC_MAG_0
#define ADS126X_BIAS_ADC1
#define ADS126X_TDAC
Definition STAR_ADS126X.h:5
#define ADS126X_BIAS_MAG_10M
#define ADS126X_REF_NEG_VSS
Definition STAR_ADS126X.h:8
void delay(int ms)
static STAR_MCP3201 adc(MCP3201_CS_PIN, &mcp_spi)
void sensor_bias_disable(ADS126X &adc)
Call once after the connector loop.
constexpr int32_t SENSOR_BIAS_CLOSED_THRESHOLD
BiasReadResult read_sensor_bias(ADS126X &adc, uint8_t drdy_pin, uint8_t adc_channel, uint8_t aincom, uint8_t settle_cycles=5)
Read one connector with sensor bias already active.
constexpr int32_t SENSOR_BIAS_OPEN_THRESHOLD
void sensor_bias_enable(ADS126X &adc)
Call once before the connector loop.
constexpr uint8_t BIAS_AVG_SAMPLES
constexpr int32_t ADC_TDAC_EXPECTED_CODE
constexpr int32_t ADC_TDAC_TOLERANCE
AdcSelfTestResult run_adc_self_test(ADS126X &adc, uint8_t drdy_pin, uint8_t original_ref_neg, uint8_t original_ref_pos)
constexpr unsigned long BIAS_SETTLE_MS
ADC self-test via internal TDAC.
static boot_report_t r
static int32_t diff(uint32_t a, uint32_t b)