suspend.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #include <stdbool.h>
  2. #include <avr/sleep.h>
  3. #include <avr/wdt.h>
  4. #include <avr/interrupt.h>
  5. #include "matrix.h"
  6. #include "action.h"
  7. #include "suspend_avr.h"
  8. #include "suspend.h"
  9. #include "timer.h"
  10. #include "led.h"
  11. #include "host.h"
  12. #ifdef PROTOCOL_LUFA
  13. # include "lufa.h"
  14. #endif
  15. #ifdef BACKLIGHT_ENABLE
  16. # include "backlight.h"
  17. #endif
  18. #ifdef AUDIO_ENABLE
  19. # include "audio.h"
  20. #endif /* AUDIO_ENABLE */
  21. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  22. # include "rgblight.h"
  23. #endif
  24. /** \brief Suspend idle
  25. *
  26. * FIXME: needs doc
  27. */
  28. void suspend_idle(uint8_t time) {
  29. cli();
  30. set_sleep_mode(SLEEP_MODE_IDLE);
  31. sleep_enable();
  32. sei();
  33. sleep_cpu();
  34. sleep_disable();
  35. }
  36. // TODO: This needs some cleanup
  37. /** \brief Run keyboard level Power down
  38. *
  39. * FIXME: needs doc
  40. */
  41. __attribute__((weak)) void suspend_power_down_user(void) {}
  42. /** \brief Run keyboard level Power down
  43. *
  44. * FIXME: needs doc
  45. */
  46. __attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); }
  47. #ifndef NO_SUSPEND_POWER_DOWN
  48. /** \brief Power down MCU with watchdog timer
  49. *
  50. * wdto: watchdog timer timeout defined in <avr/wdt.h>
  51. * WDTO_15MS
  52. * WDTO_30MS
  53. * WDTO_60MS
  54. * WDTO_120MS
  55. * WDTO_250MS
  56. * WDTO_500MS
  57. * WDTO_1S
  58. * WDTO_2S
  59. * WDTO_4S
  60. * WDTO_8S
  61. */
  62. static uint8_t wdt_timeout = 0;
  63. /** \brief Power down
  64. *
  65. * FIXME: needs doc
  66. */
  67. static void power_down(uint8_t wdto) {
  68. # ifdef PROTOCOL_LUFA
  69. if (USB_DeviceState == DEVICE_STATE_Configured) return;
  70. # endif
  71. wdt_timeout = wdto;
  72. // Watchdog Interrupt Mode
  73. wdt_intr_enable(wdto);
  74. # ifdef BACKLIGHT_ENABLE
  75. backlight_set(0);
  76. # endif
  77. // Turn off LED indicators
  78. uint8_t leds_off = 0;
  79. # if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
  80. if (is_backlight_enabled()) {
  81. // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
  82. leds_off |= (1 << USB_LED_CAPS_LOCK);
  83. }
  84. # endif
  85. led_set(leds_off);
  86. # ifdef AUDIO_ENABLE
  87. // This sometimes disables the start-up noise, so it's been disabled
  88. // stop_all_notes();
  89. # endif /* AUDIO_ENABLE */
  90. # if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  91. rgblight_suspend();
  92. # endif
  93. suspend_power_down_kb();
  94. // TODO: more power saving
  95. // See PicoPower application note
  96. // - I/O port input with pullup
  97. // - prescale clock
  98. // - BOD disable
  99. // - Power Reduction Register PRR
  100. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  101. sleep_enable();
  102. sei();
  103. sleep_cpu();
  104. sleep_disable();
  105. // Disable watchdog after sleep
  106. wdt_disable();
  107. }
  108. #endif
  109. /** \brief Suspend power down
  110. *
  111. * FIXME: needs doc
  112. */
  113. void suspend_power_down(void) {
  114. suspend_power_down_kb();
  115. #ifndef NO_SUSPEND_POWER_DOWN
  116. power_down(WDTO_15MS);
  117. #endif
  118. }
  119. __attribute__((weak)) void matrix_power_up(void) {}
  120. __attribute__((weak)) void matrix_power_down(void) {}
  121. bool suspend_wakeup_condition(void) {
  122. matrix_power_up();
  123. matrix_scan();
  124. matrix_power_down();
  125. for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
  126. if (matrix_get_row(r)) return true;
  127. }
  128. return false;
  129. }
  130. /** \brief run user level code immediately after wakeup
  131. *
  132. * FIXME: needs doc
  133. */
  134. __attribute__((weak)) void suspend_wakeup_init_user(void) {}
  135. /** \brief run keyboard level code immediately after wakeup
  136. *
  137. * FIXME: needs doc
  138. */
  139. __attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
  140. /** \brief run immediately after wakeup
  141. *
  142. * FIXME: needs doc
  143. */
  144. void suspend_wakeup_init(void) {
  145. // clear keyboard state
  146. clear_keyboard();
  147. #ifdef BACKLIGHT_ENABLE
  148. backlight_init();
  149. #endif
  150. led_set(host_keyboard_leds());
  151. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  152. rgblight_wakeup();
  153. #endif
  154. suspend_wakeup_init_kb();
  155. }
  156. #ifndef NO_SUSPEND_POWER_DOWN
  157. /* watchdog timeout */
  158. ISR(WDT_vect) {
  159. // compensate timer for sleep
  160. switch (wdt_timeout) {
  161. case WDTO_15MS:
  162. timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
  163. break;
  164. default:;
  165. }
  166. }
  167. #endif