suspend.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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 "backlight.h"
  8. #include "suspend_avr.h"
  9. #include "suspend.h"
  10. #include "timer.h"
  11. #include "led.h"
  12. #include "host.h"
  13. #ifdef PROTOCOL_LUFA
  14. #include "lufa.h"
  15. #endif
  16. #ifdef AUDIO_ENABLE
  17. #include "audio.h"
  18. #endif /* AUDIO_ENABLE */
  19. #ifdef RGBLIGHT_ANIMATIONS
  20. #include "rgblight.h"
  21. #endif
  22. #define wdt_intr_enable(value) \
  23. __asm__ __volatile__ ( \
  24. "in __tmp_reg__,__SREG__" "\n\t" \
  25. "cli" "\n\t" \
  26. "wdr" "\n\t" \
  27. "sts %0,%1" "\n\t" \
  28. "out __SREG__,__tmp_reg__" "\n\t" \
  29. "sts %0,%2" "\n\t" \
  30. : /* no outputs */ \
  31. : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
  32. "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
  33. "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \
  34. _BV(WDIE) | (value & 0x07)) ) \
  35. : "r0" \
  36. )
  37. void suspend_idle(uint8_t time)
  38. {
  39. cli();
  40. set_sleep_mode(SLEEP_MODE_IDLE);
  41. sleep_enable();
  42. sei();
  43. sleep_cpu();
  44. sleep_disable();
  45. }
  46. #ifndef NO_SUSPEND_POWER_DOWN
  47. /* Power down MCU with watchdog timer
  48. * wdto: watchdog timer timeout defined in <avr/wdt.h>
  49. * WDTO_15MS
  50. * WDTO_30MS
  51. * WDTO_60MS
  52. * WDTO_120MS
  53. * WDTO_250MS
  54. * WDTO_500MS
  55. * WDTO_1S
  56. * WDTO_2S
  57. * WDTO_4S
  58. * WDTO_8S
  59. */
  60. static uint8_t wdt_timeout = 0;
  61. static void power_down(uint8_t wdto)
  62. {
  63. #ifdef PROTOCOL_LUFA
  64. if (USB_DeviceState == DEVICE_STATE_Configured) return;
  65. #endif
  66. wdt_timeout = wdto;
  67. // Watchdog Interrupt Mode
  68. wdt_intr_enable(wdto);
  69. #ifdef BACKLIGHT_ENABLE
  70. backlight_set(0);
  71. #endif
  72. // Turn off LED indicators
  73. led_set(0);
  74. #ifdef AUDIO_ENABLE
  75. // This sometimes disables the start-up noise, so it's been disabled
  76. // stop_all_notes();
  77. #endif /* AUDIO_ENABLE */
  78. #ifdef RGBLIGHT_SLEEP
  79. #ifdef RGBLIGHT_ANIMATIONS
  80. rgblight_timer_disable();
  81. #endif
  82. rgblight_disable();
  83. #endif
  84. // TODO: more power saving
  85. // See PicoPower application note
  86. // - I/O port input with pullup
  87. // - prescale clock
  88. // - BOD disable
  89. // - Power Reduction Register PRR
  90. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  91. sleep_enable();
  92. sei();
  93. sleep_cpu();
  94. sleep_disable();
  95. // Disable watchdog after sleep
  96. wdt_disable();
  97. }
  98. #endif
  99. void suspend_power_down(void)
  100. {
  101. #ifndef NO_SUSPEND_POWER_DOWN
  102. power_down(WDTO_15MS);
  103. #endif
  104. }
  105. __attribute__ ((weak)) void matrix_power_up(void) {}
  106. __attribute__ ((weak)) void matrix_power_down(void) {}
  107. bool suspend_wakeup_condition(void)
  108. {
  109. matrix_power_up();
  110. matrix_scan();
  111. matrix_power_down();
  112. for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
  113. if (matrix_get_row(r)) return true;
  114. }
  115. return false;
  116. }
  117. // run immediately after wakeup
  118. void suspend_wakeup_init(void)
  119. {
  120. // clear keyboard state
  121. clear_keyboard();
  122. #ifdef BACKLIGHT_ENABLE
  123. backlight_init();
  124. #endif
  125. led_set(host_keyboard_leds());
  126. #ifdef RGBLIGHT_SLEEP
  127. rgblight_enable();
  128. #ifdef RGBLIGHT_ANIMATIONS
  129. rgblight_timer_enable();
  130. #endif
  131. #endif
  132. }
  133. #ifndef NO_SUSPEND_POWER_DOWN
  134. /* watchdog timeout */
  135. ISR(WDT_vect)
  136. {
  137. // compensate timer for sleep
  138. switch (wdt_timeout) {
  139. case WDTO_15MS:
  140. timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
  141. break;
  142. default:
  143. ;
  144. }
  145. }
  146. #endif