# Rotary Encoder — Setpoint Control Wiring Adds an EC11-class incremental rotary encoder to the kttl controller so the temperature **setpoint** can be dialled in live, and so the encoder's **push-switch** acts as the on-switch (identical to the board's B1 button). Target board: **STM32F4-Discovery (STM32F411VE)**. Firmware: `src/main.rs` (`SetpointKnob`, TIM1 in encoder mode). --- ## 1. Pin assignment | Encoder leg | Discovery pin | Function | Extra parts | |-----------------|------------------|---------------------------|-----------------------------| | **A** (CLK) | **PE9** (TIM1_CH1) | quadrature channel A | 10 kΩ → 3V, 100 nF → GND | | **B** (DT) | **PE11** (TIM1_CH2) | quadrature channel B | 10 kΩ → 3V, 100 nF → GND | | **C** (common) | **GND** | rotary common | — | | **SW1** | **3V** | push-switch leg | — | | **SW2** | **PA0** | push-switch leg (‖ B1) | — | | mount ×2 | **GND** | shield / mechanical | optional | > **Use 3V, not 5V.** The STM32F411 is a 3.3 V part; the pull-ups set the logic > high level seen by the pins, and the switch drives PA0 directly. 3V matches > the MCU and the existing B1 button exactly — no level shifting. --- ## 2. Schematic ``` STM32F4-Discovery Rotary encoder (EC11, 7-leg) ┌──────────────┐ │ 3V ●───────────┬────────────┬──────────────┐ │ │ │ │ │ │ │ ┌┴┐ ┌┴┐ │ │ │ 10k │ │ 10k │ │ │ (pull-ups │ │ └┬┘ └┬┘ │ to 3V) │ │ │ │ │ │ PE9 (CH1) ●───────────┼────────────● A (CLK) │ │ │ │ │ │ │ │ 100n ═╪═ 100n ═╪═ │ │ │ │ │ │ │ PE11 (CH2) ●───────────●────────────┼──● B (DT) │ │ │ │ │ │ ● C (common)──┼──┐ │ │ │ │ │ │ SW1 ●────┘ │ │ PA0 ●──────────────────────────────────● SW2 │ (push-switch, ‖ B1) │ GND ●──────┬───────────────────────────────────┘ │ │ │ mount legs ●● → GND (shield) └──────────────┘ │ │ B1 (on-board) also sits 3V ↔ PA0 — ┌─────┴──────┐ the encoder push-switch is in │ both 100n │ PARALLEL with it. │ caps → GND │ └────────────┘ ``` --- ## 3. Breadboard view ``` RC filter — repeat for each channel (A, B): 3V rail ●━━━┯━━━━━━━━━━━┯━━━━● ← from Discovery 3V │ │ [10k] [10k] A node ┼───→ PE9 │ B node │ ┼───→ PE11 [100n] [100n] │ │ GND rail ●━━━┷━━━━━━━━━━━┷━━━━● ← from Discovery GND EC11 encoder — straddles the centre trench (┊): ┌─────────────────────────────┐ │ A C B ┊ SW1 SW2 │ └──┬────┬────┬───────┬─────┬──┘ │ │ │ │ └───────→ PA0 (SW2 ‖ on-board B1 button) │ │ │ └─────────────→ 3V rail (SW1) │ │ └─────────────────────→ B node (DT, channel B → PE11) │ └──────────────────────────→ GND rail (C = common) └───────────────────────────────→ A node (CLK, channel A → PE9) ``` Power both breadboard rails from the Discovery's **3V** and **GND** pins; tap everything else off those rails. --- ## 4. Why the resistors and caps - **10 kΩ pull-ups (A→3V, B→3V):** *required.* This HAL's QEI driver puts the pins in alternate-function mode without enabling the internal pull-ups, so the channels would float. With the encoder common tied to GND, the lines idle high and each detent pulls them low. - **100 nF caps (A→GND, B→GND):** *recommended.* Each forms an RC low-pass with its 10 kΩ pull-up, τ = R·C = 10 kΩ · 100 nF = **1 ms** — long enough to absorb the ~1–5 ms of mechanical contact bounce, short enough that no hand-turned knob outruns it. Combined with the STM32 inputs' Schmitt-trigger hysteresis, this turns each bounced click into one clean edge. The cap's closure surge also provides wetting current to keep the low-current (~0.3 mA) contacts reliable. --- ## 5. Behaviour (firmware) - **TIM1 encoder mode** counts quadrature edges (×4). EC11s emit one full cycle per detent → **4 counter steps per detent** (`COUNTS_PER_DETENT`). - Each detent moves the setpoint by **`SETPOINT_STEP_C` (1 °C)**, clamped to **`[SETPOINT_MIN_C, SETPOINT_MAX_C]` = [25, 100] °C**. Power-on default is **`SETPOINT_DEFAULT_C` = 25 °C**. - The encoder is sampled every `POLL_MS` (20 ms) in the main poll loops (normal and fault), so the setpoint tracks smoothly and the per-second status log prints the live value. - **Push-switch:** wired in parallel with B1 (3V ↔ PA0). Pressing the knob is electrically identical to the board button and is handled by the existing PA0 polling — no extra code. --- ## 6. Alternatives - **Drop the caps:** enable TIM1's built-in digital input filter (sample-count debounce) in firmware instead. The RC filter is simpler and also gives wetting current + on-wire noise rejection, so it's preferred for a breadboard build. - **KY-040 module:** has onboard pull-ups — power it from **3.3 V** (not 5 V) so CLK/DT/SW idle at a level the MCU pins accept; the external 10 kΩ/100 nF here can then be omitted.