You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
3.1 KiB

  1. #include <stdint.h>
  2. #include <avr/io.h>
  3. #include <avr/interrupt.h>
  4. #include <avr/pgmspace.h>
  5. #include "led.h"
  6. #include "sleep_led.h"
  7. #ifndef SLEEP_LED_TIMER
  8. # define SLEEP_LED_TIMER 1
  9. #endif
  10. #if SLEEP_LED_TIMER == 1
  11. # define TCCRxB TCCR1B
  12. # define TIMERx_COMPA_vect TIMER1_COMPA_vect
  13. # if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register
  14. # define TIMSKx TIMSK
  15. # else
  16. # define TIMSKx TIMSK1
  17. # endif
  18. # define OCIExA OCIE1A
  19. # define OCRxx OCR1A
  20. #elif SLEEP_LED_TIMER == 3
  21. # define TCCRxB TCCR3B
  22. # define TIMERx_COMPA_vect TIMER3_COMPA_vect
  23. # define TIMSKx TIMSK3
  24. # define OCIExA OCIE3A
  25. # define OCRxx OCR3A
  26. #else
  27. error("Invalid SLEEP_LED_TIMER config")
  28. #endif
  29. /* Software PWM
  30. * ______ ______ __
  31. * | ON |___OFF___| ON |___OFF___| ....
  32. * |<-------------->|<-------------->|<- ....
  33. * PWM period PWM period
  34. *
  35. * 256 interrupts/period[resolution]
  36. * 64 periods/second[frequency]
  37. * 256*64 interrupts/second
  38. * F_CPU/(256*64) clocks/interrupt
  39. */
  40. #define SLEEP_LED_TIMER_TOP F_CPU / (256 * 64)
  41. /** \brief Sleep LED initialization
  42. *
  43. * FIXME: needs doc
  44. */
  45. void sleep_led_init(void) {
  46. /* Timer1 setup */
  47. /* CTC mode */
  48. TCCRxB |= _BV(WGM12);
  49. /* Clock selelct: clk/1 */
  50. TCCRxB |= _BV(CS10);
  51. /* Set TOP value */
  52. uint8_t sreg = SREG;
  53. cli();
  54. OCRxx = SLEEP_LED_TIMER_TOP;
  55. SREG = sreg;
  56. }
  57. /** \brief Sleep LED enable
  58. *
  59. * FIXME: needs doc
  60. */
  61. void sleep_led_enable(void) {
  62. /* Enable Compare Match Interrupt */
  63. TIMSKx |= _BV(OCIExA);
  64. }
  65. /** \brief Sleep LED disable
  66. *
  67. * FIXME: needs doc
  68. */
  69. void sleep_led_disable(void) {
  70. /* Disable Compare Match Interrupt */
  71. TIMSKx &= ~_BV(OCIExA);
  72. }
  73. /** \brief Sleep LED toggle
  74. *
  75. * FIXME: needs doc
  76. */
  77. void sleep_led_toggle(void) {
  78. /* Disable Compare Match Interrupt */
  79. TIMSKx ^= _BV(OCIExA);
  80. }
  81. /** \brief Breathing Sleep LED brighness(PWM On period) table
  82. *
  83. * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
  84. *
  85. * https://www.wolframalpha.com/input/?i=sin%28x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
  86. * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
  87. */
  88. static const uint8_t breathing_table[64] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  89. ISR(TIMERx_COMPA_vect) {
  90. /* Software PWM
  91. * timer:1111 1111 1111 1111
  92. * \_____/\/ \_______/____ count(0-255)
  93. * \ \______________ duration of step(4)
  94. * \__________________ index of step table(0-63)
  95. */
  96. static union {
  97. uint16_t row;
  98. struct {
  99. uint8_t count : 8;
  100. uint8_t duration : 2;
  101. uint8_t index : 6;
  102. } pwm;
  103. } timer = {.row = 0};
  104. timer.row++;
  105. // LED on
  106. if (timer.pwm.count == 0) {
  107. led_set(1 << USB_LED_CAPS_LOCK);
  108. }
  109. // LED off
  110. if (timer.pwm.count == pgm_read_byte(&breathing_table[timer.pwm.index])) {
  111. led_set(0);
  112. }
  113. }