Statistics
| Revision:

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

History | View | Annotate | Download (25.2 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

    
75

    
76
#include "lights.h"
77

    
78
#include <avr/interrupt.h>
79

    
80
#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
#define ORBPORT PORTC
98
#define ORBDDR  DDRC
99

    
100
// 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

    
108

    
109
// ***************
110
// ** Debugging **
111
// ***************
112

    
113
//#define LIGHTS_DEBUG
114
#undef LIGHTS_DEBUG
115

    
116
#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

    
124

    
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
};
151

    
152
// ***********
153
// ** Types **
154
// ***********
155

    
156
struct pwm_channel_t { // 2 bytes
157
    uint8_t time;
158
    uint8_t mask;
159
};
160

    
161
struct pwm_t { // 13 bytes
162
    uint8_t init_mask;
163
    struct pwm_channel_t channel[num_pwm_channels];
164
};
165

    
166

    
167
// ***************
168
// ** Variables **
169
// ***************
170

    
171
// 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

    
174
// 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

    
178
// 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

    
182
// 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

    
186
// 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

    
189
// 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

    
193
// 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
        
209
    // 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
}
242

    
243
SIGNAL (SIG_OVERFLOW0) {
244
#ifdef LIGHTS_DEBUG
245
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START
246
#endif
247

    
248
    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

    
257
    // 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

    
268
        // If this interrupt was delayed, we might already have an output compare condition.
269
        output_compare ();
270

    
271
#ifdef LIGHTS_DEBUG
272
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
273
#endif
274
}
275

    
276
SIGNAL(SIG_OUTPUT_COMPARE0) {
277
#ifdef LIGHTS_DEBUG
278
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
279
#endif
280

    
281
        // 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

    
289

    
290

    
291
// ************************************
292
// ** Internal orb setting functions **
293
// ************************************
294

    
295
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

    
313
    // 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

    
325
    // 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

    
334
    // Pointers to the two PWM channels under inspection
335
    struct pwm_channel_t *a, *b;
336

    
337
    // 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

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

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

    
350
    a=first; b=a+1; done=true;
351
    bubble next bubble
352
    if (done) return;
353

    
354
    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
}
363

    
364
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

    
379
    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

    
386
static void apply_orbs (void) {
387
        /*
388
         * Some timing tests: Time for apply_orbs with interrupts disabled, in microseconds:
389
     *             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

    
402
#ifdef LIGHTS_DEBUG
403
    LIGHTS_DEBUG_APPLY_START
404
#endif
405

    
406
    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

    
415
        // 2. sort the buffer.
416
        sort_orbs_buffer ();
417

    
418
        // 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
}
453

    
454
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
/**
476
 * 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
 *
480
 * @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
 *
500
 * @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
 * @see orb_init
505
 **/
506
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

    
512
        RELEASE_LOCK(orb_lock);
513
}
514

    
515
/**
516
 * 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
 *
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
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
}
534

    
535
/**
536
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
537
 *
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
 * @see orb_init, orb1_set, orb2_set
543
 **/
544
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
}
553

    
554
/**
555
 * 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
 *
559
 * @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
 **/
569
void orbs_set (
570
    uint8_t red1, uint8_t green1, uint8_t blue1,
571
    uint8_t red2, uint8_t green2, uint8_t blue2) {
572

    
573
        REQUIRE_LOCK_OR_RETURN(orb_lock);
574

    
575
    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
}
581

    
582

    
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
/**
597
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
598
 *
599
 * @param num the number of the orb to set (0 or 1)
600
 * @param col the color to set the orbs to
601
 **/
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
 *
609
 * @param col the color to set the orbs to
610
 **/
611
void orb1_set_color(uint8_t col) {
612
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
613
}
614

    
615
/**
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
}
623

    
624
/**
625
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
626
 *
627
 * @param col the color to set the orbs to
628
 **/
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
 *
636
 * @param col1 the color to set orb 1 to
637
 * @param col2 the color to set orb 2 to
638
 **/
639
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

    
643
#undef C_BLUE
644
#undef C_GREEN
645
#undef C_RED2
646

    
647

    
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
        // The next higher prescaler would be 1024 (30 Hz) which makes the orbs flicker visibly.
679

    
680
    // Enable the interrupts
681
    TIMSK|= _BV(OCIE0) | _BV(TOIE0);
682
}
683

    
684
/**
685
 * 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
 **/
688
void orb_disable_timer (void) {
689
    // Disable the interrupts
690
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
691
}
692

    
693

    
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
/**
730
 * 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
 **/
734
void orb_init_binary (void) {
735
    orb_init_common ();
736
    orb_set_mode (orb_mode_binary);
737
}
738

    
739
/**
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

    
749
/**
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
}