]>
Commit | Line | Data |
---|---|---|
1 | /* vim:ts=4 sts=4 et tw=80 | |
2 | * | |
3 | * fnordlicht firmware | |
4 | * | |
5 | * for additional information please | |
6 | * see http://lochraster.org/fnordlichtmini | |
7 | * | |
8 | * (c) by Alexander Neumann <alexander@bumpern.de> | |
9 | * Lars Noschinski <lars@public.noschinski.de> | |
10 | * | |
11 | * This program is free software: you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License version 3 as published by | |
13 | * the Free Software Foundation. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
18 | * more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along with | |
21 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | */ | |
23 | ||
24 | /* includes */ | |
25 | #include "globals.h" | |
26 | #include "../common/io.h" | |
27 | ||
28 | #include <stdint.h> | |
29 | #include <string.h> | |
30 | #include <avr/interrupt.h> | |
31 | #include <avr/pgmspace.h> | |
32 | ||
33 | #include "../common/common.h" | |
34 | #include "pwm.h" | |
35 | #include "timer.h" | |
36 | #include "remote.h" | |
37 | ||
38 | /* abbreviations for port, ddr and pin */ | |
39 | #define P_PORT _OUTPORT(PWM_PORT) | |
40 | #define P_DDR _DDRPORT(PWM_PORT) | |
41 | #define P2_PORT _OUTPORT(PWM2_PORT) | |
42 | #define P2_DDR _DDRPORT(PWM2_PORT) | |
43 | ||
44 | /* TYPES AND PROTOTYPES */ | |
45 | #define PWM_MAX_TIMESLOTS (2*(PWM_CHANNELS+2)) | |
46 | ||
47 | /* encapsulates all pwm data including timeslot and output mask array */ | |
48 | struct timeslot_t | |
49 | { | |
50 | uint8_t mask; | |
51 | uint16_t top; | |
52 | }; | |
53 | ||
54 | struct timeslots_t | |
55 | { | |
56 | /* store timslots in a queue */ | |
57 | struct timeslot_t slot[PWM_MAX_TIMESLOTS]; | |
58 | ||
59 | uint8_t read; /* current read head in 'slot' array */ | |
60 | uint8_t write; /* current write head in 'slot' array */ | |
61 | }; | |
62 | ||
63 | /* internal data for the fading engine */ | |
64 | struct fading_engine_t | |
65 | { | |
66 | /* a timer for each channel */ | |
67 | timer_t timer[PWM_CHANNELS]; | |
68 | ||
69 | /* and a bitmask, which stands for 'timer is running' */ | |
70 | uint8_t running; | |
71 | }; | |
72 | ||
73 | /* timer top values for 256 brightness levels (stored in flash) */ | |
74 | static const uint16_t timeslot_table[] PROGMEM = | |
75 | { | |
76 | 2, 8, 18, 31, 49, 71, 96, 126, | |
77 | 159, 197, 238, 283, 333, 386, 443, 504, | |
78 | 569, 638, 711, 787, 868, 953, 1041, 1134, | |
79 | 1230, 1331, 1435, 1543, 1655, 1772, 1892, 2016, | |
80 | 2144, 2276, 2411, 2551, 2695, 2842, 2994, 3150, | |
81 | 3309, 3472, 3640, 3811, 3986, 4165, 4348, 4535, | |
82 | 4726, 4921, 5120, 5323, 5529, 5740, 5955, 6173, | |
83 | 6396, 6622, 6852, 7087, 7325, 7567, 7813, 8063, | |
84 | 8317, 8575, 8836, 9102, 9372, 9646, 9923, 10205, | |
85 | 10490, 10779, 11073, 11370, 11671, 11976, 12285, 12598, | |
86 | 12915, 13236, 13561, 13890, 14222, 14559, 14899, 15244, | |
87 | 15592, 15945, 16301, 16661, 17025, 17393, 17765, 18141, | |
88 | 18521, 18905, 19293, 19685, 20080, 20480, 20884, 21291, | |
89 | 21702, 22118, 22537, 22960, 23387, 23819, 24254, 24693, | |
90 | 25135, 25582, 26033, 26488, 26946, 27409, 27876, 28346, | |
91 | 28820, 29299, 29781, 30267, 30757, 31251, 31750, 32251, | |
92 | 32757, 33267, 33781, 34299, 34820, 35346, 35875, 36409, | |
93 | 36946, 37488, 38033, 38582, 39135, 39692, 40253, 40818, | |
94 | 41387, 41960, 42537, 43117, 43702, 44291, 44883, 45480, | |
95 | 46080, 46684, 47293, 47905, 48521, 49141, 49765, 50393, | |
96 | 51025, 51661, 52300, 52944, 53592, 54243, 54899, 55558, | |
97 | 56222, 56889, 57560, 58235, 58914, 59598, 60285, 60975, | |
98 | 61670, 62369, 63072, 63779, 489, 1204, 1922, 2645, | |
99 | 3371, 4101, 4836, 5574, 6316, 7062, 7812, 8566, | |
100 | 9324, 10085, 10851, 11621, 12394, 13172, 13954, 14739, | |
101 | 15528, 16322, 17119, 17920, 18725, 19534, 20347, 21164, | |
102 | 21985, 22810, 23638, 24471, 25308, 26148, 26993, 27841, | |
103 | 28693, 29550, 30410, 31274, 32142, 33014, 33890, 34770, | |
104 | 35654, 36542, 37433, 38329, 39229, 40132, 41040, 41951, | |
105 | 42866, 43786, 44709, 45636, 46567, 47502, 48441, 49384, | |
106 | 50331, 51282, 52236, 53195, 54158, 55124, 56095, 57069, | |
107 | 58047, 59030, 60016, 61006, 62000, 62998 }; | |
108 | ||
109 | /* GLOBAL VARIABLES */ | |
110 | struct global_pwm_t global_pwm; | |
111 | static struct timeslots_t timeslots; | |
112 | static struct fading_engine_t fading; | |
113 | ||
114 | /* next output bitmask */ | |
115 | static volatile uint8_t pwm_next_bitmask; | |
116 | ||
117 | /* FUNCTIONS AND INTERRUPTS */ | |
118 | /* prototypes */ | |
119 | void update_pwm_timeslots(struct rgb_color_t *target); | |
120 | void update_rgb(uint8_t c); | |
121 | void enqueue_timeslot(uint8_t mask, uint16_t top); | |
122 | void dequeue_timeslot(struct timeslot_t *d); | |
123 | void update_last_timeslot(uint8_t mask); | |
124 | uint8_t timeslots_fill(void); | |
125 | ||
126 | /* initialize pwm hardware and structures */ | |
127 | void pwm_init(void) | |
128 | { | |
129 | /* init output pins */ | |
130 | ||
131 | #ifdef PWM_INVERTED | |
132 | /* set all pins high -> leds off */ | |
133 | P_PORT |= PWM_CHANNEL_MASK; | |
134 | #else | |
135 | /* set all pins low -> leds off */ | |
136 | P_PORT &= ~(PWM_CHANNEL_MASK); | |
137 | #endif | |
138 | ||
139 | #if CONFIG_SECONDARY_PWM | |
140 | #ifdef PWM_INVERTED | |
141 | /* set all pins high -> leds off */ | |
142 | P2_PORT |= PWM2_CHANNEL_MASK; | |
143 | #else | |
144 | /* set all pins low -> leds off */ | |
145 | P2_PORT &= ~(PWM2_CHANNEL_MASK); | |
146 | #endif | |
147 | #endif | |
148 | ||
149 | /* configure pins as outputs */ | |
150 | P_DDR |= PWM_CHANNEL_MASK; | |
151 | ||
152 | #if CONFIG_SECONDARY_PWM | |
153 | P2_DDR |= PWM2_CHANNEL_MASK; | |
154 | #endif | |
155 | ||
156 | /* initialize timer 1 */ | |
157 | ||
158 | /* no prescaler, CTC mode */ | |
159 | TCCR1B = _BV(CS10) | _BV(WGM12); | |
160 | ||
161 | /* enable timer1 overflow (=output compare 1a) | |
162 | * and output compare 1b interrupt */ | |
163 | _TIMSK_TIMER1 |= _BV(OCIE1A) | _BV(OCIE1B); | |
164 | ||
165 | /* set TOP for CTC mode */ | |
166 | OCR1A = 64000; | |
167 | ||
168 | /* load initial delay, trigger an overflow */ | |
169 | OCR1B = 65000; | |
170 | ||
171 | /* reset structures */ | |
172 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
173 | global_pwm.fade_delay[i] = 1; | |
174 | global_pwm.fade_step[i] = 1; | |
175 | } | |
176 | ||
177 | /* calculate initial timeslots (2 times) */ | |
178 | update_pwm_timeslots(&global_pwm.current); | |
179 | update_pwm_timeslots(&global_pwm.current); | |
180 | ||
181 | /* set initial timeslot */ | |
182 | struct timeslot_t t; | |
183 | dequeue_timeslot(&t); | |
184 | pwm_next_bitmask = t.mask; | |
185 | ||
186 | /* disable fading timers */ | |
187 | fading.running = 0; | |
188 | } | |
189 | ||
190 | /* prepare new timeslots */ | |
191 | void pwm_poll(void) | |
192 | { | |
193 | /* refill timeslots queue */ | |
194 | while (timeslots_fill() < (PWM_MAX_TIMESLOTS-PWM_CHANNELS-2)) | |
195 | update_pwm_timeslots(&global_pwm.current); | |
196 | } | |
197 | ||
198 | /* update color values for current fading */ | |
199 | void pwm_poll_fading(void) | |
200 | { | |
201 | uint8_t mask = 1; | |
202 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
203 | /* check running timers */ | |
204 | if ( (fading.running & mask) && timer_expired(&fading.timer[i])) { | |
205 | update_rgb(i); | |
206 | fading.running &= ~mask; | |
207 | } | |
208 | ||
209 | /* if timer is not running and current != target, start timer */ | |
210 | if (!(fading.running & mask) | |
211 | && global_pwm.current.rgb[i] != global_pwm.target.rgb.rgb[i] | |
212 | && global_pwm.fade_delay[i] > 0) { | |
213 | timer_set(&fading.timer[i], global_pwm.fade_delay[i]); | |
214 | fading.running |= mask; | |
215 | } | |
216 | ||
217 | /* shift mask */ | |
218 | mask <<= 1; | |
219 | } | |
220 | } | |
221 | ||
222 | /** update pwm timeslot table (for target color) */ | |
223 | void update_pwm_timeslots(struct rgb_color_t *target) | |
224 | { | |
225 | static uint8_t sorted[PWM_CHANNELS] = {0, 1, 2}; | |
226 | ||
227 | /* sort channels according to the current brightness */ | |
228 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
229 | for (uint8_t j = i+1; j < PWM_CHANNELS; j++) { | |
230 | if (target->rgb[sorted[j]] < target->rgb[sorted[i]]) { | |
231 | uint8_t temp; | |
232 | ||
233 | temp = sorted[i]; | |
234 | sorted[i] = sorted[j]; | |
235 | sorted[j] = temp; | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | /* calculate initial bitmask */ | |
241 | uint8_t initial_bitmask; | |
242 | #ifdef PWM_INVERTED | |
243 | initial_bitmask = PWM_CHANNEL_MASK; | |
244 | #else | |
245 | initial_bitmask = 0; | |
246 | #endif | |
247 | ||
248 | uint8_t chanmask = 1; | |
249 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
250 | if (target->rgb[i] > PWM_MIN_BRIGHTNESS) { | |
251 | #ifdef PWM_INVERTED | |
252 | initial_bitmask &= ~chanmask; | |
253 | #else | |
254 | initial_bitmask |= chanmask; | |
255 | #endif | |
256 | } | |
257 | ||
258 | chanmask <<= 1; | |
259 | } | |
260 | ||
261 | /* insert first timeslot */ | |
262 | enqueue_timeslot(initial_bitmask, 65000); | |
263 | ||
264 | /* calculate timeslots and masks */ | |
265 | uint8_t mask = initial_bitmask; | |
266 | uint8_t last_brightness = 0; | |
267 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
268 | uint8_t brightness = target->rgb[sorted[i]]; | |
269 | ||
270 | /* if color is (nearly off) or max, process next color */ | |
271 | if (brightness <= PWM_MIN_BRIGHTNESS || brightness == 255) | |
272 | continue; | |
273 | ||
274 | /* check if current timeslot would happen after the middle interrupt */ | |
275 | if (last_brightness < 181 && brightness >= 181) { | |
276 | /* insert (normal) middle interrupt */ | |
277 | enqueue_timeslot(mask, 65000); | |
278 | } | |
279 | ||
280 | /* if brightness is new, allocate a new timeslot */ | |
281 | if (brightness > last_brightness) { | |
282 | ||
283 | /* update mask and last_brightness */ | |
284 | last_brightness = brightness; | |
285 | #ifdef PWM_INVERTED | |
286 | mask |= _BV(sorted[i]); | |
287 | #else | |
288 | mask &= ~_BV(sorted[i]); | |
289 | #endif | |
290 | ||
291 | /* save (normal) timeslot */ | |
292 | uint16_t top = pgm_read_word(×lot_table[target->rgb[sorted[i]] - 1 ]); | |
293 | enqueue_timeslot(mask, top); | |
294 | } else { | |
295 | /* just update mask of last timeslot */ | |
296 | #ifdef PWM_INVERTED | |
297 | mask |= _BV(sorted[i]); | |
298 | #else | |
299 | mask &= ~_BV(sorted[i]); | |
300 | #endif | |
301 | ||
302 | update_last_timeslot(mask); | |
303 | } | |
304 | } | |
305 | ||
306 | /* if all interrupts happen before the middle interrupt, insert it here */ | |
307 | if (last_brightness < 181) | |
308 | enqueue_timeslot(mask, 65000); | |
309 | } | |
310 | ||
311 | /** fade any channels not already at their target brightness */ | |
312 | void update_rgb(uint8_t c) | |
313 | { | |
314 | /* return if target reached */ | |
315 | if (global_pwm.current.rgb[c] == global_pwm.target.rgb.rgb[c]) | |
316 | return; | |
317 | ||
318 | /* check direction */ | |
319 | if (global_pwm.current.rgb[c] < global_pwm.target.rgb.rgb[c]) { | |
320 | uint8_t diff = global_pwm.target.rgb.rgb[c] - global_pwm.current.rgb[c]; | |
321 | ||
322 | if (diff >= global_pwm.fade_step[c]) | |
323 | global_pwm.current.rgb[c] += global_pwm.fade_step[c]; | |
324 | else | |
325 | global_pwm.current.rgb[c] += diff; | |
326 | ||
327 | } else { | |
328 | uint8_t diff = global_pwm.current.rgb[c] - global_pwm.target.rgb.rgb[c]; | |
329 | ||
330 | if (diff >= global_pwm.fade_step[c]) | |
331 | global_pwm.current.rgb[c] -= global_pwm.fade_step[c]; | |
332 | else | |
333 | global_pwm.current.rgb[c] -= diff; | |
334 | } | |
335 | } | |
336 | ||
337 | /* timeslot queue handling */ | |
338 | void enqueue_timeslot(uint8_t mask, uint16_t top) | |
339 | { | |
340 | struct timeslot_t *t = ×lots.slot[timeslots.write]; | |
341 | t->mask = mask; | |
342 | t->top = top; | |
343 | timeslots.write = (timeslots.write + 1) % PWM_MAX_TIMESLOTS; | |
344 | } | |
345 | ||
346 | void dequeue_timeslot(struct timeslot_t *d) | |
347 | { | |
348 | struct timeslot_t *t = ×lots.slot[timeslots.read]; | |
349 | d->mask = t->mask; | |
350 | d->top = t->top; | |
351 | timeslots.read = (timeslots.read + 1) % PWM_MAX_TIMESLOTS; | |
352 | } | |
353 | ||
354 | void update_last_timeslot(uint8_t mask) | |
355 | { | |
356 | uint8_t i; | |
357 | if (timeslots.write > 0) | |
358 | i = timeslots.write-1; | |
359 | else | |
360 | i = PWM_MAX_TIMESLOTS-1; | |
361 | ||
362 | timeslots.slot[i].mask = mask; | |
363 | } | |
364 | ||
365 | uint8_t timeslots_fill(void) | |
366 | { | |
367 | if (timeslots.write >= timeslots.read) | |
368 | return timeslots.write - timeslots.read; | |
369 | else | |
370 | return PWM_MAX_TIMESLOTS - (timeslots.read - timeslots.write); | |
371 | } | |
372 | ||
373 | /* convert hsv to rgb color | |
374 | * (see http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_HSV_to_RGB ) | |
375 | * and | |
376 | * http://www.enide.net/webcms/uploads/file/projects/powerpicrgb-irda/hsvspace.pdf | |
377 | */ | |
378 | void pwm_hsv2rgb(struct dual_color_t *color) | |
379 | { | |
380 | if (color->hsv.saturation == 0) { | |
381 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) | |
382 | color->rgb.rgb[i] = color->hsv.value; | |
383 | return; | |
384 | } | |
385 | ||
386 | uint16_t h = color->hsv.hue % 360; | |
387 | uint8_t s = color->hsv.saturation; | |
388 | uint8_t v = color->hsv.value; | |
389 | ||
390 | uint16_t f = ((h % 60) * 255 + 30)/60; | |
391 | uint16_t p = (v * (255-s)+128)/255; | |
392 | uint16_t q = ((v * (255 - (s*f+128)/255))+128)/255; | |
393 | uint16_t t = (v * (255 - ((s * (255 - f))/255)))/255; | |
394 | ||
395 | uint8_t i = h/60; | |
396 | ||
397 | switch (i) { | |
398 | case 0: | |
399 | color->rgb.rgb[0] = v; | |
400 | color->rgb.rgb[1] = t; | |
401 | color->rgb.rgb[2] = p; | |
402 | break; | |
403 | case 1: | |
404 | color->rgb.rgb[0] = q; | |
405 | color->rgb.rgb[1] = v; | |
406 | color->rgb.rgb[2] = p; | |
407 | break; | |
408 | case 2: | |
409 | color->rgb.rgb[0] = p; | |
410 | color->rgb.rgb[1] = v; | |
411 | color->rgb.rgb[2] = t; | |
412 | break; | |
413 | case 3: | |
414 | color->rgb.rgb[0] = p; | |
415 | color->rgb.rgb[1] = q; | |
416 | color->rgb.rgb[2] = v; | |
417 | break; | |
418 | case 4: | |
419 | color->rgb.rgb[0] = t; | |
420 | color->rgb.rgb[1] = p; | |
421 | color->rgb.rgb[2] = v; | |
422 | break; | |
423 | case 5: | |
424 | color->rgb.rgb[0] = v; | |
425 | color->rgb.rgb[1] = p; | |
426 | color->rgb.rgb[2] = q; | |
427 | break; | |
428 | } | |
429 | } | |
430 | ||
431 | /* convert rgb to hsv color | |
432 | * (see http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV ) | |
433 | * and | |
434 | * http://www.enide.net/webcms/uploads/file/projects/powerpicrgb-irda/hsvspace.pdf | |
435 | */ | |
436 | void pwm_rgb2hsv(struct dual_color_t *color) | |
437 | { | |
438 | /* search min and max */ | |
439 | uint8_t max = color->rgb.red; | |
440 | uint8_t min = max; | |
441 | ||
442 | if (color->rgb.green > max) | |
443 | max = color->rgb.green; | |
444 | if (color->rgb.blue > max) | |
445 | max = color->rgb.blue; | |
446 | ||
447 | if (color->rgb.green < min) | |
448 | min = color->rgb.green; | |
449 | if (color->rgb.blue < min) | |
450 | min = color->rgb.blue; | |
451 | ||
452 | uint16_t hue = 0; | |
453 | uint8_t diff = max - min; | |
454 | uint8_t diffh = diff/2; | |
455 | ||
456 | /* compute value and saturation */ | |
457 | color->hsv.value = max; | |
458 | color->hsv.saturation = 0; | |
459 | ||
460 | if (max > 0) | |
461 | color->hsv.saturation = ((255 * diff)+max/2)/max; | |
462 | else { | |
463 | color->hsv.saturation = 0; | |
464 | color->hsv.hue = 0; /* undefined */ | |
465 | return; | |
466 | } | |
467 | ||
468 | if (max == min) { | |
469 | hue = 0; | |
470 | } else if (max == color->rgb.red) { | |
471 | hue = (60 * (color->rgb.green - color->rgb.blue) + diffh)/diff + 360; | |
472 | } else if (max == color->rgb.green) { | |
473 | hue = (60 * (color->rgb.blue - color->rgb.red) + diffh)/diff + 120; | |
474 | } else if (max == color->rgb.blue) { | |
475 | hue = (60 * (color->rgb.red - color->rgb.green) + diffh)/diff + 240; | |
476 | } | |
477 | ||
478 | hue = hue % 360; | |
479 | ||
480 | color->hsv.hue = hue; | |
481 | } | |
482 | ||
483 | /* stop fading, hold current color */ | |
484 | void pwm_stop_fading(void) | |
485 | { | |
486 | memcpy(&global_pwm.target.rgb, &global_pwm.current.rgb, sizeof(struct rgb_color_t)); | |
487 | ||
488 | /* ignore all timers */ | |
489 | fading.running = 0; | |
490 | } | |
491 | ||
492 | ||
493 | static uint8_t diff_abs(uint8_t a, uint8_t b) | |
494 | { | |
495 | if (a > b) | |
496 | return a-b; | |
497 | else | |
498 | return b-a; | |
499 | } | |
500 | ||
501 | static void compute_speed(uint8_t step, uint8_t delay) | |
502 | { | |
503 | /* search for max distance */ | |
504 | uint8_t max = 0; | |
505 | uint8_t dist = 0; | |
506 | ||
507 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
508 | uint8_t d = diff_abs(global_pwm.target.rgb.rgb[i], global_pwm.current.rgb[i]); | |
509 | ||
510 | if (d > dist) { | |
511 | max = i; | |
512 | dist = d; | |
513 | } | |
514 | } | |
515 | ||
516 | /* adjust fading speeds, relative to max distance */ | |
517 | global_pwm.fade_step[max] = step; | |
518 | global_pwm.fade_delay[max] = delay; | |
519 | ||
520 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
521 | if (i == max) | |
522 | continue; | |
523 | ||
524 | uint8_t d = diff_abs(global_pwm.target.rgb.rgb[i], global_pwm.current.rgb[i]); | |
525 | ||
526 | uint8_t ratio = 1; | |
527 | if (d > 0) | |
528 | ratio = (dist+d/2)/d; | |
529 | ||
530 | if (ratio == 0) | |
531 | ratio = 1; | |
532 | ||
533 | global_pwm.fade_delay[i] = ratio * delay; | |
534 | global_pwm.fade_step[i] = step; | |
535 | } | |
536 | } | |
537 | ||
538 | void pwm_fade_rgb(struct rgb_color_t *color, uint8_t step, uint8_t delay) | |
539 | { | |
540 | /* apply offsets for step and delay */ | |
541 | step = remote_apply_offset(step, global_remote.offsets.step); | |
542 | delay = remote_apply_offset(delay, global_remote.offsets.delay); | |
543 | ||
544 | /* set target color */ | |
545 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) | |
546 | global_pwm.target.rgb.rgb[i] = color->rgb[i]; | |
547 | ||
548 | /* compute correct speed for all channels */ | |
549 | if (delay == 0) | |
550 | delay = 1; | |
551 | compute_speed(step, delay); | |
552 | ||
553 | /* disable timer */ | |
554 | fading.running = 0; | |
555 | } | |
556 | ||
557 | void pwm_fade_hsv(struct hsv_color_t *color, uint8_t step, uint8_t delay) | |
558 | { | |
559 | /* apply offsets for step and delay */ | |
560 | step = remote_apply_offset(step, global_remote.offsets.step); | |
561 | delay = remote_apply_offset(delay, global_remote.offsets.delay); | |
562 | ||
563 | /* convert color */ | |
564 | memcpy(&global_pwm.target.hsv, color, sizeof(struct hsv_color_t)); | |
565 | ||
566 | /* apply offsets */ | |
567 | remote_apply_hsv_offset(&global_pwm.target.hsv); | |
568 | ||
569 | /* update rgb color in target */ | |
570 | pwm_hsv2rgb(&global_pwm.target); | |
571 | ||
572 | /* compute correct speed for all channels */ | |
573 | compute_speed(step, delay); | |
574 | ||
575 | /* disable timer */ | |
576 | fading.running = 0; | |
577 | } | |
578 | ||
579 | bool pwm_target_reached(void) | |
580 | { | |
581 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
582 | if (global_pwm.target.rgb.rgb[i] != global_pwm.current.rgb[i]) | |
583 | return false; | |
584 | } | |
585 | ||
586 | return true; | |
587 | } | |
588 | ||
589 | /* modify current color */ | |
590 | void pwm_modify_rgb(struct rgb_color_offset_t *color, uint8_t step, uint8_t delay) | |
591 | { | |
592 | for (uint8_t i = 0; i < PWM_CHANNELS; i++) { | |
593 | int16_t current = global_pwm.target.rgb.rgb[i]; | |
594 | current += color->rgb[i]; | |
595 | ||
596 | if (current > 255) | |
597 | current = 255; | |
598 | if (current < 0) | |
599 | current = 0; | |
600 | ||
601 | global_pwm.target.rgb.rgb[i] = LO8(current); | |
602 | } | |
603 | ||
604 | compute_speed(step, delay); | |
605 | ||
606 | /* disable timer */ | |
607 | fading.running = 0; | |
608 | } | |
609 | ||
610 | void pwm_modify_hsv(struct hsv_color_offset_t *color, uint8_t step, uint8_t delay) | |
611 | { | |
612 | /* convert current target color from rgb to hsv */ | |
613 | pwm_rgb2hsv(&global_pwm.target); | |
614 | ||
615 | /* apply changes, hue */ | |
616 | global_pwm.target.hsv.hue += color->hue; | |
617 | ||
618 | /* saturation */ | |
619 | int16_t sat = global_pwm.target.hsv.saturation; | |
620 | sat += color->saturation; | |
621 | if (sat > 255) | |
622 | sat = 255; | |
623 | if (sat < 0) | |
624 | sat = 0; | |
625 | global_pwm.target.hsv.saturation = LO8(sat); | |
626 | ||
627 | /* value */ | |
628 | int16_t val = global_pwm.target.hsv.value; | |
629 | val += color->value; | |
630 | if (val > 255) | |
631 | val = 255; | |
632 | if (val < 0) | |
633 | val = 0; | |
634 | global_pwm.target.hsv.value = LO8(val); | |
635 | ||
636 | /* re-convert to rgb */ | |
637 | pwm_hsv2rgb(&global_pwm.target); | |
638 | ||
639 | /* compute correct speed for all channels */ | |
640 | compute_speed(step, delay); | |
641 | ||
642 | /* disable timer */ | |
643 | fading.running = 0; | |
644 | } | |
645 | ||
646 | ||
647 | /** interrupts*/ | |
648 | ||
649 | /** timer1 overflow (=output compare a) interrupt */ | |
650 | ISR(TIMER1_COMPA_vect) | |
651 | { | |
652 | /* read next_bitmask */ | |
653 | uint8_t next_bitmask = pwm_next_bitmask; | |
654 | /* output new values */ | |
655 | P_PORT = (P_PORT & ~(PWM_CHANNEL_MASK)) | (next_bitmask << PWM_SHIFT); | |
656 | #if CONFIG_SECONDARY_PWM | |
657 | P2_PORT = (P2_PORT & ~(PWM2_CHANNEL_MASK)) | (next_bitmask << PWM2_SHIFT); | |
658 | #endif | |
659 | ||
660 | /* prepare next interrupt */ | |
661 | struct timeslot_t t; | |
662 | dequeue_timeslot(&t); | |
663 | ||
664 | /* if next timeslot would happen too fast or has already happened, just spinlock */ | |
665 | while (TCNT1 + 100 > t.top) | |
666 | { | |
667 | /* spin until timer interrupt is near enough */ | |
668 | while (t.top > TCNT1); | |
669 | ||
670 | /* output new values */ | |
671 | P_PORT = (P_PORT & ~(PWM_CHANNEL_MASK)) | (t.mask << PWM_SHIFT); | |
672 | #if CONFIG_SECONDARY_PWM | |
673 | P2_PORT = (P2_PORT & ~(PWM2_CHANNEL_MASK)) | (t.mask << PWM2_SHIFT); | |
674 | #endif | |
675 | ||
676 | /* load next timeslot */ | |
677 | dequeue_timeslot(&t); | |
678 | } | |
679 | ||
680 | /* save values for next interrupt */ | |
681 | OCR1B = t.top; | |
682 | pwm_next_bitmask = t.mask; | |
683 | } | |
684 | ||
685 | /** timer1 output compare b interrupt */ | |
686 | ISR(TIMER1_COMPB_vect, ISR_ALIASOF(TIMER1_COMPA_vect)); |