pwr_switch.cpp 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #include "pwr_switch.h"
  2. #include "bonds.h" // NSLOT, g_slot, g_connReplyMs
  3. #include "triton.h" // TB_STEAM, g_in
  4. #include <Arduino.h>
  5. #include <Adafruit_TinyUSB.h> // USBDevice.mounted()
  6. #define PWR_SWITCH_RELEASED ((PWR_SWITCH_ACTIVE) == HIGH ? LOW : HIGH)
  7. #define PULSE_MS 300u // motherboard power-header pulse width
  8. #define HOST_OFF_DEBOUNCE_MS \
  9. 1500u // USB must read "not mounted" this long before we trust it
  10. #define RETRIGGER_COOLDOWN_MS 5000u // minimum spacing between pulses
  11. static bool s_pulseActive = false;
  12. static unsigned long s_pulseStartMs = 0;
  13. static unsigned long s_hostOffSinceMs = 0;
  14. static unsigned long s_lastFireMs = 0;
  15. static bool s_everFired = false;
  16. // per-slot: with round-robin polling a shared static would get clobbered by other slots' reports
  17. // between press and release (same reasoning as rf_link.cpp's own Steam-short-press wake detector)
  18. static bool steamWasDown[NSLOT] = {};
  19. static unsigned long steamDownMs[NSLOT] = {};
  20. void pwrSwitchInit()
  21. {
  22. pinMode(PWR_SWITCH_PIN, OUTPUT);
  23. digitalWrite(PWR_SWITCH_PIN, PWR_SWITCH_RELEASED);
  24. }
  25. static void firePulse()
  26. {
  27. digitalWrite(PWR_SWITCH_PIN, PWR_SWITCH_ACTIVE);
  28. s_pulseActive = true;
  29. s_pulseStartMs = millis();
  30. s_lastFireMs = s_pulseStartMs;
  31. s_everFired = true;
  32. }
  33. void pwrSwitchTask()
  34. {
  35. // non-blocking release of an in-flight pulse
  36. if (s_pulseActive && millis() - s_pulseStartMs >= PULSE_MS) {
  37. digitalWrite(PWR_SWITCH_PIN, PWR_SWITCH_RELEASED);
  38. s_pulseActive = false;
  39. }
  40. // Host-off debounce: USBDevice.mounted() must read false CONTINUOUSLY for HOST_OFF_DEBOUNCE_MS.
  41. // Resets the instant it reads true, so a genuine boot/enumeration can never look like "off".
  42. if (USBDevice.mounted()) {
  43. s_hostOffSinceMs = 0;
  44. } else if (s_hostOffSinceMs == 0) {
  45. s_hostOffSinceMs = millis();
  46. }
  47. bool hostOff = s_hostOffSinceMs != 0 &&
  48. millis() - s_hostOffSinceMs >= HOST_OFF_DEBOUNCE_MS;
  49. for (int s = 0; s < NSLOT; s++) {
  50. if (!g_slot[s].used)
  51. continue;
  52. // A dropped link forces g_in[s] to zero (rf_link.cpp's own down-edge handling), which
  53. // would otherwise look like a same-frame button release here -- only trust g_in while
  54. // this slot is actually link-up (same 300ms threshold as anySlotLinkUp()).
  55. if (millis() - g_connReplyMs[s] >= 300u) {
  56. steamWasDown[s] = false;
  57. continue;
  58. }
  59. bool steamNow = (g_in[s].buttons & TB_STEAM) != 0;
  60. if (steamNow && !steamWasDown[s])
  61. steamDownMs[s] = millis();
  62. if (!steamNow && steamWasDown[s] &&
  63. millis() - steamDownMs[s] < 1000u && hostOff &&
  64. !s_pulseActive &&
  65. (!s_everFired ||
  66. millis() - s_lastFireMs >= RETRIGGER_COOLDOWN_MS)) {
  67. firePulse();
  68. }
  69. steamWasDown[s] = steamNow;
  70. }
  71. }