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