167 lines
5.0 KiB
Arduino
167 lines
5.0 KiB
Arduino
|
|
#include <Arduino.h>
|
||
|
|
#include <util/atomic.h>
|
||
|
|
|
||
|
|
// ── ADC Interrupt-driven single-channel read (A0) ────────────
|
||
|
|
volatile uint16_t adc_result = 0;
|
||
|
|
volatile bool adc_ready = false;
|
||
|
|
|
||
|
|
void setupADC() {
|
||
|
|
// Reference = AVCC (5 V)
|
||
|
|
ADMUX = (1 << REFS0);
|
||
|
|
|
||
|
|
// Channel = A0 (MUX[3:0] = 0000)
|
||
|
|
ADMUX &= 0xF0;
|
||
|
|
|
||
|
|
// Prescaler = 16 → 16 MHz / 16 = 1 MHz ADC clock
|
||
|
|
// Each conversion ≈ 13 ADC clocks → ~76.9 kHz sample rate
|
||
|
|
ADCSRA = (1 << ADEN) | (1 << ADIE)
|
||
|
|
| (1 << ADPS2); // ADPS = 100 → /16
|
||
|
|
|
||
|
|
// Start first conversion
|
||
|
|
ADCSRA |= (1 << ADSC);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── OOR digital input ────────────────────────────────────────
|
||
|
|
#define OOR_PIN 2 // digital pin 2 — HIGH = out of range
|
||
|
|
|
||
|
|
volatile bool OOR;
|
||
|
|
|
||
|
|
ISR(ADC_vect) {
|
||
|
|
uint16_t sample = ADC;
|
||
|
|
// Discard if OOR pin (PD2) is HIGH
|
||
|
|
OOR = (digitalRead(OOR_PIN));
|
||
|
|
if (!OOR) {
|
||
|
|
adc_result = sample;
|
||
|
|
adc_ready = true;
|
||
|
|
}
|
||
|
|
ADCSRA |= (1 << ADSC); // kick off next conversion immediately
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── ADC → mm linear mapping ─────────────────────────────────
|
||
|
|
// ADC 185 → 16 mm, ADC 900 → 26 mm
|
||
|
|
// #define adcToMM(adc) (16.0f + (float)((adc) - 185) * (10.0f / 715.0f))
|
||
|
|
#define adcToMM(adc) (0.0f + (float)((adc) - 0) * (10.0f / 1024.0f))
|
||
|
|
|
||
|
|
// ── Boundary tracking (in-range only) ────────────────────────
|
||
|
|
#define TRACK_N 10
|
||
|
|
uint16_t lowestVals[TRACK_N];
|
||
|
|
uint16_t highestVals[TRACK_N];
|
||
|
|
uint8_t lowestCount = 0;
|
||
|
|
uint8_t highestCount = 0;
|
||
|
|
|
||
|
|
// Insert val into a sorted-ascending array of up to TRACK_N entries
|
||
|
|
// keeping only the N smallest values seen so far.
|
||
|
|
static void trackLowest(uint16_t val) {
|
||
|
|
if (lowestCount < TRACK_N) {
|
||
|
|
// Array not full — insert in sorted position
|
||
|
|
uint8_t i = lowestCount;
|
||
|
|
while (i > 0 && lowestVals[i - 1] > val) {
|
||
|
|
lowestVals[i] = lowestVals[i - 1];
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
lowestVals[i] = val;
|
||
|
|
lowestCount++;
|
||
|
|
} else if (val < lowestVals[TRACK_N - 1]) {
|
||
|
|
// Replace the current largest of the "lowest" set
|
||
|
|
uint8_t i = TRACK_N - 1;
|
||
|
|
while (i > 0 && lowestVals[i - 1] > val) {
|
||
|
|
lowestVals[i] = lowestVals[i - 1];
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
lowestVals[i] = val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Insert val into a sorted-descending array of up to TRACK_N entries
|
||
|
|
// keeping only the N largest values seen so far.
|
||
|
|
static void trackHighest(uint16_t val) {
|
||
|
|
if (highestCount < TRACK_N) {
|
||
|
|
uint8_t i = highestCount;
|
||
|
|
while (i > 0 && highestVals[i - 1] < val) {
|
||
|
|
highestVals[i] = highestVals[i - 1];
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
highestVals[i] = val;
|
||
|
|
highestCount++;
|
||
|
|
} else if (val > highestVals[TRACK_N - 1]) {
|
||
|
|
uint8_t i = TRACK_N - 1;
|
||
|
|
while (i > 0 && highestVals[i - 1] < val) {
|
||
|
|
highestVals[i] = highestVals[i - 1];
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
highestVals[i] = val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void resetTracking() {
|
||
|
|
lowestCount = 0;
|
||
|
|
highestCount = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void printBoundaries() {
|
||
|
|
Serial.println(F("--- 10 Lowest In-Range ADC Values ---"));
|
||
|
|
for (uint8_t i = 0; i < lowestCount; i++) {
|
||
|
|
Serial.println(lowestVals[i]);
|
||
|
|
}
|
||
|
|
Serial.println(F("--- 10 Highest In-Range ADC Values ---"));
|
||
|
|
for (uint8_t i = 0; i < highestCount; i++) {
|
||
|
|
Serial.println(highestVals[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── State ────────────────────────────────────────────────────
|
||
|
|
bool sampling = false;
|
||
|
|
|
||
|
|
// ═════════════════════════════════════════════════════════════
|
||
|
|
void setup() {
|
||
|
|
Serial.begin(2000000);
|
||
|
|
pinMode(OOR_PIN, INPUT);
|
||
|
|
setupADC();
|
||
|
|
Serial.println(F("Send '1' to start sampling, '0' to stop and print bounds."));
|
||
|
|
}
|
||
|
|
|
||
|
|
void loop() {
|
||
|
|
// ── Serial command handling ──────────────────────────────
|
||
|
|
if (Serial.available() > 0) {
|
||
|
|
String cmd = Serial.readStringUntil('\n');
|
||
|
|
cmd.trim();
|
||
|
|
|
||
|
|
if (cmd.charAt(0) == '1') {
|
||
|
|
sampling = true;
|
||
|
|
resetTracking();
|
||
|
|
Serial.println(F("Sampling started."));
|
||
|
|
} else if (cmd.charAt(0) == '0') {
|
||
|
|
sampling = false;
|
||
|
|
Serial.println(F("Sampling stopped."));
|
||
|
|
printBoundaries();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Main sample path ────────────────────────────────────
|
||
|
|
if (!sampling) return;
|
||
|
|
|
||
|
|
// Grab the latest ADC value atomically
|
||
|
|
uint16_t val;
|
||
|
|
bool ready;
|
||
|
|
bool newOOR;
|
||
|
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||
|
|
ready = adc_ready;
|
||
|
|
val = adc_result;
|
||
|
|
adc_ready = false;
|
||
|
|
newOOR = OOR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!ready) return; // nothing new — come back next iteration
|
||
|
|
|
||
|
|
// All values here are in-range (OOR filtered in ISR)
|
||
|
|
trackLowest(val);
|
||
|
|
trackHighest(val);
|
||
|
|
|
||
|
|
float mm = adcToMM(val);
|
||
|
|
Serial.print(val);
|
||
|
|
Serial.print(", ");
|
||
|
|
Serial.print(mm, 2);
|
||
|
|
Serial.print(" mm, ");
|
||
|
|
Serial.println(newOOR ? "out of range" : "in range");
|
||
|
|
}
|