Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / libdragonfly / lights.c @ 1392

History | View | Annotate | Download (25.2 KB)

1 241 bcoltin
/**
2
 * Copyright (c) 2007 Colony Project
3
 *
4
 * Permission is hereby granted, free of charge, to any person
5
 * obtaining a copy of this software and associated documentation
6
 * files (the "Software"), to deal in the Software without
7
 * restriction, including without limitation the rights to use,
8
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the
10
 * Software is furnished to do so, subject to the following
11
 * conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
 * OTHER DEALINGS IN THE SOFTWARE.
24
 **/
25
26
/**
27
 * @file ligths.c
28
 * @brief Orbs
29
 *
30
 * Implemenation for the orbs (tri-colored LEDs)
31
 *
32
 * @author Colony Project, CMU Robotics Club
33 1142 deffi
 * @bug Unfinished
34 241 bcoltin
 **/
35
36 8 bcoltin
/*
37
lights.c
38 1142 deffi
Controls orb1 and orb2. Can be extended for a software PWM that may be used for servos in the future (although maybe
39
using a different timer might be preferable).
40 8 bcoltin

41
author: CMU Robotics Club, Colony Project
42

43
Change Log:
44 1142 deffi
3/31/2009 - Martin
45
    Rewritten from scratch. Fixes code duplication, long ISRs, bugs, unnecessary synchronized code, memory waste
46
*/
47 8 bcoltin
48
49
50 1142 deffi
/*
51
 * Test cases:
52
 *   - The following code has to work without flickering:
53
 *     orb_init_pwm(); while(1) { orbs_set(1,1,1,254,254,254); }
54
 */
55
56
/*
57
 * Possible optimizations:
58
 *   - Use pointers instead of indicies for current_pwm_channel
59
 *   - Optimize output_compare()
60
 *   - Use a different sorting algorithm (see sort_orbs_buffer for further comments on this issue)
61
 *   - Use pointers in fill_orbs_buffer
62
 *   - Optimized orb_set (use the knowledge that there are only 3 distinct values, don't use a loop but unroll the
63
 *     sorting, which is no problem for 3 values)
64
 *   - Use a lower update frequency. The next higher prescaler value leads to a frequency of 30Hz which is too low (the
65
 *     orbs are flickering). So the timer would have to be reloaded manually after 127 to generate 60Hz. This would
66
 *     decrease the resolution from 8 to 7 bit, but 128 steps should still be enough.
67
 *   - On setting the orbs, combine channels with the same time. This would reduce the all-values-equal OC interrupt
68
 *     (30us) to the time of one regular OC interrupt (6us/10us). Also, it would reduce the total cpu usage whenever
69
 *     some of the values are equal.
70
 *
71
 * When code is changed, the performance measurements above should be redone.
72
 */
73
74
75
76 8 bcoltin
#include "lights.h"
77 1142 deffi
78 8 bcoltin
#include <avr/interrupt.h>
79
80 1142 deffi
#include "dragonfly_lib.h"
81
82
83
// ***************
84
// ** Constants **
85
// ***************
86
87
#define NUM_ORBS 2   // Number or orbs
88
#define NUM_COLORS 3 // Number of colors per orb
89
#define num_pwm_channels NUM_ORBS*NUM_COLORS
90
91
92
// *********
93
// ** I/O **
94
// *********
95
96
// Orb port
97 8 bcoltin
#define ORBPORT PORTC
98 1142 deffi
#define ORBDDR  DDRC
99 8 bcoltin
100 1142 deffi
// Orb pins
101
#define ORB1_RED   0
102
#define ORB1_GREEN 1
103
#define ORB1_BLUE  2
104
#define ORB2_RED   4
105
#define ORB2_GREEN 5
106
#define ORB2_BLUE  6
107 8 bcoltin
108
109 1142 deffi
// ***************
110
// ** Debugging **
111
// ***************
112 8 bcoltin
113 1142 deffi
//#define LIGHTS_DEBUG
114
#undef LIGHTS_DEBUG
115 8 bcoltin
116 1142 deffi
#define LIGHTS_DEBUG_INIT                             DDRF=6;
117
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START         PORTF|=4;
118
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END           PORTF&=~4;
119
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START   PORTF|=2;
120
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END     PORTF&=~2;
121
#define LIGHTS_DEBUG_APPLY_START                      //PORTF|=2;
122
#define LIGHTS_DEBUG_APPLY_END                        //PORTF&=~2;
123 8 bcoltin
124 1142 deffi
125
// ***********
126
// ** Masks **
127
// ***********
128
129
// Some useful bit masks. All of them are are calculated from the I/O definitions above. The calculations should be done
130
// at compile time (even if they are not, they are only executed once at startup).
131
132
// Masks for the individual LEDs
133
#define orb1_red_mask    _BV (ORB1_RED  )
134
#define orb1_green_mask  _BV (ORB1_GREEN)
135
#define orb1_blue_mask   _BV (ORB1_BLUE )
136
#define orb2_red_mask    _BV (ORB2_RED  )
137
#define orb2_green_mask  _BV (ORB2_GREEN)
138
#define orb2_blue_mask   _BV (ORB2_BLUE )
139
140
// Mask for all LEDs
141
#define all_orbs_mask \
142
    orb1_red_mask | orb1_green_mask | orb1_blue_mask | \
143
    orb2_red_mask | orb2_green_mask | orb2_blue_mask;
144
145
// Mask for the individual LEDs, organized as an array for programmatic access. The layout of this array is
146
// orb_mask[orb_num, color_num]
147
const uint8_t orb_mask[NUM_ORBS][NUM_COLORS]={
148
    { orb1_red_mask, orb1_green_mask, orb1_blue_mask },
149
    { orb2_red_mask, orb2_green_mask, orb2_blue_mask }
150 8 bcoltin
};
151
152 1142 deffi
// ***********
153
// ** Types **
154
// ***********
155
156
struct pwm_channel_t { // 2 bytes
157
    uint8_t time;
158
    uint8_t mask;
159 8 bcoltin
};
160
161 1142 deffi
struct pwm_t { // 13 bytes
162
    uint8_t init_mask;
163
    struct pwm_channel_t channel[num_pwm_channels];
164
};
165 8 bcoltin
166
167 1142 deffi
// ***************
168
// ** Variables **
169
// ***************
170 8 bcoltin
171 1142 deffi
// Whether to use PWM (true) or binary (false) orb mode. Not volatile because it's only read once per function.
172
bool enable_orb_pwm=true;
173 8 bcoltin
174 1142 deffi
// The PWM channels and the buffer pointers. This data structure is triple buffered, see above for the reasons. Not
175
// volatile because they are not modified asynchronously (the read buffer is never written to asynchronously).
176
struct pwm_t pwm_buffer[3];
177 8 bcoltin
178 1142 deffi
// The front buffer the ISR reads from. Other threads may not touch this pointer or the buffer it points to. Not
179
// volatile because it may only be modified by the ISR.
180
struct pwm_t *pwm_read_buffer =&pwm_buffer[0];
181 8 bcoltin
182 1142 deffi
// The back buffer we can write to. The ISR may not touch this pointer or the buffer it points to. Not volatile because
183
// it may only be modified by the caller.
184
struct pwm_t *pwm_write_buffer=&pwm_buffer[1];
185 8 bcoltin
186 1142 deffi
// The middle buffer to flip the write or read buffer with. Not volatile because it is only read once per function.
187
struct pwm_t *pwm_free_buffer =&pwm_buffer[2];
188 8 bcoltin
189 1142 deffi
// Whether to perform a page flip on the beginning of the next PWM cycle. Not volatile because it is only read once
190
// per function.
191
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
192 8 bcoltin
193 1142 deffi
// The orb value array. Orb values are written here to be sorted into pwm_channels. Not volatile because all accesses
194
// are from guarded (thread safe) functions.
195
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
196
197
198
// ****************
199
// ** Timer ISRs **
200
// ****************
201
202
// Not volatile because it is only accessed in the interrupt handler.
203
uint8_t current_pwm_channel=0;
204
205
206
static void output_compare (void) {
207
        // This function is called when an output compare condition may have occured.
208 8 bcoltin
209 1142 deffi
    // If the OC interrupt is executed without delay, TCNT0==time+1 (where time==OCR0), because the interrupt flag is
210
        // queued at the next timer clock cycle after an output compare.
211
212
    // What may happen here is that the interrupt is delayed for more than one timer clock cycle (33 us). In that case,
213
        // the timer has already counted on and TCNT0 is bigger than current_channel_timer. Also, while during the ISR no
214
        // other interrupts will occur, the timer may still count on. Thus, we have to check the following channel as well.
215
216
        // Some optimization is possible in this function.
217
218
    while (1) {
219
                // The timer value at which the output compare interrupt should occur (one timer clock cycle after the output
220
                // compare condition is detected).
221
                uint8_t current_channel_time=pwm_read_buffer->channel[current_pwm_channel].time+1;
222
223
                // If the counter is not at this time yet, we don't have to do anything right now.
224
                if (current_channel_time>TCNT0) return;
225
226
                // We have an output compare condition for the current channel.
227
228
        // Turn the current channel off
229
        ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
230
231
                // If this was the last channel, exit
232
                if (current_pwm_channel==num_pwm_channels-1) return;
233
234
        // Increment the channel index
235
        current_pwm_channel++;
236
237
        // There is a next channel, load its OCR value
238
        if (pwm_read_buffer->channel[current_pwm_channel].time<255)
239
            OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
240
    }
241 8 bcoltin
}
242
243 1142 deffi
SIGNAL (SIG_OVERFLOW0) {
244
#ifdef LIGHTS_DEBUG
245
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START
246
#endif
247 8 bcoltin
248 1142 deffi
    if (pwm_page_flip) {
249
        // Flip the read buffer with the free buffer. We are in an ISR (and we didn't re-enable interrupts), so we don't
250
        // have to synchronize explicitly.
251
        struct pwm_t *temp = pwm_read_buffer;
252
        pwm_read_buffer    = pwm_free_buffer;
253
        pwm_free_buffer    = temp;
254
        pwm_page_flip=false;
255
    }
256 8 bcoltin
257 1142 deffi
    // Turn only the appropriate PWM channels on. Do this directly on the orb port because at this point all orbs should
258
    // be off anyway.
259
    ORBPORT|=all_orbs_mask;
260
    ORBPORT&=pwm_read_buffer->init_mask;
261
262
    // Start at the first channel
263
    current_pwm_channel=0;
264
265
    // Load the first OCR
266
    OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
267 8 bcoltin
268 1142 deffi
        // If this interrupt was delayed, we might already have an output compare condition.
269
        output_compare ();
270 8 bcoltin
271 1142 deffi
#ifdef LIGHTS_DEBUG
272
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
273
#endif
274 8 bcoltin
}
275
276 1142 deffi
SIGNAL(SIG_OUTPUT_COMPARE0) {
277
#ifdef LIGHTS_DEBUG
278
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
279
#endif
280 8 bcoltin
281 1142 deffi
        // We have an output compare condition.
282
        output_compare ();
283
284
#ifdef LIGHTS_DEBUG
285
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END
286
#endif
287
}
288 8 bcoltin
289
290
291 1142 deffi
// ************************************
292
// ** Internal orb setting functions **
293
// ************************************
294 8 bcoltin
295 1142 deffi
static void sort_orbs_buffer (void) {
296
    // This function applies a bubble sort to sort the elements of the pwm_write_buffer->channel array by the time
297
    // field.
298
    // This implementation is heavily optimized. Note that due to the low (and constant) number of elements to be
299
    // sorted, the runtime complexity (O(n^2) for bubble sort) is not relevant here. In fact, a more advanced algorithm
300
    // like quick sort or merge sort might even be slower due to higher overhead.
301
    // That said, it is possible that selection sort (which is also in O(n^2)) would be faster that bubble sort because
302
    // it only has to do a maximum of (n-1) swapping steps (as opposed to n*(n-1)/2 for bubble sort). However, the check
303
    // if the elements are already in the correct order would either have to be left out (doing the full search every
304
    // time, even if the array is already sorted) or done explicitly, so selection sort might actually be slower than
305
    // bubble sort, especially if the array is already sorted or almost sorted.
306
307
    // This implementation uses macros to make the algorithm more clear because the loop is rolled out and the function
308
    // would become quite long without macros.
309
310
    // Macro to swap two values of any type. Requires a variable of the appropriate type called swap_temp.
311
    #define swap(a,b) { swap_temp=a; a=b; b=swap_temp; }
312 8 bcoltin
313 1142 deffi
    // Macro to do one bubble sorting step (compare & swap)
314
    #define bubble \
315
        if(a->time > b->time) \
316
        { \
317
            swap (a->time, b->time); \
318
            swap (a->mask, b->mask); \
319
            done=false; \
320
        }
321
322
    // Macro to move to the next bubble sort pair
323
    #define next { a++; b++; }
324 8 bcoltin
325 1142 deffi
    // Whether no change was made during the last run, which means that all values are already in correct order.
326
    bool done;
327
328
    // A temporary variable for swapping.
329
    uint8_t swap_temp;
330
331
    // Precompute the first PWM channel (tested faster).
332
    struct pwm_channel_t *first=&(pwm_write_buffer->channel[0]);
333 8 bcoltin
334 1142 deffi
    // Pointers to the two PWM channels under inspection
335
    struct pwm_channel_t *a, *b;
336 8 bcoltin
337 1142 deffi
    // The actual sorting
338
    a=first; b=a+1; done=true;
339
    bubble next bubble next bubble next bubble next bubble
340
    if (done) return;
341 8 bcoltin
342 1142 deffi
    a=first; b=a+1; done=true;
343
    bubble next bubble next bubble next bubble
344
    if (done) return;
345 8 bcoltin
346 1142 deffi
    a=first; b=a+1; done=true;
347
    bubble next bubble next bubble
348
    if (done) return;
349 8 bcoltin
350 1142 deffi
    a=first; b=a+1; done=true;
351
    bubble next bubble
352
    if (done) return;
353 8 bcoltin
354 1142 deffi
    a=first; b=a+1; done=true;
355
    bubble
356
    if (done) return;
357
358
    // Undefine the macros so they do not disturb some other function.
359
    #undef next
360
    #undef bubble
361
    #undef swap
362 8 bcoltin
}
363
364 1142 deffi
static void fill_orbs_buffer (void) {
365
    // We do not use a loop here because it introduces 27us overhead, which is quite much, given the total time for
366
    // optimized copying and sorting of 34us (elements already in correct order) to 71 us (elements in reverse order).
367
368
    #define copy_value(orb, color) \
369
        index=NUM_COLORS*orb+color; \
370
        time=orb_values[orb][color]; \
371
        mask=orb_mask[orb][color]; \
372
        \
373
        pwm_write_buffer->channel[index].time=time-1; \
374
        pwm_write_buffer->channel[index].mask=mask; \
375
        \
376
        if (time!=0) \
377
            pwm_write_buffer->init_mask &= ~mask; \
378 8 bcoltin
379 1142 deffi
    uint8_t index, time, mask;
380
    copy_value(0,0); copy_value(0,1); copy_value(0,2);
381
    copy_value(1,0); copy_value(1,1); copy_value(1,2);
382
383
    #undef copy_value
384
}
385 8 bcoltin
386 1142 deffi
static void apply_orbs (void) {
387
        /*
388 1392 deffi
         * Some timing tests: Time for apply_orbs with interrupts disabled, in microseconds:
389 1142 deffi
     *             Values in:     Correct order      Reverse order
390
     * Naive bubble sort:         148                217
391
     * Aborting bubble sort:       71                232
392
     * Only count to top:          73                189
393
     *
394
     * Loops rolled out:           61                120
395
     * Using pointers:             62                 98
396
     * Copy loop also rolled out:  35                 72
397
         *
398
         * Note that rolling out both loops and using pointers saves 52%/62% of time! 27us were spent on loop overhead,
399
         * which is quite much, considering an optimized total time for copying and sorting or 35us.
400
         */
401 8 bcoltin
402 1142 deffi
#ifdef LIGHTS_DEBUG
403
    LIGHTS_DEBUG_APPLY_START
404
#endif
405 8 bcoltin
406 1142 deffi
    if (enable_orb_pwm) {
407
        // PWM mode
408
409
        pwm_write_buffer->init_mask=~0;
410
411
        // 1. Write the orb values and corresponding masks to the pwm channels
412
        // array unsorted.
413
        fill_orbs_buffer ();
414 8 bcoltin
415 1142 deffi
        // 2. sort the buffer.
416
        sort_orbs_buffer ();
417 8 bcoltin
418 1142 deffi
        // Flip the write buffer with the free buffer.
419
        SYNC {
420
            struct pwm_t *temp = pwm_write_buffer;
421
            pwm_write_buffer   = pwm_free_buffer;
422
            pwm_free_buffer    = temp;
423
        }
424
425
        // On the next overflow, flip the read buffer with the free buffer.
426
        pwm_page_flip=true;
427
    }
428
    else {
429
        // Binary mode.
430
        // The outputs are inverted.
431
        uint8_t on=0;
432
433
        if (orb_values[0][0]) on |= orb_mask[0][0];
434
        if (orb_values[0][1]) on |= orb_mask[0][1];
435
        if (orb_values[0][2]) on |= orb_mask[0][2];
436
        if (orb_values[1][0]) on |= orb_mask[1][0];
437
        if (orb_values[1][1]) on |= orb_mask[1][1];
438
        if (orb_values[1][2]) on |= orb_mask[1][2];
439
440
        // Write the new orb states to the output port. Synchronized because it is a RMW operation.
441
        SYNC {
442
            uint8_t value=ORBPORT;
443
            value |= all_orbs_mask; // All orbs off
444
            value &= ~on; // Selected orbs on
445
            ORBPORT=value;
446
        }
447
    }
448
449
#ifdef LIGHTS_DEBUG
450
    LIGHTS_DEBUG_APPLY_END
451
#endif
452 8 bcoltin
}
453
454 1142 deffi
static void set_orb_values (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
455
    // Write the values to the array, but do not sort them yet, as we might want to write the other orb values first so
456
    // we don't have to sort twice.
457
    // Any function calling this function will probably want to call apply_orbs() afterwards.
458
    orb_values[num][0]=red;
459
    orb_values[num][1]=green;
460
    orb_values[num][2]=blue;
461
}
462
463
464
// ***********************
465
// ** RGB color setting **
466
// ***********************
467
468
// All of these functions use set_orb_values to set the actual values, and then call apply_orbs() to apply the changes.
469
// set_orb_values should be used (even though it would be faster to set the array directly) because the binary/pwm mode
470
// has to be handled.
471
// All of these functions must be
472
473
uint8_t orb_lock=0;
474
475 8 bcoltin
/**
476 1142 deffi
 * Sets the specified orb to the specified color. The orbs must be initialized before this function may be used.
477
 * Note that, when setting both orbs, using orbs_set is faster then setting the orbs individually because the values are
478
 * only sorted once.
479 8 bcoltin
 *
480 1142 deffi
 * @param num the number of the orb to set (0 or 1)
481
 * @param red the red value for the specified orb
482
 * @param green the green value for the specified orb
483
 * @param blue the blue value for the specified orb
484
 * @see
485
 */
486
void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
487
        REQUIRE_LOCK_OR_RETURN(orb_lock);
488
489
    set_orb_values (num, red, green, blue);
490
    apply_orbs ();
491
492
        RELEASE_LOCK(orb_lock);
493
}
494
495
/**
496
 * Set orb1 to the color specified. The orbs must be initialized before this function may be used. Note that, when
497
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
498
 * once.
499 8 bcoltin
 *
500 1142 deffi
 * @param red the red component of the color
501
 * @param green the green component of the color
502
 * @param blue the blue component of the color
503
 *
504 8 bcoltin
 * @see orb_init
505
 **/
506 1142 deffi
void orb1_set (uint8_t red, uint8_t green, uint8_t blue) {
507
        REQUIRE_LOCK_OR_RETURN(orb_lock);
508
509
    set_orb_values (0, red, green, blue);
510
    apply_orbs ();
511 8 bcoltin
512 1142 deffi
        RELEASE_LOCK(orb_lock);
513 8 bcoltin
}
514
515
/**
516 1142 deffi
 * Set orb2 to the color specified. The orbs must be initialized before this function may be used. Note that, when
517
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
518
 * once.
519 8 bcoltin
 *
520
 * @param red_led the red component of the color
521
 * @param green_led the green component of the color
522
 * @param blue_led the blue component of the color
523
 *
524
 * @see orb_init
525
 **/
526 1142 deffi
void orb2_set (uint8_t red, uint8_t green, uint8_t blue) {
527
        REQUIRE_LOCK_OR_RETURN(orb_lock);
528
529
    set_orb_values (1, red, green, blue);
530
    apply_orbs ();
531
532
        RELEASE_LOCK(orb_lock);
533 8 bcoltin
}
534
535
/**
536 1142 deffi
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
537 8 bcoltin
 *
538
 * @param red_led the red component of the color
539
 * @param green_led the green component of the color
540
 * @param blue_led the blue component of the color
541
 *
542 1142 deffi
 * @see orb_init, orb1_set, orb2_set
543 8 bcoltin
 **/
544 1142 deffi
void orb_set (uint8_t red, uint8_t green, uint8_t blue) {
545
        REQUIRE_LOCK_OR_RETURN(orb_lock);
546
547
    set_orb_values (0, red, green, blue);
548
    set_orb_values (1, red, green, blue);
549
    apply_orbs ();
550
551
        RELEASE_LOCK(orb_lock);
552 8 bcoltin
}
553
554
/**
555 1142 deffi
 * Set the orbs to the respective values. The orbs must be initialized before this function may be used. Note that, when
556
 * setting both orbs, this function is faster than calling orb1_set and orb2_set (or orb_n_set) because the values are
557
 * only sorted once.
558 8 bcoltin
 *
559 1142 deffi
 * @param red1
560
 * @param green1
561
 * @param blue1
562
 * @param red2
563
 * @param green2
564
 * @param blue2
565
 * @see orb1_set
566
 * @see orb2_set
567
 * @see orb_n_set
568 8 bcoltin
 **/
569 1142 deffi
void orbs_set (
570
    uint8_t red1, uint8_t green1, uint8_t blue1,
571
    uint8_t red2, uint8_t green2, uint8_t blue2) {
572 8 bcoltin
573 1142 deffi
        REQUIRE_LOCK_OR_RETURN(orb_lock);
574 8 bcoltin
575 1142 deffi
    set_orb_values (0, red1, green1, blue1);
576
    set_orb_values (1, red2, green2, blue2);
577
    apply_orbs ();
578
579
        RELEASE_LOCK(orb_lock);
580 8 bcoltin
}
581
582 1142 deffi
583
// ******************************
584
// ** Predefined color setting **
585
// ******************************
586
587
// This functions just call the corresponding orb*_set functions. If the orbs array is accessed in any other way, it
588
// must be synchronized on orb_lock (REQUIRE_LOCK_OR_RETURN and RELEASE_LOCK)! Note that one synchronized function
589
// cannot call another one with this lock implementation.
590
591
// Macros for extracting a color.
592
#define C_RED(col)   (((col & 0xE0) >> 5) * 36)
593
#define C_GREEN(col) (((col & 0x1C) >> 2) * 36)
594
#define C_BLUE(col)  (((col & 0x03)     ) * 85)
595
596 8 bcoltin
/**
597 1142 deffi
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
598 8 bcoltin
 *
599 1142 deffi
 * @param num the number of the orb to set (0 or 1)
600 8 bcoltin
 * @param col the color to set the orbs to
601 1142 deffi
 **/
602
void orb_n_set_color(uint8_t num, uint8_t col) {
603
    orb_n_set(num, C_RED(col), C_GREEN(col), C_BLUE(col));
604
}
605
606
/**
607
 * Set orb1 to the specified color. This function is intended to be used with the predefined colors.
608 8 bcoltin
 *
609 1142 deffi
 * @param col the color to set the orbs to
610 8 bcoltin
 **/
611 1142 deffi
void orb1_set_color(uint8_t col) {
612
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
613
}
614 8 bcoltin
615 1142 deffi
/**
616
 * Set orb2 to the specified color. This function is intended to be used with the predefined colors.
617
 *
618
 * @param col the color to set the orbs to
619
 **/
620
void orb2_set_color(uint8_t col) {
621
    orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
622 8 bcoltin
}
623
624
/**
625 1142 deffi
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
626 8 bcoltin
 *
627
 * @param col the color to set the orbs to
628 1142 deffi
 **/
629
void orb_set_color(uint8_t col) {
630
    orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
631
}
632
633
/**
634
 * Set the orbs to the respective color. This function is intended to be used with the predefined colors.
635 8 bcoltin
 *
636 1142 deffi
 * @param col1 the color to set orb 1 to
637
 * @param col2 the color to set orb 2 to
638 8 bcoltin
 **/
639 1142 deffi
void orbs_set_color(uint8_t col1, uint8_t col2) {
640
    orbs_set (C_RED(col1), C_GREEN(col1), C_BLUE(col1), C_RED(col2), C_GREEN(col2), C_BLUE(col2));
641
}
642 8 bcoltin
643 1142 deffi
#undef C_BLUE
644
#undef C_GREEN
645
#undef C_RED2
646 8 bcoltin
647 1142 deffi
648
// ******************
649
// ** Mode setting **
650
// ******************
651
652
/**
653
 * Enables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
654
 * @see orb_set_mode
655
 **/
656
void orb_enable_timer (void) {
657
    // Use 8 bit TC0.
658
    //
659
    // Timer mode: We cannot use CTC mode because it can only clear on OCR0 (in contrast to the 16 bit timers which can
660
    // also use the ICR for that) and OCR0 is already used for generating output compare interrupts. We also need
661
    // immediate (non double buffered) update of OCR0, so the only mode left is "Normal".
662
    //
663
    // Note that for a timer counting from 0 to 255, there are 256 states and thus 257 output possibilities
664
    // (0/256...256/256)! However, there are only 256 values in the byte used to specify the PWM value. Possible ways
665
    // to deal with that:
666
    //   1. use a 16 bit variable for the PWM value (memory waste, overhead)
667
    //   2. use an additional flag for the 257th value (inconvenient)
668
    //   3. use 1/256...256/256 (skip 0, never complete off)
669
    //   4. use 0/256...256/256 (skip 256, never complete on)
670
    //   5. skip a value somewhere in the middle
671
    //   6. reload the timer after 254
672
    // For this implementation, variant 4 was chosen.
673
    //
674
    // Using an 8 bit timer has the added advantage that all the comparisons are faster.
675
676
    // Normal mode, Compare match output off, Prescaler
677
    TCCR0=_BV(CS02) | _BV(CS01); // 256, 120 Hz
678 1392 deffi
        // The next higher prescaler would be 1024 (30 Hz) which makes the orbs flicker visibly.
679 1142 deffi
680
    // Enable the interrupts
681
    TIMSK|= _BV(OCIE0) | _BV(TOIE0);
682 8 bcoltin
}
683
684
/**
685 1142 deffi
 * Disables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
686
 * @see orb_set_mode
687 8 bcoltin
 **/
688 1142 deffi
void orb_disable_timer (void) {
689
    // Disable the interrupts
690
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
691 8 bcoltin
}
692
693 1142 deffi
694
void orb_set_mode (orb_mode_t mode) {
695
    // Set enable_orb_pwm to the appropriate value and disable or enable the timer.
696
    if (mode==orb_mode_binary) {
697
        orb_disable_timer ();
698
699
        enable_orb_pwm=false;
700
        apply_orbs ();
701
    }
702
    else { // orb_mode_pwm
703
        enable_orb_pwm=true;
704
        apply_orbs ();
705
706
        orb_enable_timer ();
707
    }
708
}
709
710
711
// ********************
712
// ** Initialization **
713
// ********************
714
715
// Orb initialization code common to all modes.
716
static void orb_init_common (void) {
717
    // Enable the output ports and turn off the LEDs
718
    ORBPORT |=  all_orbs_mask;
719
    ORBDDR  |=  all_orbs_mask;
720
721
    // Set all orbs to "off"
722
    orb_set (0, 0, 0);
723
724
#ifdef LIGHTS_DEBUG
725
    LIGHTS_DEBUG_INIT
726
#endif
727
}
728
729 8 bcoltin
/**
730 1142 deffi
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
731
 *
732
 * @see orb_init_pwm
733 8 bcoltin
 **/
734 1142 deffi
void orb_init_binary (void) {
735
    orb_init_common ();
736
    orb_set_mode (orb_mode_binary);
737 8 bcoltin
}
738
739 1142 deffi
/**
740
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
741
 *
742
 * @see orb_init_binary
743
 **/
744
void orb_init_pwm (void) {
745
    orb_init_common ();
746
    orb_set_mode (orb_mode_pwm);
747
}
748 8 bcoltin
749 1142 deffi
/**
750
 * Initializes the orbs in default mode. One of the orb_init* functions must be called before the orbs can be used. Use
751
 * the orb_init_binary or orb_init_pwm function if you want one specific mode.
752
 *
753
 * @see orb_init_pwm
754
 * @see orb_init_binary
755
 **/
756
void orb_init () {
757
    orb_init_pwm ();
758
}