diff options
Diffstat (limited to 'keyboards/cannonkeys/satisfaction75/led.c')
-rw-r--r-- | keyboards/cannonkeys/satisfaction75/led.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/keyboards/cannonkeys/satisfaction75/led.c b/keyboards/cannonkeys/satisfaction75/led.c new file mode 100644 index 000000000..39ff4d784 --- /dev/null +++ b/keyboards/cannonkeys/satisfaction75/led.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | Copyright 2012 Jun Wako <[email protected]> | ||
3 | |||
4 | This program is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation, either version 2 of the License, or | ||
7 | (at your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <hal.h> | ||
19 | #include "led_custom.h" | ||
20 | #include "satisfaction75.h" | ||
21 | |||
22 | static void breathing_callback(PWMDriver *pwmp); | ||
23 | |||
24 | static PWMConfig pwmCFG = { | ||
25 | 0xFFFF, /* PWM clock frequency */ | ||
26 | 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ | ||
27 | NULL, /* No Callback */ | ||
28 | { | ||
29 | {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */ | ||
30 | {PWM_OUTPUT_DISABLED, NULL}, | ||
31 | {PWM_OUTPUT_DISABLED, NULL}, | ||
32 | {PWM_OUTPUT_DISABLED, NULL} | ||
33 | }, | ||
34 | 0, /* HW dependent part.*/ | ||
35 | 0 | ||
36 | }; | ||
37 | |||
38 | static PWMConfig pwmCFG_breathing = { | ||
39 | 0xFFFF, /* 10kHz PWM clock frequency */ | ||
40 | 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ | ||
41 | breathing_callback, /* Breathing Callback */ | ||
42 | { | ||
43 | {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */ | ||
44 | {PWM_OUTPUT_DISABLED, NULL}, | ||
45 | {PWM_OUTPUT_DISABLED, NULL}, | ||
46 | {PWM_OUTPUT_DISABLED, NULL} | ||
47 | }, | ||
48 | 0, /* HW dependent part.*/ | ||
49 | 0 | ||
50 | }; | ||
51 | |||
52 | // See http://jared.geek.nz/2013/feb/linear-led-pwm | ||
53 | static uint16_t cie_lightness(uint16_t v) { | ||
54 | if (v <= 5243) // if below 8% of max | ||
55 | return v / 9; // same as dividing by 900% | ||
56 | else { | ||
57 | uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare | ||
58 | // to get a useful result with integer division, we shift left in the expression above | ||
59 | // and revert what we've done again after squaring. | ||
60 | y = y * y * y >> 8; | ||
61 | if (y > 0xFFFFUL) // prevent overflow | ||
62 | return 0xFFFFU; | ||
63 | else | ||
64 | return (uint16_t) y; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | |||
69 | void backlight_init_ports(void) { | ||
70 | palSetPadMode(GPIOA, 6, PAL_MODE_ALTERNATE(1)); | ||
71 | pwmStart(&PWMD3, &pwmCFG); | ||
72 | if(kb_backlight_config.enable){ | ||
73 | if(kb_backlight_config.breathing){ | ||
74 | breathing_enable(); | ||
75 | } else{ | ||
76 | backlight_set(kb_backlight_config.level); | ||
77 | } | ||
78 | } else { | ||
79 | backlight_set(0); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | void suspend_power_down_user(void) { | ||
84 | backlight_set(0); | ||
85 | } | ||
86 | void suspend_wakeup_init_user(void) { | ||
87 | if(kb_backlight_config.enable){ | ||
88 | if(kb_backlight_config.breathing){ | ||
89 | breathing_enable(); | ||
90 | } else{ | ||
91 | backlight_set(kb_backlight_config.level); | ||
92 | } | ||
93 | } else { | ||
94 | backlight_set(0); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | void backlight_set(uint8_t level) { | ||
99 | uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t) level / BACKLIGHT_LEVELS)); | ||
100 | if (level == 0) { | ||
101 | // Turn backlight off | ||
102 | pwmDisableChannel(&PWMD3, 0); | ||
103 | } else { | ||
104 | // Turn backlight on | ||
105 | if(!is_breathing()){ | ||
106 | pwmEnableChannel(&PWMD3, 0, PWM_FRACTION_TO_WIDTH(&PWMD3,0xFFFF,duty)); | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | |||
112 | uint8_t backlight_tick = 0; | ||
113 | |||
114 | void backlight_task(void) { | ||
115 | } | ||
116 | |||
117 | #define BREATHING_NO_HALT 0 | ||
118 | #define BREATHING_HALT_OFF 1 | ||
119 | #define BREATHING_HALT_ON 2 | ||
120 | #define BREATHING_STEPS 128 | ||
121 | |||
122 | static uint8_t breathing_period = BREATHING_PERIOD; | ||
123 | static uint8_t breathing_halt = BREATHING_NO_HALT; | ||
124 | static uint16_t breathing_counter = 0; | ||
125 | |||
126 | bool is_breathing(void) { | ||
127 | return PWMD3.config == &pwmCFG_breathing; | ||
128 | } | ||
129 | |||
130 | #define breathing_min() do {breathing_counter = 0;} while (0) | ||
131 | #define breathing_max() do {breathing_counter = breathing_period * 256 / 2;} while (0) | ||
132 | |||
133 | |||
134 | void breathing_interrupt_enable(void){ | ||
135 | pwmStop(&PWMD3); | ||
136 | pwmStart(&PWMD3, &pwmCFG_breathing); | ||
137 | chSysLockFromISR(); | ||
138 | pwmEnablePeriodicNotification(&PWMD3); | ||
139 | pwmEnableChannelI( | ||
140 | &PWMD3, | ||
141 | 0, | ||
142 | PWM_FRACTION_TO_WIDTH( | ||
143 | &PWMD3, | ||
144 | 0xFFFF, | ||
145 | 0xFFFF | ||
146 | ) | ||
147 | ); | ||
148 | chSysUnlockFromISR(); | ||
149 | } | ||
150 | |||
151 | void breathing_interrupt_disable(void){ | ||
152 | pwmStop(&PWMD3); | ||
153 | pwmStart(&PWMD3, &pwmCFG); | ||
154 | } | ||
155 | |||
156 | void breathing_enable(void) | ||
157 | { | ||
158 | breathing_counter = 0; | ||
159 | breathing_halt = BREATHING_NO_HALT; | ||
160 | breathing_interrupt_enable(); | ||
161 | } | ||
162 | |||
163 | void breathing_pulse(void) | ||
164 | { | ||
165 | if (kb_backlight_config.level == 0) | ||
166 | breathing_min(); | ||
167 | else | ||
168 | breathing_max(); | ||
169 | breathing_halt = BREATHING_HALT_ON; | ||
170 | breathing_interrupt_enable(); | ||
171 | } | ||
172 | |||
173 | void breathing_disable(void) | ||
174 | { | ||
175 | breathing_interrupt_disable(); | ||
176 | // Restore backlight level | ||
177 | backlight_set(kb_backlight_config.level); | ||
178 | } | ||
179 | |||
180 | void breathing_self_disable(void) | ||
181 | { | ||
182 | if (kb_backlight_config.level == 0) | ||
183 | breathing_halt = BREATHING_HALT_OFF; | ||
184 | else | ||
185 | breathing_halt = BREATHING_HALT_ON; | ||
186 | } | ||
187 | |||
188 | void breathing_toggle(void) { | ||
189 | if (is_breathing()){ | ||
190 | breathing_disable(); | ||
191 | } else { | ||
192 | breathing_enable(); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | void breathing_period_set(uint8_t value) | ||
197 | { | ||
198 | if (!value) | ||
199 | value = 1; | ||
200 | breathing_period = value; | ||
201 | } | ||
202 | |||
203 | void breathing_period_default(void) { | ||
204 | breathing_period_set(BREATHING_PERIOD); | ||
205 | } | ||
206 | |||
207 | void breathing_period_inc(void) | ||
208 | { | ||
209 | breathing_period_set(breathing_period+1); | ||
210 | } | ||
211 | |||
212 | void breathing_period_dec(void) | ||
213 | { | ||
214 | breathing_period_set(breathing_period-1); | ||
215 | } | ||
216 | |||
217 | /* To generate breathing curve in python: | ||
218 | * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] | ||
219 | */ | ||
220 | static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||
221 | |||
222 | // Use this before the cie_lightness function. | ||
223 | static inline uint16_t scale_backlight(uint16_t v) { | ||
224 | return v / BACKLIGHT_LEVELS * kb_backlight_config.level; | ||
225 | } | ||
226 | |||
227 | static void breathing_callback(PWMDriver *pwmp) | ||
228 | { | ||
229 | (void)pwmp; | ||
230 | uint16_t interval = (uint16_t) breathing_period * 256 / BREATHING_STEPS; | ||
231 | // resetting after one period to prevent ugly reset at overflow. | ||
232 | breathing_counter = (breathing_counter + 1) % (breathing_period * 256); | ||
233 | uint8_t index = breathing_counter / interval % BREATHING_STEPS; | ||
234 | |||
235 | if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || | ||
236 | ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) | ||
237 | { | ||
238 | breathing_interrupt_disable(); | ||
239 | } | ||
240 | |||
241 | uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); | ||
242 | |||
243 | chSysLockFromISR(); | ||
244 | pwmEnableChannelI( | ||
245 | &PWMD3, | ||
246 | 0, | ||
247 | PWM_FRACTION_TO_WIDTH( | ||
248 | &PWMD3, | ||
249 | 0xFFFF, | ||
250 | duty | ||
251 | ) | ||
252 | ); | ||
253 | chSysUnlockFromISR(); | ||
254 | } | ||