Optimize pin mapping for direct bitwise GPIO ops

Rewire GPIO↔HMC472A so GPIO(n) = step bit (n-1):
  GPIO1→V6(0.5dB), GPIO2→V5(1dB), ... GPIO6→V1(16dB)

This enables single-instruction GPIO updates:
  GPIO.out_w1tc = (step & 0x3F) << 1
  GPIO.out_w1ts = (~step) & 0x7E

Replaces 28-line loop with 4-line bitwise code.
This commit is contained in:
Ryan Malloy 2026-02-03 00:28:33 -07:00
parent b5794c5f8d
commit 86a5db5b08
4 changed files with 189 additions and 148 deletions

View File

@ -18,21 +18,30 @@
#define WIFI_TIMEOUT_MS 15000 #define WIFI_TIMEOUT_MS 15000
// --- HMC472A Control Pins (active-low) --- // --- HMC472A Control Pins (active-low) ---
// GPIOs 1-6: contiguous block in lower 32-bit register // Optimized mapping: GPIO number = step bit position + 1
// Enables glitch-free simultaneous writes via GPIO.out_w1ts/w1tc // Enables single-instruction bitwise ops instead of loop
static constexpr uint8_t PIN_V1 = 1; // 16 dB (MSB) //
static constexpr uint8_t PIN_V2 = 2; // 8 dB // Wiring (GPIO → HMC472A pin):
static constexpr uint8_t PIN_V3 = 3; // 4 dB // GPIO1 → V6 (0.5 dB) = step bit 0 (LSB)
static constexpr uint8_t PIN_V4 = 4; // 2 dB // GPIO2 → V5 (1 dB) = step bit 1
static constexpr uint8_t PIN_V5 = 5; // 1 dB // GPIO3 → V4 (2 dB) = step bit 2
static constexpr uint8_t PIN_V6 = 6; // 0.5 dB (LSB) // GPIO4 → V3 (4 dB) = step bit 3
// GPIO5 → V2 (8 dB) = step bit 4
// GPIO6 → V1 (16 dB) = step bit 5 (MSB)
//
static constexpr uint8_t PIN_V6 = 1; // 0.5 dB (LSB) - step bit 0
static constexpr uint8_t PIN_V5 = 2; // 1 dB - step bit 1
static constexpr uint8_t PIN_V4 = 3; // 2 dB - step bit 2
static constexpr uint8_t PIN_V3 = 4; // 4 dB - step bit 3
static constexpr uint8_t PIN_V2 = 5; // 8 dB - step bit 4
static constexpr uint8_t PIN_V1 = 6; // 16 dB (MSB) - step bit 5
// Pin array ordered by attenuation value (V1=16dB first)
static constexpr uint8_t ATTEN_PINS[6] = {PIN_V1, PIN_V2, PIN_V3, PIN_V4, PIN_V5, PIN_V6}; static constexpr uint8_t ATTEN_PINS[6] = {PIN_V1, PIN_V2, PIN_V3, PIN_V4, PIN_V5, PIN_V6};
static constexpr float ATTEN_DB[6] = {16.0f, 8.0f, 4.0f, 2.0f, 1.0f, 0.5f}; static constexpr float ATTEN_DB[6] = {16.0f, 8.0f, 4.0f, 2.0f, 1.0f, 0.5f};
// Bitmask covering all 6 control pins in the GPIO register // Bitmask: 0b01111110 = bits 1-6 in GPIO register
static constexpr uint32_t ATTEN_PIN_MASK = (1 << PIN_V1) | (1 << PIN_V2) | (1 << PIN_V3) | static constexpr uint32_t ATTEN_PIN_MASK = 0x7E;
(1 << PIN_V4) | (1 << PIN_V5) | (1 << PIN_V6);
// --- Status LED --- // --- Status LED ---
static constexpr uint8_t PIN_LED = 15; // Built-in LED, active HIGH static constexpr uint8_t PIN_LED = 15; // Built-in LED, active HIGH

View File

@ -79,33 +79,18 @@ bool Attenuator::getGPIOState(uint8_t index) const {
} }
void Attenuator::applyToGPIO() { void Attenuator::applyToGPIO() {
// Calculate which pins should be HIGH (bit=0, not attenuating) // Optimized bitwise GPIO update — no loop needed!
// and which should be LOW (bit=1, attenuating)
// //
// Active-low logic: step bit set → GPIO LOW → attenuation engaged // Pin mapping: GPIO(n) = step bit (n-1), so step << 1 aligns with GPIOs 1-6
// Active-low: step bit 1 → GPIO LOW, step bit 0 → GPIO HIGH
// //
// Using register writes for glitch-free simultaneous update: // Example: step=5 (0b000101 = 2.5dB) → GPIO1,3 LOW, GPIO2,4,5,6 HIGH
// GPIO.out_w1ts = pins to set HIGH (write-1-to-set)
// GPIO.out_w1tc = pins to set LOW (write-1-to-clear)
uint32_t pins_high = 0; // Bits to set HIGH uint32_t step_bits = (_step & 0x3F) << 1; // Step value shifted to GPIO positions
uint32_t pins_low = 0; // Bits to set LOW
for (uint8_t i = 0; i < 6; i++) { // Atomic register writes for glitch-free update
uint32_t pin_bit = 1 << ATTEN_PINS[i]; GPIO.out_w1tc = step_bits; // Set LOW where step bit = 1
if (getBit(i)) { GPIO.out_w1ts = (~step_bits) & ATTEN_PIN_MASK; // Set HIGH where step bit = 0
// Step bit is 1 → engage attenuation → GPIO LOW
pins_low |= pin_bit;
} else {
// Step bit is 0 → pass signal → GPIO HIGH
pins_high |= pin_bit;
}
}
// Atomic-ish register writes (as atomic as we can get without disabling interrupts)
// Clear first, then set — ensures no glitch to opposite state
GPIO.out_w1tc = pins_low;
GPIO.out_w1ts = pins_high;
} }
void Attenuator::saveToNVS() { void Attenuator::saveToNVS() {

View File

@ -1,47 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500" width="800" height="500"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 520" width="800" height="520">
<defs> <defs>
<style> <style>
.board { fill: #1a472a; stroke: #0d2818; stroke-width: 2; } .board { fill: #1a472a; stroke: #0d2818; stroke-width: 2; }
.board-label { font-family: monospace; font-size: 14px; font-weight: bold; fill: white; } .board-label { font-family: monospace; font-size: 14px; font-weight: bold; fill: white; }
.pin-label { font-family: monospace; font-size: 11px; fill: #333; } .pin-label { font-family: monospace; font-size: 11px; fill: #333; }
.pin-label-white { font-family: monospace; font-size: 10px; fill: #e0e0e0; } .pin-label-white { font-family: monospace; font-size: 10px; fill: #e0e0e0; }
.wire { stroke-width: 2; fill: none; } .wire { stroke-width: 2.5; fill: none; }
.wire-data { stroke: #2196F3; } .wire-data { stroke: #2196F3; }
.wire-power { stroke: #f44336; } .wire-power { stroke: #f44336; }
.wire-gnd { stroke: #333; } .wire-gnd { stroke: #333; }
.title { font-family: sans-serif; font-size: 18px; font-weight: bold; fill: #333; } .title { font-family: sans-serif; font-size: 18px; font-weight: bold; fill: #333; }
.subtitle { font-family: sans-serif; font-size: 12px; fill: #666; } .subtitle { font-family: sans-serif; font-size: 12px; fill: #666; }
.note { font-family: sans-serif; font-size: 11px; fill: #555; } .note { font-family: sans-serif; font-size: 11px; fill: #555; }
.note-box { fill: #fff8e1; stroke: #ffb300; stroke-width: 1; } .note-box { fill: #e3f2fd; stroke: #1976d2; stroke-width: 1; rx: 5; }
.header { fill: #333; } .header { fill: #333; }
.pin { fill: #c9a227; stroke: #8b7355; stroke-width: 1; } .pin { fill: #c9a227; stroke: #8b7355; stroke-width: 1; }
.sma { fill: #c9a227; stroke: #8b7355; stroke-width: 1; } .sma { fill: #c9a227; stroke: #8b7355; stroke-width: 1; }
.usb { fill: #333; stroke: #222; stroke-width: 1; } .usb { fill: #333; stroke: #222; stroke-width: 1; }
.chip { fill: #1a1a1a; stroke: #333; stroke-width: 1; } .chip { fill: #1a1a1a; stroke: #333; stroke-width: 1; }
.code { font-family: monospace; font-size: 10px; fill: #1565c0; }
</style> </style>
</defs> </defs>
<!-- Title --> <!-- Title -->
<text x="400" y="30" text-anchor="middle" class="title">HMC472A Attenuator Controller Wiring</text> <text x="400" y="28" text-anchor="middle" class="title">HMC472A Attenuator Controller Wiring</text>
<text x="400" y="50" text-anchor="middle" class="subtitle">ESP32-S2 Mini to HMC472A Module Connection Diagram</text> <text x="400" y="46" text-anchor="middle" class="subtitle">Optimized Mapping: GPIO(n) = Step Bit (n-1) — Enables Direct Bitwise Ops</text>
<!-- ESP32-S2 Mini Board --> <!-- ESP32-S2 Mini Board -->
<rect x="80" y="100" width="180" height="280" rx="5" class="board"/> <rect x="80" y="80" width="180" height="260" rx="5" class="board"/>
<text x="170" y="125" text-anchor="middle" class="board-label">ESP32-S2 Mini</text> <text x="170" y="102" text-anchor="middle" class="board-label">ESP32-S2 Mini</text>
<text x="170" y="140" text-anchor="middle" class="pin-label-white">(WEMOS/LOLIN)</text> <text x="170" y="116" text-anchor="middle" class="pin-label-white">(WEMOS/LOLIN)</text>
<!-- USB-C on S2 Mini --> <!-- USB-C on S2 Mini -->
<rect x="145" y="90" width="50" height="15" rx="3" class="usb"/> <rect x="145" y="70" width="50" height="14" rx="3" class="usb"/>
<text x="170" y="101" text-anchor="middle" style="font-size:8px;fill:#888;">USB-C</text> <text x="170" y="80" text-anchor="middle" style="font-size:8px;fill:#888;">USB-C</text>
<!-- ESP32 chip representation --> <!-- ESP32 chip representation -->
<rect x="130" y="155" width="80" height="60" rx="2" class="chip"/> <rect x="130" y="130" width="80" height="50" rx="2" class="chip"/>
<text x="170" y="190" text-anchor="middle" style="font-size:9px;fill:#666;">ESP32-S2</text> <text x="170" y="160" text-anchor="middle" style="font-size:9px;fill:#666;">ESP32-S2</text>
<!-- S2 Mini Pin Header (left side, top to bottom) --> <!-- S2 Mini Pin Header (left side) -->
<g transform="translate(85, 230)"> <g transform="translate(85, 195)">
<!-- Pin rectangles -->
<rect x="0" y="0" width="25" height="10" class="pin"/><text x="30" y="9" class="pin-label-white">3V3</text> <rect x="0" y="0" width="25" height="10" class="pin"/><text x="30" y="9" class="pin-label-white">3V3</text>
<rect x="0" y="15" width="25" height="10" class="pin"/><text x="30" y="24" class="pin-label-white">GPIO1</text> <rect x="0" y="15" width="25" height="10" class="pin"/><text x="30" y="24" class="pin-label-white">GPIO1</text>
<rect x="0" y="30" width="25" height="10" class="pin"/><text x="30" y="39" class="pin-label-white">GPIO2</text> <rect x="0" y="30" width="25" height="10" class="pin"/><text x="30" y="39" class="pin-label-white">GPIO2</text>
@ -53,86 +53,91 @@
</g> </g>
<!-- S2 Mini Pin Header (right side) --> <!-- S2 Mini Pin Header (right side) -->
<g transform="translate(210, 230)"> <g transform="translate(210, 195)">
<rect x="0" y="0" width="25" height="10" class="pin"/><text x="-5" y="9" text-anchor="end" class="pin-label-white">5V</text> <rect x="0" y="0" width="25" height="10" class="pin"/><text x="-5" y="9" text-anchor="end" class="pin-label-white">5V</text>
<rect x="0" y="105" width="25" height="10" class="pin"/><text x="-5" y="114" text-anchor="end" class="pin-label-white">GND</text> <rect x="0" y="105" width="25" height="10" class="pin"/><text x="-5" y="114" text-anchor="end" class="pin-label-white">GND</text>
</g> </g>
<!-- HMC472A Module Board --> <!-- HMC472A Module Board -->
<rect x="520" y="100" width="200" height="280" rx="5" class="board"/> <rect x="520" y="80" width="200" height="260" rx="5" class="board"/>
<text x="620" y="125" text-anchor="middle" class="board-label">HMC472A Module</text> <text x="620" y="102" text-anchor="middle" class="board-label">HMC472A Module</text>
<text x="620" y="140" text-anchor="middle" class="pin-label-white">6-bit RF Attenuator</text> <text x="620" y="116" text-anchor="middle" class="pin-label-white">6-bit RF Attenuator</text>
<!-- HMC472A chip representation --> <!-- HMC472A chip representation -->
<rect x="555" y="155" width="60" height="40" rx="2" class="chip"/> <rect x="555" y="130" width="60" height="40" rx="2" class="chip"/>
<text x="585" y="180" text-anchor="middle" style="font-size:8px;fill:#666;">HMC472A</text> <text x="585" y="155" text-anchor="middle" style="font-size:8px;fill:#666;">HMC472A</text>
<!-- SMA Connectors --> <!-- SMA Connectors -->
<ellipse cx="540" y="230" rx="15" ry="20" class="sma"/> <ellipse cx="540" cy="200" rx="15" ry="20" class="sma"/>
<text x="540" y="260" text-anchor="middle" class="pin-label-white">RF IN</text> <text x="540" y="228" text-anchor="middle" class="pin-label-white">RF IN</text>
<ellipse cx="700" cy="230" rx="15" ry="20" class="sma"/> <ellipse cx="700" cy="200" rx="15" ry="20" class="sma"/>
<text x="700" y="260" text-anchor="middle" class="pin-label-white">RF OUT</text> <text x="700" y="228" text-anchor="middle" class="pin-label-white">RF OUT</text>
<!-- HMC472A Control Header (8-pin) --> <!-- HMC472A Control Header (8-pin) - optimized order -->
<g transform="translate(595, 280)"> <g transform="translate(595, 250)">
<rect x="0" y="0" width="25" height="10" class="pin"/><text x="30" y="9" class="pin-label-white">+5V</text> <rect x="0" y="0" width="25" height="10" class="pin"/><text x="30" y="9" class="pin-label-white">+5V</text>
<rect x="0" y="15" width="25" height="10" class="pin"/><text x="30" y="24" class="pin-label-white">V6 (0.5dB)</text> <rect x="0" y="15" width="25" height="10" class="pin"/><text x="30" y="24" class="pin-label-white">V6 (0.5dB) bit0</text>
<rect x="0" y="30" width="25" height="10" class="pin"/><text x="30" y="39" class="pin-label-white">V5 (1dB)</text> <rect x="0" y="30" width="25" height="10" class="pin"/><text x="30" y="39" class="pin-label-white">V5 (1dB) bit1</text>
<rect x="0" y="45" width="25" height="10" class="pin"/><text x="30" y="54" class="pin-label-white">V4 (2dB)</text> <rect x="0" y="45" width="25" height="10" class="pin"/><text x="30" y="54" class="pin-label-white">V4 (2dB) bit2</text>
<rect x="0" y="60" width="25" height="10" class="pin"/><text x="30" y="69" class="pin-label-white">V3 (4dB)</text> <rect x="0" y="60" width="25" height="10" class="pin"/><text x="30" y="69" class="pin-label-white">V3 (4dB) bit3</text>
<rect x="0" y="75" width="25" height="10" class="pin"/><text x="30" y="84" class="pin-label-white">V2 (8dB)</text> <rect x="0" y="75" width="25" height="10" class="pin"/><text x="30" y="84" class="pin-label-white">V2 (8dB) bit4</text>
<rect x="0" y="90" width="25" height="10" class="pin"/><text x="30" y="99" class="pin-label-white">V1 (16dB)</text> <rect x="0" y="90" width="25" height="10" class="pin"/><text x="30" y="99" class="pin-label-white">V1 (16dB) bit5</text>
<rect x="0" y="105" width="25" height="10" class="pin"/><text x="30" y="114" class="pin-label-white">GND</text> <rect x="0" y="105" width="25" height="10" class="pin"/><text x="30" y="114" class="pin-label-white">GND</text>
</g> </g>
<!-- Wiring: Power (5V) - Red --> <!-- Wiring: Power (5V) - Red -->
<path d="M 235 235 H 280 V 120 H 560 V 280 H 595" class="wire wire-power"/> <path d="M 235 200 H 280 V 100 H 560 V 250 H 595" class="wire wire-power"/>
<circle cx="235" cy="235" r="4" fill="#f44336"/> <circle cx="235" cy="200" r="4" fill="#f44336"/>
<circle cx="595" cy="285" r="4" fill="#f44336"/> <circle cx="595" cy="255" r="4" fill="#f44336"/>
<!-- Wiring: GND - Black --> <!-- Wiring: GND - Black -->
<path d="M 110 340 H 70 V 400 H 580 V 390 H 595" class="wire wire-gnd"/> <path d="M 110 305 H 65 V 375 H 575 V 360 H 595" class="wire wire-gnd"/>
<circle cx="110" cy="340" r="4" fill="#333"/> <circle cx="110" cy="305" r="4" fill="#333"/>
<circle cx="595" cy="390" r="4" fill="#333"/> <circle cx="595" cy="360" r="4" fill="#333"/>
<!-- Wiring: GPIO1 → V1 (16dB) - Blue --> <!-- Straight-through data wiring (optimized - no crossovers!) -->
<path d="M 110 250 H 50 V 415 H 590 V 375 H 595" class="wire wire-data"/> <!-- GPIO1 → V6 -->
<circle cx="110" cy="250" r="3" fill="#2196F3"/> <path d="M 110 215 H 60 V 365 H 590 V 270 H 595" class="wire wire-data"/>
<circle cx="595" cy="375" r="3" fill="#2196F3"/> <circle cx="110" cy="215" r="3" fill="#2196F3"/>
<circle cx="595" cy="270" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO2 → V2 (8dB) --> <!-- GPIO2 → V5 -->
<path d="M 110 265 H 55 V 410 H 585 V 360 H 595" class="wire wire-data"/> <path d="M 110 230 H 55 V 370 H 585 V 285 H 595" class="wire wire-data"/>
<circle cx="110" cy="265" r="3" fill="#2196F3"/> <circle cx="110" cy="230" r="3" fill="#2196F3"/>
<circle cx="595" cy="360" r="3" fill="#2196F3"/> <circle cx="595" cy="285" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO3 → V3 (4dB) --> <!-- GPIO3 → V4 -->
<path d="M 110 280 H 60 V 405 H 580 V 345 H 595" class="wire wire-data"/> <path d="M 110 245 H 50 V 375 H 580 V 300 H 595" class="wire wire-data"/>
<circle cx="110" cy="280" r="3" fill="#2196F3"/> <circle cx="110" cy="245" r="3" fill="#2196F3"/>
<circle cx="595" cy="345" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO4 → V4 (2dB) -->
<path d="M 110 295 H 65 V 395 H 575 V 330 H 595" class="wire wire-data"/>
<circle cx="110" cy="295" r="3" fill="#2196F3"/>
<circle cx="595" cy="330" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO5 → V5 (1dB) -->
<path d="M 110 310 H 70 V 385 H 570 V 315 H 595" class="wire wire-data"/>
<circle cx="110" cy="310" r="3" fill="#2196F3"/>
<circle cx="595" cy="315" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO6 → V6 (0.5dB) -->
<path d="M 110 325 H 75 V 375 H 565 V 300 H 595" class="wire wire-data"/>
<circle cx="110" cy="325" r="3" fill="#2196F3"/>
<circle cx="595" cy="300" r="3" fill="#2196F3"/> <circle cx="595" cy="300" r="3" fill="#2196F3"/>
<!-- GPIO4 → V3 -->
<path d="M 110 260 H 45 V 380 H 575 V 315 H 595" class="wire wire-data"/>
<circle cx="110" cy="260" r="3" fill="#2196F3"/>
<circle cx="595" cy="315" r="3" fill="#2196F3"/>
<!-- GPIO5 → V2 -->
<path d="M 110 275 H 40 V 385 H 570 V 330 H 595" class="wire wire-data"/>
<circle cx="110" cy="275" r="3" fill="#2196F3"/>
<circle cx="595" cy="330" r="3" fill="#2196F3"/>
<!-- GPIO6 → V1 -->
<path d="M 110 290 H 35 V 390 H 565 V 345 H 595" class="wire wire-data"/>
<circle cx="110" cy="290" r="3" fill="#2196F3"/>
<circle cx="595" cy="345" r="3" fill="#2196F3"/>
<!-- Legend --> <!-- Legend -->
<rect x="280" y="420" width="240" height="70" rx="5" class="note-box"/> <rect x="270" y="410" width="260" height="100" rx="5" class="note-box"/>
<text x="400" y="438" text-anchor="middle" style="font-weight:bold;" class="note">Wiring Summary</text> <text x="400" y="428" text-anchor="middle" style="font-weight:bold;" class="note">Optimized Bitwise Mapping</text>
<line x1="290" y1="445" x2="310" y2="445" stroke="#f44336" stroke-width="3"/>
<text x="315" y="449" class="note">Power (+5V)</text> <line x1="285" y1="445" x2="305" y2="445" stroke="#2196F3" stroke-width="3"/>
<line x1="400" y1="445" x2="420" y2="445" stroke="#333" stroke-width="3"/> <text x="310" y="449" class="note">Data: GPIO(n) → V(7-n)</text>
<text x="425" y="449" class="note">Ground</text>
<line x1="290" y1="465" x2="310" y2="465" stroke="#2196F3" stroke-width="3"/> <line x1="285" y1="465" x2="305" y2="465" stroke="#f44336" stroke-width="3"/>
<text x="315" y="469" class="note">Data (GPIO→Vx)</text> <text x="310" y="469" class="note">Power (+5V from USB)</text>
<text x="400" y="483" text-anchor="middle" class="note" style="font-style:italic;">Active-LOW: LOW=Attenuate, HIGH=Pass</text>
<line x1="285" y1="485" x2="305" y2="485" stroke="#333" stroke-width="3"/>
<text x="310" y="489" class="note">Ground</text>
<text x="400" y="503" text-anchor="middle" class="code">GPIO.out_w1tc = (step &amp; 0x3F) &lt;&lt; 1</text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -4,33 +4,51 @@
![Wiring Diagram](wiring-diagram.svg) ![Wiring Diagram](wiring-diagram.svg)
## Pin Mapping Table ## Pin Mapping (Optimized for Bitwise Ops)
| ESP32-S2 Mini | HMC472A Module | Function | Attenuation | The wiring is arranged so GPIO number = step bit position + 1, enabling single-instruction bitwise operations in firmware.
| ESP32-S2 Mini | HMC472A Module | Step Bit | Attenuation |
|---------------|----------------|----------|-------------| |---------------|----------------|----------|-------------|
| GPIO1 | V1 | Bit 5 (MSB) | 16 dB | | GPIO1 | V6 | Bit 0 (LSB) | 0.5 dB |
| GPIO2 | V2 | Bit 4 | 8 dB | | GPIO2 | V5 | Bit 1 | 1 dB |
| GPIO3 | V3 | Bit 3 | 4 dB | | GPIO3 | V4 | Bit 2 | 2 dB |
| GPIO4 | V4 | Bit 2 | 2 dB | | GPIO4 | V3 | Bit 3 | 4 dB |
| GPIO5 | V5 | Bit 1 | 1 dB | | GPIO5 | V2 | Bit 4 | 8 dB |
| GPIO6 | V6 | Bit 0 (LSB) | 0.5 dB | | GPIO6 | V1 | Bit 5 (MSB) | 16 dB |
| 5V (VBUS) | +5V | Power | — | | 5V (VBUS) | +5V | — | Power |
| GND | GND | Ground | — | | GND | GND | — | Ground |
### Why This Mapping?
```c
// Old approach (loop required):
for (int i = 0; i < 6; i++) {
pin = ATTEN_PINS[5-i]; // Reverse mapping
if (step & (1 << i)) set_low(pin);
}
// New approach (pure bitwise):
GPIO.out_w1tc = (step & 0x3F) << 1; // Set LOW where step=1
GPIO.out_w1ts = (~step & 0x3F) << 1 & 0x7E; // Set HIGH where step=0
```
The step value bits map directly to GPIO register positions. No loop, no lookup table.
## HMC472A Module Header Pinout ## HMC472A Module Header Pinout
The 8-pin header on the HMC472A module (top to bottom when viewing from component side): The 8-pin header on the HMC472A module (top to bottom when viewing from component side):
| Pin | Signal | Description | | Pin | Signal | ESP32 GPIO | Description |
|-----|----------|--------------------------| |-----|----------|------------|--------------------------|
| 1 | +5V | Power supply (+5V DC) | | 1 | +5V | 5V VBUS | Power supply (+5V DC) |
| 2 | V6 | 0.5 dB control (active-low) | | 2 | V6 | GPIO1 | 0.5 dB (step bit 0, LSB) |
| 3 | V5 | 1 dB control (active-low) | | 3 | V5 | GPIO2 | 1 dB (step bit 1) |
| 4 | V4 | 2 dB control (active-low) | | 4 | V4 | GPIO3 | 2 dB (step bit 2) |
| 5 | V3 | 4 dB control (active-low) | | 5 | V3 | GPIO4 | 4 dB (step bit 3) |
| 6 | V2 | 8 dB control (active-low) | | 6 | V2 | GPIO5 | 8 dB (step bit 4) |
| 7 | V1 | 16 dB control (active-low) | | 7 | V1 | GPIO6 | 16 dB (step bit 5, MSB) |
| 8 | GND | Ground | | 8 | GND | GND | Ground |
## Logic Levels ## Logic Levels
@ -42,36 +60,59 @@ The HMC472A accepts 05V TTL/CMOS logic. The ESP32-S2's 3.3V GPIO output is fu
## Attenuation Examples ## Attenuation Examples
| GPIO State (1-6) | Step | Total Attenuation | | Step | Binary | GPIO State (1-6) | Total Attenuation |
|------------------|------|-------------------| |------|------------|------------------|-------------------|
| `111111` (all HIGH) | 0 | 0 dB (insertion loss only) | | 0 | `0b000000` | `HHHHHH` | 0 dB |
| `111110` | 1 | 0.5 dB | | 1 | `0b000001` | `LHHHHH` | 0.5 dB |
| `111100` | 3 | 1.5 dB | | 5 | `0b000101` | `LHLHHH` | 2.5 dB |
| `110000` | 15 | 7.5 dB | | 31 | `0b011111` | `LLLLLH` | 15.5 dB |
| `100000` | 31 | 15.5 dB | | 63 | `0b111111` | `LLLLLL` | 31.5 dB |
| `000000` (all LOW) | 63 | 31.5 dB |
Formula: `attenuation = step × 0.5 dB` where step = 063 Formula: `attenuation = step × 0.5 dB` where step = 063
## Wiring Diagram (Text)
```
ESP32-S2 Mini HMC472A Module
(Left Header) (8-pin Header)
3V3 ─┤ 1 ┌─────┐
──────────────────────────────┐ │ +5V │← 1
GPIO1 ─┤ 2 ───────────────────│ V6 │← 2 (0.5 dB)
GPIO2 ─┤ 3 ───────────────────│ V5 │← 3 (1 dB)
GPIO3 ─┤ 4 ───────────────────│ V4 │← 4 (2 dB)
GPIO4 ─┤ 5 ───────────────────│ V3 │← 5 (4 dB)
GPIO5 ─┤ 6 ───────────────────│ V2 │← 6 (8 dB)
GPIO6 ─┤ 7 ───────────────────│ V1 │← 7 (16 dB)
GND ─┤ 8 ───────────────────│ GND │← 8
└─────┘
(Right Header)
5V ─┤ 16 ─────────────────┘ (to +5V)
```
**Note:** The diagram shows straight-through wiring (no crossovers) because the optimized mapping aligns GPIO numbers with the module header in reverse order.
## ESP32-S2 Mini Physical Pinout ## ESP32-S2 Mini Physical Pinout
``` ```
┌─────────────────┐ ┌─────────────────┐
│ USB-C │ │ USB-C │
└─────────────────┘ └─────────────────┘
3V3 ─┤ 1 16 ├─ 5V (VBUS) ◄── Power to HMC472A 3V3 ─┤ 1 16 ├─ 5V (VBUS) ──► +5V
GPIO1 ─┤ 2 15 ├─ GPIO15 (LED) GPIO1 ─┤ 2 15 ├─ GPIO15 (LED) │
GPIO2 ─┤ 3 14 ├─ GPIO14 GPIO2 ─┤ 3 14 ├─ GPIO14 │
GPIO3 ─┤ 4 13 ├─ GPIO13 GPIO3 ─┤ 4 13 ├─ GPIO13 │
GPIO4 ─┤ 5 12 ├─ GPIO12 GPIO4 ─┤ 5 12 ├─ GPIO12 │
GPIO5 ─┤ 6 11 ├─ GPIO11 GPIO5 ─┤ 6 11 ├─ GPIO11 │
GPIO6 ─┤ 7 10 ├─ GPIO10 GPIO6 ─┤ 7 10 ├─ GPIO10 │
GND ─┤ 8 9 ├─ GPIO9 GND ─┤ 8 9 ├─ GPIO9 │
└─────────────────┘ └─────────────────┘ │
(Left header) ↓ ↓ ↓ ↓ ↓ ↓ │
V6 V5 V4 V3 V2 V1 ◄───────────┘
(HMC472A control pins + power)
``` ```
GPIOs 16 are on the left header, pins 27. Convenient sequential layout for ribbon cable. GPIOs 16 are on the left header, pins 27. Direct ribbon cable from S2 Mini left header to HMC472A control header (reversed).
## KiCad Schematic ## KiCad Schematic
@ -81,5 +122,6 @@ A full KiCad schematic is available at `hmc472-controller.kicad_sch` for detaile
1. **No level shifting required** — ESP32-S2 3.3V GPIO drives HMC472A directly 1. **No level shifting required** — ESP32-S2 3.3V GPIO drives HMC472A directly
2. **Boot state** — GPIOs default HIGH at boot = 0 dB attenuation (safe) 2. **Boot state** — GPIOs default HIGH at boot = 0 dB attenuation (safe)
3. **Glitch-free switching** — Firmware uses register-level writes (`GPIO.out_w1ts`/`GPIO.out_w1tc`) to change all 6 bits atomically 3. **Glitch-free switching** — Firmware uses register-level writes (`GPIO.out_w1ts`/`GPIO.out_w1tc`)
4. **Power** — The S2 Mini's 5V pin sources USB VBUS directly, sufficient for HMC472A's 2.5 mA draw 4. **Power** — The S2 Mini's 5V pin sources USB VBUS directly, sufficient for HMC472A's 2.5 mA draw
5. **Bitwise optimization** — GPIO = bit + 1 mapping eliminates loop in firmware