RAK3401: reliable AIN1 button β interrupt latch, debounce, digital read, instant screen-on π€π€#2652
Closed
disq wants to merge 2 commits into
Closed
RAK3401: reliable AIN1 button β interrupt latch, debounce, digital read, instant screen-on π€π€#2652disq wants to merge 2 commits into
disq wants to merge 2 commits into
Conversation
β¦ad, instant screen-on The RAK3401 companion build drives its only user button (AIN1) by polling analogRead() once per main-loop iteration, with no interrupt. Presses are dropped whenever the loop briefly stalls (LoRa RX/TX, BLE, flash writes), and a single click waits out the full multi-click window before the screen wakes. MomentaryButton (generic): - Configurable multi-click window on the analog constructor (default unchanged). - reset(): abandon the in-flight gesture (used to swallow a screen-wake press). - enableInterrupt(): attach a permanent FALLING-edge GPIOTE interrupt that latches presses so they survive loop stalls (and can wake the MCU where the platform sleeps on interrupts). Debounced by counting one press per press and only re-arming after check() has seen a clean release β rejects press/release contact bounce. Skipped for analog/SAADC-threshold buttons (GPIOTE and analogRead conflict on one pin). companion_radio UITask: wake the display instantly on the press edge when it is off (instead of after the multi-click window), and arm the button interrupt at startup for PIN_USER_BTN_ANA boards. variants/rak3401: read the AIN1 button digitally (active-low, internal pull-up) instead of via SAADC. digitalRead coexists with the GPIOTE edge interrupt where analogRead does not (analogRead reads stuck-low once the interrupt is attached). Multi-click retained: single=NEXT / double=PREV / triple=SELECT / long=ENTER. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings meshcore-dev#2642's RAK3401 board pieces into this branch so the AIN1 button story is self-contained, using LPCOMP (not GPIO SENSE) as the hibernate wake source. AIN1 (P0.31) is wired as an analog button (pressed == analogRead() < threshold). GPIO SENSE can't wake on it: the digital input buffer reads the *released* idle level as LOW even though analogRead reports ~VDD, so arming SENSE_Low latches DETECT the instant we enter SYSTEMOFF β sd_power_system_off() returns immediately and the fall-through reset reboots ("instant wake"). LATCH/EVENTS_PORT clearing and release debounce don't help, because the released level itself reads LOW digitally. LPCOMP operates in the analog domain and sees the idle level correctly. Arm a DOWN crossing at ~1/2 VDD: released idles above threshold, a press pulls the pin below -> downward crossing -> wake. Wait for a confirmed release (analogRead above threshold) before arming, bounded by a 5s timeout so a stuck/low reading can't wedge shutdown. - NRF52Board::configureVoltageWake() gains a detect_down param (default false = UP crossing for voltage recovery; true = DOWN crossing for a button press), selecting ANADETECT Down/Up and INTENSET DOWN/UP accordingly. - RAK3401Board.h: powerOff() override routing through initiateShutdown(USER). - RAK3401Board::initiateShutdown() arms LPCOMP on AIN7 at REFSEL 3 (~1/2 VDD) for a USER shutdown when PIN_USER_BTN_ANA is defined. Channel/threshold overridable via PWRMGT_BTN_LPCOMP_AIN / PWRMGT_BTN_LPCOMP_REFSEL. Ports the fix from meshcore-dev#2642. Built: RAK_3401_companion_radio_ble. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
Probably not needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The RAK3401 companion build has a single user button on AIN1 (P0.31), driven by
analogRead()polled once per main-loop iteration β there is no interrupt serving it. Symptoms:This PR makes the AIN1 button story self-contained: reliable input and a working hibernate wake. It folds in #2642's RAK3401 board pieces (so that PR can be closed in favor of this one).
Changes
MomentaryButton(generic)reset()β abandon the in-flight gesture.enableInterrupt()β permanent FALLING-edge GPIOTE interrupt that latches presses so they survive loop stalls. Debounced: one count per press, re-armed only aftercheck()sees a clean release (rejects press/release contact bounce). Skipped for analog/SAADC-threshold buttons (GPIOTE vsanalogReadconflict on the same pin).companion_radio
UITaskPIN_USER_BTN_ANAboards.variants/rak3401β inputdigitalReadcoexists with the GPIOTE edge interrupt whereanalogReaddoes not (analogRead otherwise reads stuck "always pressed" once the interrupt is attached). Multi-click retained: single=NEXT / double=PREV / triple=SELECT / long=ENTER.Hibernate wake via LPCOMP (2nd commit; supersedes #2642's GPIO-SENSE approach)
NRF52Board::configureVoltageWake()gains adetect_downparam (defaultfalse= UP crossing for voltage recovery;true= DOWN crossing for a button press), selectingANADETECTDown/Up andINTENSETDOWN/UP accordingly.RAK3401Board.h:powerOff()override routing throughinitiateShutdown(USER).RAK3401Board::initiateShutdown(): whenPIN_USER_BTN_ANAis defined, arm LPCOMP on AIN7 at REFSEL 3 (~Β½ VDD) for a DOWN crossing as the SYSTEMOFF wake source. Waits for a confirmed release (analogRead above threshold) before arming, bounded by a 5 s timeout. Channel/threshold overridable viaPWRMGT_BTN_LPCOMP_AIN/PWRMGT_BTN_LPCOMP_REFSEL.Why LPCOMP, not GPIO SENSE
AIN1 is wired as an analog button (
pressed == analogRead() < threshold). Its digital input buffer reads the released idle level as LOW even thoughanalogReadreports ~VDD, so armingSENSE_Lowlatches DETECT the instant we enter SYSTEMOFF βsd_power_system_off()returns immediately and the fall-through reset reboots ("instant wake").LATCH/EVENTS_PORTclearing and release debounce don't help, because the released level itself reads LOW digitally. LPCOMP operates in the analog domain and sees the idle level correctly, so a DOWN-crossing arm works: released idles above threshold, a press pulls below β wake.Relationship to #2642
This supersedes #2642 β it ports #2642's
powerOff()override and AIN SYSTEMOFF wake, but replaces the GPIO-SENSE wake (which instant-wakes on this pin) with the LPCOMP approach. #2642 can be closed once this lands.Testing
RAK_3401_companion_radio_ble,RAK_3401_companion_radio_usb.companion_radio_blehardware: digital read fixes the SAADC+GPIOTE "always pressed" conflict; presses reliable through loop stalls; single/double/triple/long all work, no dropped or double-counted clicks.Known edge case
If the loop stalls through the middle of a fast double-click so
check()never re-arms between the two presses, the second press is dropped (fails toward "one click", never a phantom extra). Normal clicking is unaffected.π€ Generated with Claude Code