suspend.c 4.5 KB

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