sleep_led.c (6014B)
1 #include <ch.h> 2 #include <hal.h> 3 4 #include "led.h" 5 #include "sleep_led.h" 6 7 /* All right, we go the "software" way: timer, toggle LED in interrupt. 8 * Based on hasu's code for AVRs. 9 * Use LP timer on Kinetises, TIM14 on STM32F0. 10 */ 11 12 #ifndef SLEEP_LED_GPT_DRIVER 13 # if defined(STM32F0XX) 14 # define SLEEP_LED_GPT_DRIVER GPTD14 15 # endif 16 #endif 17 18 #if defined(KL2x) || defined(K20x) || defined(SLEEP_LED_GPT_DRIVER) /* common parts for timers/interrupts */ 19 20 /* Breathing Sleep LED brighness(PWM On period) table 21 * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle 22 * 23 * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 24 * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } 25 */ 26 static const uint8_t breathing_table[64] = {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}; 27 28 void sleep_led_timer_callback(void) { 29 /* Software PWM 30 * timer:1111 1111 1111 1111 31 * \_____/\/ \_______/____ count(0-255) 32 * \ \______________ duration of step(4) 33 * \__________________ index of step table(0-63) 34 */ 35 36 // this works for cca 65536 irqs/sec 37 static union { 38 uint16_t row; 39 struct { 40 uint8_t count : 8; 41 uint8_t duration : 2; 42 uint8_t index : 6; 43 } pwm; 44 } timer = {.row = 0}; 45 static led_t led_state = {0}; 46 47 timer.row++; 48 49 // LED on 50 if (timer.pwm.count == 0) { 51 led_state.caps_lock = true; 52 led_set(led_state.raw); 53 } 54 // LED off 55 if (timer.pwm.count == breathing_table[timer.pwm.index]) { 56 led_state.caps_lock = false; 57 led_set(led_state.raw); 58 } 59 } 60 61 #endif /* common parts for known platforms */ 62 63 #if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */ 64 65 /* Use Low Power Timer (LPTMR) */ 66 # define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR 67 # define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF 68 69 /* LPTMR clock options */ 70 # define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ 71 # define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ 72 # define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */ 73 # define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ 74 75 /* Work around inconsistencies in Freescale naming */ 76 # if !defined(SIM_SCGC5_LPTMR) 77 # define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER 78 # endif 79 80 /* interrupt handler */ 81 OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { 82 OSAL_IRQ_PROLOGUE(); 83 84 sleep_led_timer_callback(); 85 86 /* Reset the counter */ 87 RESET_COUNTER; 88 89 OSAL_IRQ_EPILOGUE(); 90 } 91 92 /* Initialise the timer */ 93 void sleep_led_init(void) { 94 /* Make sure the clock to the LPTMR is enabled */ 95 SIM->SCGC5 |= SIM_SCGC5_LPTMR; 96 /* Reset LPTMR settings */ 97 LPTMR0->CSR = 0; 98 /* Set the compare value */ 99 LPTMR0->CMR = 0; // trigger on counter value (i.e. every time) 100 101 /* Set up clock source and prescaler */ 102 /* Software PWM 103 * ______ ______ __ 104 * | ON |___OFF___| ON |___OFF___| .... 105 * |<-------------->|<-------------->|<- .... 106 * PWM period PWM period 107 * 108 * R interrupts/period[resolution] 109 * F periods/second[frequency] 110 * R * F interrupts/second 111 */ 112 113 /* === OPTION 1 === */ 114 # if 0 115 // 1kHz LPO 116 // No prescaler => 1024 irqs/sec 117 // Note: this is too slow for a smooth breathe 118 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; 119 # endif /* OPTION 1 */ 120 121 /* === OPTION 2 === */ 122 # if 1 123 // nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) 124 MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock 125 # if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others 126 MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; 127 # endif /* KL27 */ 128 MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock 129 // to work in stop mode, also MCG_C1_IREFSTEN 130 // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec => 131 // => approx F=61, R=256, duration = 4 132 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK) | LPTMRx_PSR_PRESCALE(6); 133 # endif /* OPTION 2 */ 134 135 /* === OPTION 3 === */ 136 # if 0 137 // OSC output (external crystal), usually 8MHz or 16MHz 138 OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock 139 // to work in stop mode, also OSC_CR_EREFSTEN 140 // Divide by 2^N 141 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); 142 # endif /* OPTION 3 */ 143 /* === END OPTIONS === */ 144 145 /* Interrupt on TCF set (compare flag) */ 146 nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority 147 LPTMR0->CSR |= LPTMRx_CSR_TIE; 148 } 149 150 void sleep_led_enable(void) { 151 /* Enable the timer */ 152 LPTMR0->CSR |= LPTMRx_CSR_TEN; 153 } 154 155 void sleep_led_disable(void) { 156 /* Disable the timer */ 157 LPTMR0->CSR &= ~LPTMRx_CSR_TEN; 158 } 159 160 void sleep_led_toggle(void) { 161 /* Toggle the timer */ 162 LPTMR0->CSR ^= LPTMRx_CSR_TEN; 163 } 164 165 #elif defined(SLEEP_LED_GPT_DRIVER) 166 167 static void gptTimerCallback(GPTDriver *gptp) { 168 (void)gptp; 169 sleep_led_timer_callback(); 170 } 171 172 static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0}; 173 174 /* Initialise the timer */ 175 void sleep_led_init(void) { 176 gptStart(&SLEEP_LED_GPT_DRIVER, &gptcfg); 177 } 178 179 void sleep_led_enable(void) { 180 gptStartContinuous(&SLEEP_LED_GPT_DRIVER, gptcfg.frequency / 0xFFFF); 181 } 182 183 void sleep_led_disable(void) { 184 gptStopTimer(&SLEEP_LED_GPT_DRIVER); 185 } 186 187 void sleep_led_toggle(void) { 188 (SLEEP_LED_GPT_DRIVER.state == GPT_READY) ? sleep_led_enable() : sleep_led_disable(); 189 } 190 191 #else /* platform selection: not on familiar chips */ 192 193 void sleep_led_init(void) {} 194 195 void sleep_led_enable(void) { 196 led_set(2); // Caps Lock 197 } 198 199 void sleep_led_disable(void) { 200 led_set(0); 201 } 202 203 void sleep_led_toggle(void) { 204 // not implemented 205 } 206 207 #endif /* platform selection */