summaryrefslogtreecommitdiff
path: root/src/peripherals/avrdx-sigrow.ts
diff options
context:
space:
mode:
authorApexo2026-03-28 23:40:53 +0100
committerApexo2026-03-28 23:40:53 +0100
commit1b194ac4578dea8e71b0d61d1cb4d875f435ba71 (patch)
tree786019a0c6f34b458f3272bf2ecbde0de1976e0a /src/peripherals/avrdx-sigrow.ts
downloadanduril-sim-1b194ac4578dea8e71b0d61d1cb4d875f435ba71.tar.gz
anduril-sim-1b194ac4578dea8e71b0d61d1cb4d875f435ba71.tar.bz2
anduril-sim-1b194ac4578dea8e71b0d61d1cb4d875f435ba71.zip
D3AA simulator
Diffstat (limited to '')
-rw-r--r--src/peripherals/avrdx-sigrow.ts59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/peripherals/avrdx-sigrow.ts b/src/peripherals/avrdx-sigrow.ts
new file mode 100644
index 0000000..ddd266a
--- /dev/null
+++ b/src/peripherals/avrdx-sigrow.ts
@@ -0,0 +1,59 @@
+// AVR-Dx SIGROW - Signature Row
+// Read-only factory calibration data. Pre-loaded with values that produce
+// correct temperature readings with the D3AA firmware's conversion formula.
+
+import { CPU } from 'avr8js/cpu/cpu';
+
+// Typical AVR32DD20 calibration values.
+// The firmware formula (from arch/avr32dd20.c mcu_temp_raw2cooked):
+// temp = (sigrow_offset << 4) - measurement
+// temp *= sigrow_slope
+// temp += 65536 / 8
+// temp >>= 10
+// result is Kelvin << 6
+//
+// IMPORTANT: On AVR, the (sigrow_offset << 4) shift is done in 16-bit
+// arithmetic (int is 16-bit on AVR), so offset MUST be ≤ 0x0FFF (12-bit)
+// or the shift overflows. The datasheet specifies TEMPSENSE1 as a 12-bit value.
+//
+// With slope=0x036F (879), offset=0x0800 (2048):
+// offset << 4 = 32768 (fits in 16 bits)
+// At 25°C: ADC_acc16 ≈ 10548, result = 19081 → 23°C (±2°C rounding)
+// At 80°C: ADC_acc16 ≈ 6447, result = 22601 → 78°C
+// Range -40°C..125°C: ADC values 3092..15394, all in valid 16-bit range
+const DEFAULT_TEMPSENSE0 = 0x036F; // slope
+const DEFAULT_TEMPSENSE1 = 0x0800; // offset (12-bit)
+
+const TEMPSENSE0 = 0;
+const TEMPSENSE1 = 2;
+
+export class AVRDxSIGROW {
+ constructor(cpu: CPU, base: number, readonly slope = DEFAULT_TEMPSENSE0, readonly offset = DEFAULT_TEMPSENSE1) {
+ // TEMPSENSE0 (16-bit at 0x1104-0x1105)
+ cpu.readHooks[base + TEMPSENSE0] = () => slope & 0xFF;
+ cpu.readHooks[base + TEMPSENSE0 + 1] = () => (slope >> 8) & 0xFF;
+
+ // TEMPSENSE1 (16-bit at 0x1106-0x1107)
+ cpu.readHooks[base + TEMPSENSE1] = () => offset & 0xFF;
+ cpu.readHooks[base + TEMPSENSE1 + 1] = () => (offset >> 8) & 0xFF;
+
+ // Write hooks to prevent accidental writes
+ cpu.writeHooks[base + TEMPSENSE0] = () => true;
+ cpu.writeHooks[base + TEMPSENSE0 + 1] = () => true;
+ cpu.writeHooks[base + TEMPSENSE1] = () => true;
+ cpu.writeHooks[base + TEMPSENSE1 + 1] = () => true;
+ }
+
+ /** Compute the raw ADC result (16-bit accumulated) for a given temperature in Celsius */
+ tempCToRawADC(tempC: number): number {
+ const tempK = tempC + 273.15;
+ const kelvin6 = Math.round(tempK * 64);
+ // Reverse the firmware formula:
+ // kelvin6 = ((offset << 4) - measurement) * slope + 8192) >> 10
+ // kelvin6 << 10 = (offset << 4 - measurement) * slope + 8192
+ // (kelvin6 << 10) - 8192 = (offset << 4 - measurement) * slope
+ // measurement = (offset << 4) - ((kelvin6 << 10) - 8192) / slope
+ const measurement = (this.offset << 4) - ((kelvin6 << 10) - 8192) / this.slope;
+ return Math.max(0, Math.min(0xFFFF, Math.round(measurement)));
+ }
+}