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
// --- HMC472A Control Pins (active-low) ---
// GPIOs 1-6: contiguous block in lower 32-bit register
// Enables glitch-free simultaneous writes via GPIO.out_w1ts/w1tc
static constexpr uint8_t PIN_V1 = 1; // 16 dB (MSB)
static constexpr uint8_t PIN_V2 = 2; // 8 dB
static constexpr uint8_t PIN_V3 = 3; // 4 dB
static constexpr uint8_t PIN_V4 = 4; // 2 dB
static constexpr uint8_t PIN_V5 = 5; // 1 dB
static constexpr uint8_t PIN_V6 = 6; // 0.5 dB (LSB)
// Optimized mapping: GPIO number = step bit position + 1
// Enables single-instruction bitwise ops instead of loop
//
// Wiring (GPIO → HMC472A pin):
// GPIO1 → V6 (0.5 dB) = step bit 0 (LSB)
// GPIO2 → V5 (1 dB) = step bit 1
// GPIO3 → V4 (2 dB) = step bit 2
// 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 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
static constexpr uint32_t ATTEN_PIN_MASK = (1 << PIN_V1) | (1 << PIN_V2) | (1 << PIN_V3) |
(1 << PIN_V4) | (1 << PIN_V5) | (1 << PIN_V6);
// Bitmask: 0b01111110 = bits 1-6 in GPIO register
static constexpr uint32_t ATTEN_PIN_MASK = 0x7E;
// --- Status LED ---
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() {
// Calculate which pins should be HIGH (bit=0, not attenuating)
// and which should be LOW (bit=1, attenuating)
// Optimized bitwise GPIO update — no loop needed!
//
// 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:
// GPIO.out_w1ts = pins to set HIGH (write-1-to-set)
// GPIO.out_w1tc = pins to set LOW (write-1-to-clear)
// Example: step=5 (0b000101 = 2.5dB) → GPIO1,3 LOW, GPIO2,4,5,6 HIGH
uint32_t pins_high = 0; // Bits to set HIGH
uint32_t pins_low = 0; // Bits to set LOW
uint32_t step_bits = (_step & 0x3F) << 1; // Step value shifted to GPIO positions
for (uint8_t i = 0; i < 6; i++) {
uint32_t pin_bit = 1 << ATTEN_PINS[i];
if (getBit(i)) {
// 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;
// Atomic register writes for glitch-free update
GPIO.out_w1tc = step_bits; // Set LOW where step bit = 1
GPIO.out_w1ts = (~step_bits) & ATTEN_PIN_MASK; // Set HIGH where step bit = 0
}
void Attenuator::saveToNVS() {

View File

@ -1,47 +1,47 @@
<?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>
<style>
.board { fill: #1a472a; stroke: #0d2818; stroke-width: 2; }
.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-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-power { stroke: #f44336; }
.wire-gnd { stroke: #333; }
.title { font-family: sans-serif; font-size: 18px; font-weight: bold; fill: #333; }
.subtitle { font-family: sans-serif; font-size: 12px; fill: #666; }
.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; }
.pin { fill: #c9a227; stroke: #8b7355; stroke-width: 1; }
.sma { fill: #c9a227; stroke: #8b7355; stroke-width: 1; }
.usb { fill: #333; stroke: #222; stroke-width: 1; }
.chip { fill: #1a1a1a; stroke: #333; stroke-width: 1; }
.code { font-family: monospace; font-size: 10px; fill: #1565c0; }
</style>
</defs>
<!-- Title -->
<text x="400" y="30" 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="28" text-anchor="middle" class="title">HMC472A Attenuator Controller Wiring</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 -->
<rect x="80" y="100" width="180" height="280" rx="5" class="board"/>
<text x="170" y="125" 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>
<rect x="80" y="80" width="180" height="260" rx="5" class="board"/>
<text x="170" y="102" text-anchor="middle" class="board-label">ESP32-S2 Mini</text>
<text x="170" y="116" text-anchor="middle" class="pin-label-white">(WEMOS/LOLIN)</text>
<!-- USB-C on S2 Mini -->
<rect x="145" y="90" width="50" height="15" rx="3" class="usb"/>
<text x="170" y="101" text-anchor="middle" style="font-size:8px;fill:#888;">USB-C</text>
<rect x="145" y="70" width="50" height="14" rx="3" class="usb"/>
<text x="170" y="80" text-anchor="middle" style="font-size:8px;fill:#888;">USB-C</text>
<!-- ESP32 chip representation -->
<rect x="130" y="155" width="80" height="60" rx="2" class="chip"/>
<text x="170" y="190" text-anchor="middle" style="font-size:9px;fill:#666;">ESP32-S2</text>
<rect x="130" y="130" width="80" height="50" rx="2" class="chip"/>
<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) -->
<g transform="translate(85, 230)">
<!-- Pin rectangles -->
<!-- S2 Mini Pin Header (left side) -->
<g transform="translate(85, 195)">
<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="30" width="25" height="10" class="pin"/><text x="30" y="39" class="pin-label-white">GPIO2</text>
@ -53,86 +53,91 @@
</g>
<!-- 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="105" width="25" height="10" class="pin"/><text x="-5" y="114" text-anchor="end" class="pin-label-white">GND</text>
</g>
<!-- HMC472A Module Board -->
<rect x="520" y="100" width="200" height="280" rx="5" class="board"/>
<text x="620" y="125" 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>
<rect x="520" y="80" width="200" height="260" rx="5" class="board"/>
<text x="620" y="102" text-anchor="middle" class="board-label">HMC472A Module</text>
<text x="620" y="116" text-anchor="middle" class="pin-label-white">6-bit RF Attenuator</text>
<!-- HMC472A chip representation -->
<rect x="555" y="155" width="60" height="40" rx="2" class="chip"/>
<text x="585" y="180" text-anchor="middle" style="font-size:8px;fill:#666;">HMC472A</text>
<rect x="555" y="130" width="60" height="40" rx="2" class="chip"/>
<text x="585" y="155" text-anchor="middle" style="font-size:8px;fill:#666;">HMC472A</text>
<!-- SMA Connectors -->
<ellipse cx="540" y="230" rx="15" ry="20" class="sma"/>
<text x="540" y="260" text-anchor="middle" class="pin-label-white">RF IN</text>
<ellipse cx="700" cy="230" rx="15" ry="20" class="sma"/>
<text x="700" y="260" text-anchor="middle" class="pin-label-white">RF OUT</text>
<ellipse cx="540" cy="200" rx="15" ry="20" class="sma"/>
<text x="540" y="228" text-anchor="middle" class="pin-label-white">RF IN</text>
<ellipse cx="700" cy="200" rx="15" ry="20" class="sma"/>
<text x="700" y="228" text-anchor="middle" class="pin-label-white">RF OUT</text>
<!-- HMC472A Control Header (8-pin) -->
<g transform="translate(595, 280)">
<!-- HMC472A Control Header (8-pin) - optimized order -->
<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="15" width="25" height="10" class="pin"/><text x="30" y="24" class="pin-label-white">V6 (0.5dB)</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="45" width="25" height="10" class="pin"/><text x="30" y="54" class="pin-label-white">V4 (2dB)</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="75" width="25" height="10" class="pin"/><text x="30" y="84" class="pin-label-white">V2 (8dB)</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="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) bit1</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) bit3</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) bit5</text>
<rect x="0" y="105" width="25" height="10" class="pin"/><text x="30" y="114" class="pin-label-white">GND</text>
</g>
<!-- Wiring: Power (5V) - Red -->
<path d="M 235 235 H 280 V 120 H 560 V 280 H 595" class="wire wire-power"/>
<circle cx="235" cy="235" r="4" fill="#f44336"/>
<circle cx="595" cy="285" r="4" fill="#f44336"/>
<path d="M 235 200 H 280 V 100 H 560 V 250 H 595" class="wire wire-power"/>
<circle cx="235" cy="200" r="4" fill="#f44336"/>
<circle cx="595" cy="255" r="4" fill="#f44336"/>
<!-- Wiring: GND - Black -->
<path d="M 110 340 H 70 V 400 H 580 V 390 H 595" class="wire wire-gnd"/>
<circle cx="110" cy="340" r="4" fill="#333"/>
<circle cx="595" cy="390" r="4" fill="#333"/>
<path d="M 110 305 H 65 V 375 H 575 V 360 H 595" class="wire wire-gnd"/>
<circle cx="110" cy="305" r="4" fill="#333"/>
<circle cx="595" cy="360" r="4" fill="#333"/>
<!-- Wiring: GPIO1 → V1 (16dB) - Blue -->
<path d="M 110 250 H 50 V 415 H 590 V 375 H 595" class="wire wire-data"/>
<circle cx="110" cy="250" r="3" fill="#2196F3"/>
<circle cx="595" cy="375" r="3" fill="#2196F3"/>
<!-- Straight-through data wiring (optimized - no crossovers!) -->
<!-- GPIO1 → V6 -->
<path d="M 110 215 H 60 V 365 H 590 V 270 H 595" class="wire wire-data"/>
<circle cx="110" cy="215" r="3" fill="#2196F3"/>
<circle cx="595" cy="270" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO2 → V2 (8dB) -->
<path d="M 110 265 H 55 V 410 H 585 V 360 H 595" class="wire wire-data"/>
<circle cx="110" cy="265" r="3" fill="#2196F3"/>
<circle cx="595" cy="360" r="3" fill="#2196F3"/>
<!-- GPIO2 → V5 -->
<path d="M 110 230 H 55 V 370 H 585 V 285 H 595" class="wire wire-data"/>
<circle cx="110" cy="230" r="3" fill="#2196F3"/>
<circle cx="595" cy="285" r="3" fill="#2196F3"/>
<!-- Wiring: GPIO3 → V3 (4dB) -->
<path d="M 110 280 H 60 V 405 H 580 V 345 H 595" class="wire wire-data"/>
<circle cx="110" cy="280" 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"/>
<!-- GPIO3 → V4 -->
<path d="M 110 245 H 50 V 375 H 580 V 300 H 595" class="wire wire-data"/>
<circle cx="110" cy="245" 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 -->
<rect x="280" y="420" width="240" height="70" rx="5" class="note-box"/>
<text x="400" y="438" text-anchor="middle" style="font-weight:bold;" class="note">Wiring Summary</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="400" y1="445" x2="420" y2="445" stroke="#333" stroke-width="3"/>
<text x="425" y="449" class="note">Ground</text>
<line x1="290" y1="465" x2="310" y2="465" stroke="#2196F3" stroke-width="3"/>
<text x="315" y="469" class="note">Data (GPIO→Vx)</text>
<text x="400" y="483" text-anchor="middle" class="note" style="font-style:italic;">Active-LOW: LOW=Attenuate, HIGH=Pass</text>
<rect x="270" y="410" width="260" height="100" rx="5" class="note-box"/>
<text x="400" y="428" text-anchor="middle" style="font-weight:bold;" class="note">Optimized Bitwise Mapping</text>
<line x1="285" y1="445" x2="305" y2="445" stroke="#2196F3" stroke-width="3"/>
<text x="310" y="449" class="note">Data: GPIO(n) → V(7-n)</text>
<line x1="285" y1="465" x2="305" y2="465" stroke="#f44336" stroke-width="3"/>
<text x="310" y="469" class="note">Power (+5V from USB)</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>

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)
## 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 |
| GPIO2 | V2 | Bit 4 | 8 dB |
| GPIO3 | V3 | Bit 3 | 4 dB |
| GPIO4 | V4 | Bit 2 | 2 dB |
| GPIO5 | V5 | Bit 1 | 1 dB |
| GPIO6 | V6 | Bit 0 (LSB) | 0.5 dB |
| 5V (VBUS) | +5V | Power | — |
| GND | GND | Ground | — |
| GPIO1 | V6 | Bit 0 (LSB) | 0.5 dB |
| GPIO2 | V5 | Bit 1 | 1 dB |
| GPIO3 | V4 | Bit 2 | 2 dB |
| GPIO4 | V3 | Bit 3 | 4 dB |
| GPIO5 | V2 | Bit 4 | 8 dB |
| GPIO6 | V1 | Bit 5 (MSB) | 16 dB |
| 5V (VBUS) | +5V | — | Power |
| 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
The 8-pin header on the HMC472A module (top to bottom when viewing from component side):
| Pin | Signal | Description |
|-----|----------|--------------------------|
| 1 | +5V | Power supply (+5V DC) |
| 2 | V6 | 0.5 dB control (active-low) |
| 3 | V5 | 1 dB control (active-low) |
| 4 | V4 | 2 dB control (active-low) |
| 5 | V3 | 4 dB control (active-low) |
| 6 | V2 | 8 dB control (active-low) |
| 7 | V1 | 16 dB control (active-low) |
| 8 | GND | Ground |
| Pin | Signal | ESP32 GPIO | Description |
|-----|----------|------------|--------------------------|
| 1 | +5V | 5V VBUS | Power supply (+5V DC) |
| 2 | V6 | GPIO1 | 0.5 dB (step bit 0, LSB) |
| 3 | V5 | GPIO2 | 1 dB (step bit 1) |
| 4 | V4 | GPIO3 | 2 dB (step bit 2) |
| 5 | V3 | GPIO4 | 4 dB (step bit 3) |
| 6 | V2 | GPIO5 | 8 dB (step bit 4) |
| 7 | V1 | GPIO6 | 16 dB (step bit 5, MSB) |
| 8 | GND | GND | Ground |
## 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
| GPIO State (1-6) | Step | Total Attenuation |
|------------------|------|-------------------|
| `111111` (all HIGH) | 0 | 0 dB (insertion loss only) |
| `111110` | 1 | 0.5 dB |
| `111100` | 3 | 1.5 dB |
| `110000` | 15 | 7.5 dB |
| `100000` | 31 | 15.5 dB |
| `000000` (all LOW) | 63 | 31.5 dB |
| Step | Binary | GPIO State (1-6) | Total Attenuation |
|------|------------|------------------|-------------------|
| 0 | `0b000000` | `HHHHHH` | 0 dB |
| 1 | `0b000001` | `LHHHHH` | 0.5 dB |
| 5 | `0b000101` | `LHLHHH` | 2.5 dB |
| 31 | `0b011111` | `LLLLLH` | 15.5 dB |
| 63 | `0b111111` | `LLLLLL` | 31.5 dB |
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
```
┌─────────────────┐
│ USB-C │
└─────────────────┘
3V3 ─┤ 1 16 ├─ 5V (VBUS) ◄── Power to HMC472A
GPIO1 ─┤ 2 15 ├─ GPIO15 (LED)
GPIO2 ─┤ 3 14 ├─ GPIO14
GPIO3 ─┤ 4 13 ├─ GPIO13
GPIO4 ─┤ 5 12 ├─ GPIO12
GPIO5 ─┤ 6 11 ├─ GPIO11
GPIO6 ─┤ 7 10 ├─ GPIO10
GND ─┤ 8 9 ├─ GPIO9
└─────────────────┘
(Left header)
3V3 ─┤ 1 16 ├─ 5V (VBUS) ──► +5V
GPIO1 ─┤ 2 15 ├─ GPIO15 (LED) │
GPIO2 ─┤ 3 14 ├─ GPIO14 │
GPIO3 ─┤ 4 13 ├─ GPIO13 │
GPIO4 ─┤ 5 12 ├─ GPIO12 │
GPIO5 ─┤ 6 11 ├─ GPIO11 │
GPIO6 ─┤ 7 10 ├─ GPIO10 │
GND ─┤ 8 9 ├─ GPIO9 │
└─────────────────┘ │
↓ ↓ ↓ ↓ ↓ ↓ │
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
@ -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
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
5. **Bitwise optimization** — GPIO = bit + 1 mapping eliminates loop in firmware