diff --git a/firmware/include/config.h b/firmware/include/config.h index 5346eef..6f29e7e 100644 --- a/firmware/include/config.h +++ b/firmware/include/config.h @@ -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 diff --git a/firmware/src/attenuator.cpp b/firmware/src/attenuator.cpp index b9846f4..a3d2fae 100644 --- a/firmware/src/attenuator.cpp +++ b/firmware/src/attenuator.cpp @@ -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() { diff --git a/hardware/wiring-diagram.svg b/hardware/wiring-diagram.svg index 2583556..436dcb4 100644 --- a/hardware/wiring-diagram.svg +++ b/hardware/wiring-diagram.svg @@ -1,47 +1,47 @@ - + - HMC472A Attenuator Controller Wiring - ESP32-S2 Mini to HMC472A Module Connection Diagram + HMC472A Attenuator Controller Wiring + Optimized Mapping: GPIO(n) = Step Bit (n-1) — Enables Direct Bitwise Ops - - ESP32-S2 Mini - (WEMOS/LOLIN) + + ESP32-S2 Mini + (WEMOS/LOLIN) - - USB-C + + USB-C - - ESP32-S2 + + ESP32-S2 - - - + + 3V3 GPIO1 GPIO2 @@ -53,86 +53,91 @@ - + 5V GND - - HMC472A Module - 6-bit RF Attenuator + + HMC472A Module + 6-bit RF Attenuator - - HMC472A + + HMC472A - - RF IN - - RF OUT + + RF IN + + RF OUT - - + + +5V - V6 (0.5dB) - V5 (1dB) - V4 (2dB) - V3 (4dB) - V2 (8dB) - V1 (16dB) + V6 (0.5dB) bit0 + V5 (1dB) bit1 + V4 (2dB) bit2 + V3 (4dB) bit3 + V2 (8dB) bit4 + V1 (16dB) bit5 GND - - - + + + - - - + + + - - - - + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - Wiring Summary - - Power (+5V) - - Ground - - Data (GPIO→Vx) - Active-LOW: LOW=Attenuate, HIGH=Pass + + Optimized Bitwise Mapping + + + Data: GPIO(n) → V(7-n) + + + Power (+5V from USB) + + + Ground + + GPIO.out_w1tc = (step & 0x3F) << 1 diff --git a/hardware/wiring.md b/hardware/wiring.md index 8034d00..8ec88f6 100644 --- a/hardware/wiring.md +++ b/hardware/wiring.md @@ -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 0–5V 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 = 0–63 +## 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 1–6 are on the left header, pins 2–7. Convenient sequential layout for ribbon cable. +GPIOs 1–6 are on the left header, pins 2–7. 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