Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (25.1 KB)

1
/**
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
 * @bug Unfinished
34
 **/
35

    
36
/*
37
lights.c
38
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

41
author: CMU Robotics Club, Colony Project
42

43
Change Log:
44
3/31/2009 - Martin
45
    Rewritten from scratch. Fixes code duplication, long ISRs, bugs, unnecessary synchronized code, memory waste
46
*/
47

    
48

    
49

    
50
/*
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
#include "dragonfly_lib.h"
75
#include "lights.h"
76

    
77
// ***************
78
// ** Constants **
79
// ***************
80

    
81
#define NUM_ORBS 2   // Number or orbs
82
#define NUM_COLORS 3 // Number of colors per orb
83
#define num_pwm_channels NUM_ORBS*NUM_COLORS
84

    
85

    
86
// *********
87
// ** I/O **
88
// *********
89

    
90
// Orb port
91
#define ORBPORT PORTC
92
#define ORBDDR  DDRC
93

    
94
// Orb pins
95
#define ORB1_RED   0
96
#define ORB1_GREEN 1
97
#define ORB1_BLUE  2
98
#define ORB2_RED   4
99
#define ORB2_GREEN 5
100
#define ORB2_BLUE  6
101

    
102

    
103
// ***************
104
// ** Debugging **
105
// ***************
106

    
107
//#define LIGHTS_DEBUG
108
#undef LIGHTS_DEBUG
109

    
110
#define LIGHTS_DEBUG_INIT                             DDRF=6;
111
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START         PORTF|=4;
112
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END           PORTF&=~4;
113
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START   PORTF|=2;
114
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END     PORTF&=~2;
115
#define LIGHTS_DEBUG_APPLY_START                      //PORTF|=2;
116
#define LIGHTS_DEBUG_APPLY_END                        //PORTF&=~2;
117

    
118

    
119
// ***********
120
// ** Masks **
121
// ***********
122

    
123
// Some useful bit masks. All of them are are calculated from the I/O definitions above. The calculations should be done
124
// at compile time (even if they are not, they are only executed once at startup).
125

    
126
// Masks for the individual LEDs
127
#define orb1_red_mask    _BV (ORB1_RED  )
128
#define orb1_green_mask  _BV (ORB1_GREEN)
129
#define orb1_blue_mask   _BV (ORB1_BLUE )
130
#define orb2_red_mask    _BV (ORB2_RED  )
131
#define orb2_green_mask  _BV (ORB2_GREEN)
132
#define orb2_blue_mask   _BV (ORB2_BLUE )
133

    
134
// Mask for all LEDs
135
#define all_orbs_mask \
136
    orb1_red_mask | orb1_green_mask | orb1_blue_mask | \
137
    orb2_red_mask | orb2_green_mask | orb2_blue_mask;
138

    
139
// Mask for the individual LEDs, organized as an array for programmatic access. The layout of this array is
140
// orb_mask[orb_num, color_num]
141
const uint8_t orb_mask[NUM_ORBS][NUM_COLORS]={
142
    { orb1_red_mask, orb1_green_mask, orb1_blue_mask },
143
    { orb2_red_mask, orb2_green_mask, orb2_blue_mask }
144
};
145

    
146
// ***********
147
// ** Types **
148
// ***********
149

    
150
struct pwm_channel_t { // 2 bytes
151
    uint8_t time;
152
    uint8_t mask;
153
};
154

    
155
struct pwm_t { // 13 bytes
156
    uint8_t init_mask;
157
    struct pwm_channel_t channel[num_pwm_channels];
158
};
159

    
160

    
161
// ***************
162
// ** Variables **
163
// ***************
164

    
165
// Whether to use PWM (true) or binary (false) orb mode. Not volatile because it's only read once per function.
166
bool enable_orb_pwm=true;
167

    
168
// The PWM channels and the buffer pointers. This data structure is triple buffered, see above for the reasons. Not
169
// volatile because they are not modified asynchronously (the read buffer is never written to asynchronously).
170
struct pwm_t pwm_buffer[3];
171

    
172
// The front buffer the ISR reads from. Other threads may not touch this pointer or the buffer it points to. Not
173
// volatile because it may only be modified by the ISR.
174
struct pwm_t *pwm_read_buffer =&pwm_buffer[0];
175

    
176
// The back buffer we can write to. The ISR may not touch this pointer or the buffer it points to. Not volatile because
177
// it may only be modified by the caller.
178
struct pwm_t *pwm_write_buffer=&pwm_buffer[1]; 
179

    
180
// The middle buffer to flip the write or read buffer with. Not volatile because it is only read once per function.
181
struct pwm_t *pwm_free_buffer =&pwm_buffer[2]; 
182

    
183
// Whether to perform a page flip on the beginning of the next PWM cycle. Not volatile because it is only read once
184
// per function.
185
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
186

    
187
// The orb value array. Orb values are written here to be sorted into pwm_channels. Not volatile because all accesses
188
// are from guarded (thread safe) functions.
189
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
190

    
191

    
192
// ****************
193
// ** Timer ISRs **
194
// ****************
195

    
196
// Not volatile because it is only accessed in the interrupt handler.
197
uint8_t current_pwm_channel=0;
198

    
199

    
200
static void output_compare (void) {
201
        // This function is called when an output compare condition may have occured.
202
        
203
    // If the OC interrupt is executed without delay, TCNT0==time+1 (where time==OCR0), because the interrupt flag is
204
        // queued at the next timer clock cycle after an output compare.
205

    
206
    // What may happen here is that the interrupt is delayed for more than one timer clock cycle (33 us). In that case,
207
        // the timer has already counted on and TCNT0 is bigger than current_channel_timer. Also, while during the ISR no
208
        // other interrupts will occur, the timer may still count on. Thus, we have to check the following channel as well.
209

    
210
        // Some optimization is possible in this function.
211

    
212
    while (1) {
213
                // The timer value at which the output compare interrupt should occur (one timer clock cycle after the output
214
                // compare condition is detected).
215
                uint8_t current_channel_time=pwm_read_buffer->channel[current_pwm_channel].time+1;
216
                
217
                // If the counter is not at this time yet, we don't have to do anything right now.
218
                if (current_channel_time>TCNT0) return;
219
                
220
                // We have an output compare condition for the current channel.
221
                
222
        // Turn the current channel off
223
        ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
224

    
225
                // If this was the last channel, exit
226
                if (current_pwm_channel==num_pwm_channels-1) return;
227

    
228
        // Increment the channel index
229
        current_pwm_channel++;
230

    
231
        // There is a next channel, load its OCR value
232
        if (pwm_read_buffer->channel[current_pwm_channel].time<255)
233
            OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
234
    }
235
}
236

    
237
SIGNAL (SIG_OVERFLOW0) {
238
#ifdef LIGHTS_DEBUG
239
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START
240
#endif
241

    
242
    if (pwm_page_flip) {
243
        // Flip the read buffer with the free buffer. We are in an ISR (and we didn't re-enable interrupts), so we don't
244
        // have to synchronize explicitly.
245
        struct pwm_t *temp = pwm_read_buffer;
246
        pwm_read_buffer    = pwm_free_buffer;
247
        pwm_free_buffer    = temp;
248
        pwm_page_flip=false;
249
    }
250

    
251
    // Turn only the appropriate PWM channels on. Do this directly on the orb port because at this point all orbs should
252
    // be off anyway.
253
    ORBPORT|=all_orbs_mask;
254
    ORBPORT&=pwm_read_buffer->init_mask;
255
    
256
    // Start at the first channel
257
    current_pwm_channel=0;
258
    
259
    // Load the first OCR
260
    OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
261

    
262
        // If this interrupt was delayed, we might already have an output compare condition.
263
        output_compare ();
264

    
265
#ifdef LIGHTS_DEBUG
266
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
267
#endif
268
}
269

    
270
SIGNAL(SIG_OUTPUT_COMPARE0) {
271
#ifdef LIGHTS_DEBUG
272
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
273
#endif
274

    
275
        // We have an output compare condition.
276
        output_compare ();
277
    
278
#ifdef LIGHTS_DEBUG
279
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END
280
#endif
281
}
282

    
283

    
284

    
285
// ************************************
286
// ** Internal orb setting functions **
287
// ************************************
288

    
289
static void sort_orbs_buffer (void) {
290
    // This function applies a bubble sort to sort the elements of the pwm_write_buffer->channel array by the time
291
    // field.
292
    // This implementation is heavily optimized. Note that due to the low (and constant) number of elements to be
293
    // sorted, the runtime complexity (O(n^2) for bubble sort) is not relevant here. In fact, a more advanced algorithm
294
    // like quick sort or merge sort might even be slower due to higher overhead.
295
    // That said, it is possible that selection sort (which is also in O(n^2)) would be faster that bubble sort because
296
    // 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
297
    // if the elements are already in the correct order would either have to be left out (doing the full search every
298
    // time, even if the array is already sorted) or done explicitly, so selection sort might actually be slower than
299
    // bubble sort, especially if the array is already sorted or almost sorted.
300
    
301
    // This implementation uses macros to make the algorithm more clear because the loop is rolled out and the function
302
    // would become quite long without macros.
303
    
304
    // Macro to swap two values of any type. Requires a variable of the appropriate type called swap_temp.
305
    #define swap(a,b) { swap_temp=a; a=b; b=swap_temp; }
306

    
307
    // Macro to do one bubble sorting step (compare & swap)
308
    #define bubble \
309
        if(a->time > b->time) \
310
        { \
311
            swap (a->time, b->time); \
312
            swap (a->mask, b->mask); \
313
            done=false; \
314
        }
315
    
316
    // Macro to move to the next bubble sort pair
317
    #define next { a++; b++; }
318

    
319
    // Whether no change was made during the last run, which means that all values are already in correct order.
320
    bool done;
321
    
322
    // A temporary variable for swapping.
323
    uint8_t swap_temp;
324
    
325
    // Precompute the first PWM channel (tested faster).
326
    struct pwm_channel_t *first=&(pwm_write_buffer->channel[0]);
327

    
328
    // Pointers to the two PWM channels under inspection
329
    struct pwm_channel_t *a, *b;
330

    
331
    // The actual sorting
332
    a=first; b=a+1; done=true;
333
    bubble next bubble next bubble next bubble next bubble
334
    if (done) return;
335

    
336
    a=first; b=a+1; done=true;
337
    bubble next bubble next bubble next bubble
338
    if (done) return;
339

    
340
    a=first; b=a+1; done=true;
341
    bubble next bubble next bubble
342
    if (done) return;
343

    
344
    a=first; b=a+1; done=true;
345
    bubble next bubble
346
    if (done) return;
347

    
348
    a=first; b=a+1; done=true;
349
    bubble
350
    if (done) return;
351

    
352
    // Undefine the macros so they do not disturb some other function.
353
    #undef next
354
    #undef bubble
355
    #undef swap
356
}
357

    
358
static void fill_orbs_buffer (void) {
359
    // We do not use a loop here because it introduces 27us overhead, which is quite much, given the total time for
360
    // optimized copying and sorting of 34us (elements already in correct order) to 71 us (elements in reverse order).
361
    
362
    #define copy_value(orb, color) \
363
        index=NUM_COLORS*orb+color; \
364
        time=orb_values[orb][color]; \
365
        mask=orb_mask[orb][color]; \
366
        \
367
        pwm_write_buffer->channel[index].time=time-1; \
368
        pwm_write_buffer->channel[index].mask=mask; \
369
        \
370
        if (time!=0) \
371
            pwm_write_buffer->init_mask &= ~mask; \
372

    
373
    uint8_t index, time, mask;
374
    copy_value(0,0); copy_value(0,1); copy_value(0,2);
375
    copy_value(1,0); copy_value(1,1); copy_value(1,2);
376
    
377
    #undef copy_value
378
}
379

    
380
static void apply_orbs (void) {
381
        /*
382
         * Some timing tests: Time for apply_orbs with interrupts disabled, in microseconds:
383
     *             Values in:     Correct order      Reverse order
384
     * Naive bubble sort:         148                217
385
     * Aborting bubble sort:       71                232
386
     * Only count to top:          73                189
387
     *
388
     * Loops rolled out:           61                120
389
     * Using pointers:             62                 98
390
     * Copy loop also rolled out:  35                 72
391
         *
392
         * Note that rolling out both loops and using pointers saves 52%/62% of time! 27us were spent on loop overhead,
393
         * which is quite much, considering an optimized total time for copying and sorting or 35us.
394
         */
395

    
396
#ifdef LIGHTS_DEBUG
397
    LIGHTS_DEBUG_APPLY_START
398
#endif
399

    
400
    if (enable_orb_pwm) {
401
        // PWM mode
402
        
403
        pwm_write_buffer->init_mask=~0;
404
    
405
        // 1. Write the orb values and corresponding masks to the pwm channels
406
        // array unsorted.
407
        fill_orbs_buffer ();
408

    
409
        // 2. sort the buffer.
410
        sort_orbs_buffer ();
411

    
412
        // Flip the write buffer with the free buffer.
413
        SYNC {
414
            struct pwm_t *temp = pwm_write_buffer;
415
            pwm_write_buffer   = pwm_free_buffer;
416
            pwm_free_buffer    = temp;
417
        }
418
        
419
        // On the next overflow, flip the read buffer with the free buffer.
420
        pwm_page_flip=true;
421
    }
422
    else {
423
        // Binary mode.
424
        // The outputs are inverted.
425
        uint8_t on=0;
426
        
427
        if (orb_values[0][0]) on |= orb_mask[0][0];
428
        if (orb_values[0][1]) on |= orb_mask[0][1];
429
        if (orb_values[0][2]) on |= orb_mask[0][2];
430
        if (orb_values[1][0]) on |= orb_mask[1][0];
431
        if (orb_values[1][1]) on |= orb_mask[1][1];
432
        if (orb_values[1][2]) on |= orb_mask[1][2];
433
    
434
        // Write the new orb states to the output port. Synchronized because it is a RMW operation.
435
        SYNC {
436
            uint8_t value=ORBPORT;
437
            value |= all_orbs_mask; // All orbs off
438
            value &= ~on; // Selected orbs on
439
            ORBPORT=value;
440
        }
441
    }
442
            
443
#ifdef LIGHTS_DEBUG
444
    LIGHTS_DEBUG_APPLY_END
445
#endif
446
}
447

    
448
static void set_orb_values (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
449
    // Write the values to the array, but do not sort them yet, as we might want to write the other orb values first so
450
    // we don't have to sort twice.
451
    // Any function calling this function will probably want to call apply_orbs() afterwards.
452
    orb_values[num][0]=red;
453
    orb_values[num][1]=green;
454
    orb_values[num][2]=blue;
455
}
456

    
457

    
458
// ***********************
459
// ** RGB color setting **
460
// ***********************
461

    
462
// All of these functions use set_orb_values to set the actual values, and then call apply_orbs() to apply the changes.
463
// set_orb_values should be used (even though it would be faster to set the array directly) because the binary/pwm mode
464
// has to be handled.
465
// All of these functions must be 
466

    
467
uint8_t orb_lock=0;
468

    
469
/**
470
 * Sets the specified orb to the specified color. The orbs must be initialized before this function may be used.
471
 * Note that, when setting both orbs, using orbs_set is faster then setting the orbs individually because the values are
472
 * only sorted once.
473
 *
474
 * @param num the number of the orb to set (0 or 1)
475
 * @param red the red value for the specified orb
476
 * @param green the green value for the specified orb
477
 * @param blue the blue value for the specified orb
478
 * @see 
479
 */
480
void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
481
        REQUIRE_LOCK_OR_RETURN(orb_lock);
482
        
483
    set_orb_values (num, red, green, blue);
484
    apply_orbs ();
485
        
486
        RELEASE_LOCK(orb_lock);
487
}
488

    
489
/**
490
 * Set orb1 to the color specified. The orbs must be initialized before this function may be used. Note that, when
491
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
492
 * once.
493
 *
494
 * @param red the red component of the color
495
 * @param green the green component of the color
496
 * @param blue the blue component of the color
497
 *
498
 * @see orb_init
499
 **/
500
void orb1_set (uint8_t red, uint8_t green, uint8_t blue) {
501
        REQUIRE_LOCK_OR_RETURN(orb_lock);
502
        
503
    set_orb_values (0, red, green, blue);
504
    apply_orbs ();
505

    
506
        RELEASE_LOCK(orb_lock);
507
}
508

    
509
/**
510
 * Set orb2 to the color specified. The orbs must be initialized before this function may be used. Note that, when
511
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
512
 * once.
513
 *
514
 * @param red_led the red component of the color
515
 * @param green_led the green component of the color
516
 * @param blue_led the blue component of the color
517
 *
518
 * @see orb_init
519
 **/
520
void orb2_set (uint8_t red, uint8_t green, uint8_t blue) {
521
        REQUIRE_LOCK_OR_RETURN(orb_lock);
522
        
523
    set_orb_values (1, red, green, blue);
524
    apply_orbs ();
525

    
526
        RELEASE_LOCK(orb_lock);
527
}
528

    
529
/**
530
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
531
 *
532
 * @param red_led the red component of the color
533
 * @param green_led the green component of the color
534
 * @param blue_led the blue component of the color
535
 *
536
 * @see orb_init, orb1_set, orb2_set
537
 **/
538
void orb_set (uint8_t red, uint8_t green, uint8_t blue) {
539
        REQUIRE_LOCK_OR_RETURN(orb_lock);
540
        
541
    set_orb_values (0, red, green, blue);
542
    set_orb_values (1, red, green, blue);
543
    apply_orbs ();
544

    
545
        RELEASE_LOCK(orb_lock);
546
}
547

    
548
/**
549
 * Set the orbs to the respective values. The orbs must be initialized before this function may be used. Note that, when
550
 * setting both orbs, this function is faster than calling orb1_set and orb2_set (or orb_n_set) because the values are
551
 * only sorted once.
552
 *
553
 * @param red1
554
 * @param green1
555
 * @param blue1
556
 * @param red2
557
 * @param green2
558
 * @param blue2
559
 * @see orb1_set
560
 * @see orb2_set
561
 * @see orb_n_set
562
 **/
563
void orbs_set (
564
    uint8_t red1, uint8_t green1, uint8_t blue1,
565
    uint8_t red2, uint8_t green2, uint8_t blue2) {
566

    
567
        REQUIRE_LOCK_OR_RETURN(orb_lock);
568

    
569
    set_orb_values (0, red1, green1, blue1);
570
    set_orb_values (1, red2, green2, blue2);
571
    apply_orbs ();
572

    
573
        RELEASE_LOCK(orb_lock);
574
}
575

    
576

    
577
// ******************************
578
// ** Predefined color setting **
579
// ******************************
580

    
581
// This functions just call the corresponding orb*_set functions. If the orbs array is accessed in any other way, it
582
// must be synchronized on orb_lock (REQUIRE_LOCK_OR_RETURN and RELEASE_LOCK)! Note that one synchronized function
583
// cannot call another one with this lock implementation.
584

    
585
// Macros for extracting a color.
586
#define C_RED(col)   (((col & 0xE0) >> 5) * 36)
587
#define C_GREEN(col) (((col & 0x1C) >> 2) * 36)
588
#define C_BLUE(col)  (((col & 0x03)     ) * 85)
589

    
590
/**
591
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
592
 *
593
 * @param num the number of the orb to set (0 or 1)
594
 * @param col the color to set the orbs to
595
 **/
596
void orb_n_set_color(uint8_t num, uint8_t col) {
597
    orb_n_set(num, C_RED(col), C_GREEN(col), C_BLUE(col));
598
}
599

    
600
/**
601
 * Set orb1 to the specified color. This function is intended to be used with the predefined colors.
602
 *
603
 * @param col the color to set the orbs to
604
 **/
605
void orb1_set_color(uint8_t col) {
606
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
607
}
608

    
609
/**
610
 * Set orb2 to the specified color. This function is intended to be used with the predefined colors.
611
 *
612
 * @param col the color to set the orbs to
613
 **/
614
void orb2_set_color(uint8_t col) {
615
    orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
616
}
617

    
618
/**
619
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
620
 *
621
 * @param col the color to set the orbs to
622
 **/
623
void orb_set_color(uint8_t col) {
624
    orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
625
}
626

    
627
/**
628
 * Set the orbs to the respective color. This function is intended to be used with the predefined colors.
629
 *
630
 * @param col1 the color to set orb 1 to
631
 * @param col2 the color to set orb 2 to
632
 **/
633
void orbs_set_color(uint8_t col1, uint8_t col2) {
634
    orbs_set (C_RED(col1), C_GREEN(col1), C_BLUE(col1), C_RED(col2), C_GREEN(col2), C_BLUE(col2));
635
}
636

    
637
#undef C_BLUE
638
#undef C_GREEN
639
#undef C_RED2
640

    
641

    
642
// ******************
643
// ** Mode setting **
644
// ******************
645

    
646
/**
647
 * Enables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
648
 * @see orb_set_mode
649
 **/
650
void orb_enable_timer (void) {
651
    // Use 8 bit TC0.
652
    //
653
    // Timer mode: We cannot use CTC mode because it can only clear on OCR0 (in contrast to the 16 bit timers which can
654
    // also use the ICR for that) and OCR0 is already used for generating output compare interrupts. We also need
655
    // immediate (non double buffered) update of OCR0, so the only mode left is "Normal".
656
    //
657
    // Note that for a timer counting from 0 to 255, there are 256 states and thus 257 output possibilities
658
    // (0/256...256/256)! However, there are only 256 values in the byte used to specify the PWM value. Possible ways
659
    // to deal with that:
660
    //   1. use a 16 bit variable for the PWM value (memory waste, overhead)
661
    //   2. use an additional flag for the 257th value (inconvenient)
662
    //   3. use 1/256...256/256 (skip 0, never complete off)
663
    //   4. use 0/256...256/256 (skip 256, never complete on)
664
    //   5. skip a value somewhere in the middle
665
    //   6. reload the timer after 254
666
    // For this implementation, variant 4 was chosen.
667
    //
668
    // Using an 8 bit timer has the added advantage that all the comparisons are faster.
669
    
670
    // Normal mode, Compare match output off, Prescaler
671
    TCCR0=_BV(CS02) | _BV(CS01); // 256, 120 Hz
672
        // The next higher prescaler would be 1024 (30 Hz) which makes the orbs flicker visibly.
673

    
674
    // Enable the interrupts
675
    TIMSK|= _BV(OCIE0) | _BV(TOIE0);
676
}
677

    
678
/**
679
 * Disables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
680
 * @see orb_set_mode
681
 **/
682
void orb_disable_timer (void) {
683
    // Disable the interrupts
684
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
685
}
686

    
687

    
688
void orb_set_mode (orb_mode_t mode) {
689
    // Set enable_orb_pwm to the appropriate value and disable or enable the timer.
690
    if (mode==orb_mode_binary) {
691
        orb_disable_timer ();
692

    
693
        enable_orb_pwm=false;
694
        apply_orbs ();
695
    }
696
    else { // orb_mode_pwm
697
        enable_orb_pwm=true;
698
        apply_orbs ();
699

    
700
        orb_enable_timer ();
701
    }
702
}
703

    
704

    
705
// ********************
706
// ** Initialization **
707
// ********************
708

    
709
// Orb initialization code common to all modes.
710
static void orb_init_common (void) {
711
    // Enable the output ports and turn off the LEDs
712
    ORBPORT |=  all_orbs_mask;
713
    ORBDDR  |=  all_orbs_mask;
714

    
715
    // Set all orbs to "off"
716
    orb_set (0, 0, 0);
717
    
718
#ifdef LIGHTS_DEBUG
719
    LIGHTS_DEBUG_INIT
720
#endif
721
}
722

    
723
/**
724
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
725
 * 
726
 * @see orb_init_pwm
727
 **/
728
void orb_init_binary (void) {
729
    orb_init_common ();
730
    orb_set_mode (orb_mode_binary);
731
}
732

    
733
/**
734
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
735
 * 
736
 * @see orb_init_binary
737
 **/
738
void orb_init_pwm (void) {
739
    orb_init_common ();
740
    orb_set_mode (orb_mode_pwm);
741
}
742

    
743
/**
744
 * Initializes the orbs in default mode. One of the orb_init* functions must be called before the orbs can be used. Use
745
 * the orb_init_binary or orb_init_pwm function if you want one specific mode.
746
 *
747
 * @see orb_init_pwm
748
 * @see orb_init_binary
749
 **/
750
void orb_init () {
751
    orb_init_pwm ();
752
}