Project

General

Profile

Statistics
| Revision:

root / branches / library_refactor / projects / libdragonfly / lights.c @ 1139

History | View | Annotate | Download (29.8 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
 * Quick start: call orb_init_pwm or orb_init_binary, depending on which mode you want to use. Call orb*set or
50
 * orb*set_color to set the orbs.
51
 * 
52
 * The orbs have two modes of operation: PWM mode and binary mode. In PWM mode, a pwm signal is generated by a hardware
53
 * timer and the orbs can be set to a value of 0 through 255. In binary mode, the orbs can only be turned on or off and
54
 * a value of 0 means "off" and any other value means "on". The mode can be chosen on initialization and can be changed
55
 * at runtime using the orb_set_mode function.
56
 *
57
 * Operation (PWM mode): On timer overflow, all LEDs with a value>0 are turned on and the output compare value for the
58
 * first LED is loaded. On compare match, the corresponding LED is turned off and the next output compare value is
59
 * loaded. All masks are precomputed and sorted by time when setting the values.
60
 *
61
 * The data structure (pwm_t) containing the PWM times and masks is triple buffered. This is because the buffer the ISR
62
 * is reading from may only be modified on timer overflow before the next PWM sequence is started, because otherwise the
63
 * next OCR value might be sed to a value smaller than the current timer value, resulting in the remaining channels not
64
 * being turned off in that PWM period (flash to on). When using two buffers, the page flip can only occur on a timer
65
 * overflow for the same reason. So after writing a buffer and marking it for page flip, neither of the buffers could be
66
 * modified because the front buffer is read by the ISR and the back buffer could be switched at any time. So the
67
 * calling thread would have to be delayed by up to one full PWM period (8ms in the current implementation, but
68
 * 20ms-50ms would be a reasonable value to expect here). To avoid this, triple buffering is used.
69
 *
70
 * The code for applying the orbs is fairly optimized. See the apply_orbs function for some time measurements and
71
 * further nodes.
72
 *
73
 * The PWM frequency is 120Hz (8ms period time). The next lower frequency (determined by the prescaler) is 30 Hz which
74
 * is too slow (orbs flicker).
75
 *
76
 * The orbs code is thread safe, which means that the functions may be called from another interrupt handler. If there
77
 * are multiple concurrent calls to the orb*set* functions, one of them is ignored and the orbs are never left in an
78
 * inconsistent state. For example, if the orbs are set to green by the main thread and to red by an interrupt handler,
79
 * the resulting color will be either red or green, but never yellow.
80
 * Thread safety is achieved by grabbing a lock at the beginning of all functions that modify the orb code and releasing
81
 * the lock at the end. If the lock is already taken, the function just returns doing nothing.
82
 *
83
 * Some performance measurements:
84
 *   - Time for setting new orb values (PWM mode):       35us-72us (depending on the degree to which the array is
85
 *                                                                  already in the correct order)
86
 *   - Time for setting new orb values (binary mode):        5.5us
87
 *
88
 *   - Interrupt time (PWM mode only):                         8us (overflow)
89
 *                                                            10us (output compare)
90
 *                                                             6us (last output compare)
91
 *                                                            30us (output compare, all value equal)
92
 *
93
 *   - Maximum total interrupt time per period:               64us
94
 *   - Maximum CPU usage for interrupts (PWM mode only):     <0.8%
95
 *
96
 *   - Maximum contiguous synchronized block:                 30us (output compare interrupt, all values equal)
97
 *
98
 * There are some potential optimizations left. See the source code for more information.
99
 *
100
 * A note on robustness: if the output compare interrupt is disabled for too long, either due to a long ISR or a long
101
 * synchronized code block, the orbs will flicker to brighter values for being turned off too late. With software PWM,
102
 * there's nothing at all to be done about that. The problem can be alleviated by using a lower PWM frequency, but then
103
 * the orbs will start flickering all the time due to the low update frequency.
104
 * Some measurements: with 100us synchronized blocks, the flickering is accepptably low. Longer synchronized blocks
105
 * mean more flickering. At 1ms synchronized blocks, the flickering is quite bad, especially for low orb values. Note
106
 * that orb value 0 never flickers at all because the corresponding channels are not turned on at all.
107
 * Test code (not the _delay_us restrictions!)
108
 *   orb_set (1,1,1); while (1) { SYNC { for (uint8_t m=0; m<10; ++m) { _delay_us(10); } } }
109
 **/
110

    
111
/*
112

113
All different:
114

115
Overflow: 8us
116

117
OC: 5*10+1*6
118

119
All same:
120
OC: 30us
121

122
*/
123

    
124
/*
125
 * Test cases:
126
 *   - The following code has to work without flickering:
127
 *     orb_init_pwm(); while(1) { orbs_set(1,1,1,254,254,254); }
128
 */
129

    
130
/*
131
 * Possible optimizations:
132
 *   - Use pointers instead of indicies for current_pwm_channel
133
 *   - Optimize output_compare()
134
 *   - Use a different sorting algorithm (see sort_orbs_buffer for further comments on this issue)
135
 *   - Use pointers in fill_orbs_buffer
136
 *   - Optimized orb_set (use the knowledge that there are only 3 distinct values, don't use a loop but unroll the
137
 *     sorting, which is no problem for 3 values)
138
 *   - Use a lower update frequency. The next higher prescaler value leads to a frequency of 30Hz which is too low (the
139
 *     orbs are flickering). So the timer would have to be reloaded manually after 127 to generate 60Hz. This would
140
 *     decrease the resolution from 8 to 7 bit, but 128 steps should still be enough.
141
 *   - On setting the orbs, combine channels with the same time. This would reduce the all-values-equal OC interrupt
142
 *     (30us) to the time of one regular OC interrupt (6us/10us). Also, it would reduce the total cpu usage whenever
143
 *     some of the values are equal.
144
 *
145
 * When code is changed, the performance measurements above should be redone.
146
 */
147

    
148

    
149

    
150
#include "lights.h"
151

    
152
#include <avr/interrupt.h>
153

    
154
#include "dragonfly_lib.h"
155

    
156

    
157
// ***************
158
// ** Constants **
159
// ***************
160

    
161
#define NUM_ORBS 2   // Number or orbs
162
#define NUM_COLORS 3 // Number of colors per orb
163
#define num_pwm_channels NUM_ORBS*NUM_COLORS
164

    
165

    
166
// *********
167
// ** I/O **
168
// *********
169

    
170
// Orb port
171
#define ORBPORT PORTC
172
#define ORBDDR  DDRC
173

    
174
// Orb pins
175
#define ORB1_RED   0
176
#define ORB1_GREEN 1
177
#define ORB1_BLUE  2
178
#define ORB2_RED   4
179
#define ORB2_GREEN 5
180
#define ORB2_BLUE  6
181

    
182

    
183
// ***************
184
// ** Debugging **
185
// ***************
186

    
187
//#define LIGHTS_DEBUG
188
#undef LIGHTS_DEBUG
189

    
190
#define LIGHTS_DEBUG_INIT                             DDRF=6;
191
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START         PORTF|=4;
192
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END           PORTF&=~4;
193
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START   PORTF|=2;
194
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END     PORTF&=~2;
195
#define LIGHTS_DEBUG_APPLY_START                      //PORTF|=2;
196
#define LIGHTS_DEBUG_APPLY_END                        //PORTF&=~2;
197

    
198

    
199
// ***********
200
// ** Masks **
201
// ***********
202

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

    
206
// Masks for the individual LEDs
207
#define orb1_red_mask    _BV (ORB1_RED  )
208
#define orb1_green_mask  _BV (ORB1_GREEN)
209
#define orb1_blue_mask   _BV (ORB1_BLUE )
210
#define orb2_red_mask    _BV (ORB2_RED  )
211
#define orb2_green_mask  _BV (ORB2_GREEN)
212
#define orb2_blue_mask   _BV (ORB2_BLUE )
213

    
214
// Mask for all LEDs
215
#define all_orbs_mask \
216
    orb1_red_mask | orb1_green_mask | orb1_blue_mask | \
217
    orb2_red_mask | orb2_green_mask | orb2_blue_mask;
218

    
219
// Mask for the individual LEDs, organized as an array for programmatic access. The layout of this array is
220
// orb_mask[orb_num, color_num]
221
const uint8_t orb_mask[NUM_ORBS][NUM_COLORS]={
222
    { orb1_red_mask, orb1_green_mask, orb1_blue_mask },
223
    { orb2_red_mask, orb2_green_mask, orb2_blue_mask }
224
};
225

    
226
// ***********
227
// ** Types **
228
// ***********
229

    
230
struct pwm_channel_t { // 2 bytes
231
    uint8_t time;
232
    uint8_t mask;
233
};
234

    
235
struct pwm_t { // 13 bytes
236
    uint8_t init_mask;
237
    struct pwm_channel_t channel[num_pwm_channels];
238
};
239

    
240

    
241
// ***************
242
// ** Variables **
243
// ***************
244

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

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

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

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

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

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

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

    
271

    
272
// ****************
273
// ** Timer ISRs **
274
// ****************
275

    
276
// Not volatile because it is only accessed in the interrupt handler.
277
uint8_t current_pwm_channel=0;
278

    
279

    
280
static void output_compare (void) {
281
        // This function is called when an output compare condition may have occured.
282
        
283
    // If the OC interrupt is executed without delay, TCNT0==time+1 (where time==OCR0), because the interrupt flag is
284
        // queued at the next timer clock cycle after an output compare.
285

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

    
290
        // Some optimization is possible in this function.
291

    
292
    while (1) {
293
                // The timer value at which the output compare interrupt should occur (one timer clock cycle after the output
294
                // compare condition is detected).
295
                uint8_t current_channel_time=pwm_read_buffer->channel[current_pwm_channel].time+1;
296
                
297
                // If the counter is not at this time yet, we don't have to do anything right now.
298
                if (current_channel_time>TCNT0) return;
299
                
300
                // We have an output compare condition for the current channel.
301
                
302
        // Turn the current channel off
303
        ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
304

    
305
                // If this was the last channel, exit
306
                if (current_pwm_channel==num_pwm_channels-1) return;
307

    
308
        // Increment the channel index
309
        current_pwm_channel++;
310

    
311
        // There is a next channel, load its OCR value
312
        if (pwm_read_buffer->channel[current_pwm_channel].time<255)
313
            OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
314
    }
315
}
316

    
317
SIGNAL (SIG_OVERFLOW0) {
318
#ifdef LIGHTS_DEBUG
319
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START
320
#endif
321

    
322
    if (pwm_page_flip) {
323
        // Flip the read buffer with the free buffer. We are in an ISR (and we didn't re-enable interrupts), so we don't
324
        // have to synchronize explicitly.
325
        struct pwm_t *temp = pwm_read_buffer;
326
        pwm_read_buffer    = pwm_free_buffer;
327
        pwm_free_buffer    = temp;
328
        pwm_page_flip=false;
329
    }
330

    
331
    // Turn only the appropriate PWM channels on. Do this directly on the orb port because at this point all orbs should
332
    // be off anyway.
333
    ORBPORT|=all_orbs_mask;
334
    ORBPORT&=pwm_read_buffer->init_mask;
335
    
336
    // Start at the first channel
337
    current_pwm_channel=0;
338
    
339
    // Load the first OCR
340
    OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
341

    
342
        // If this interrupt was delayed, we might already have an output compare condition.
343
        output_compare ();
344

    
345
#ifdef LIGHTS_DEBUG
346
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
347
#endif
348
}
349

    
350
SIGNAL(SIG_OUTPUT_COMPARE0) {
351
#ifdef LIGHTS_DEBUG
352
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
353
#endif
354

    
355
        // We have an output compare condition.
356
        output_compare ();
357
    
358
#ifdef LIGHTS_DEBUG
359
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END
360
#endif
361
}
362

    
363

    
364

    
365
// ************************************
366
// ** Internal orb setting functions **
367
// ************************************
368

    
369
static void sort_orbs_buffer (void) {
370
    // This function applies a bubble sort to sort the elements of the pwm_write_buffer->channel array by the time
371
    // field.
372
    // This implementation is heavily optimized. Note that due to the low (and constant) number of elements to be
373
    // sorted, the runtime complexity (O(n^2) for bubble sort) is not relevant here. In fact, a more advanced algorithm
374
    // like quick sort or merge sort might even be slower due to higher overhead.
375
    // That said, it is possible that selection sort (which is also in O(n^2)) would be faster that bubble sort because
376
    // 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
377
    // if the elements are already in the correct order would either have to be left out (doing the full search every
378
    // time, even if the array is already sorted) or done explicitly, so selection sort might actually be slower than
379
    // bubble sort, especially if the array is already sorted or almost sorted.
380
    
381
    // This implementation uses macros to make the algorithm more clear because the loop is rolled out and the function
382
    // would become quite long without macros.
383
    
384
    // Macro to swap two values of any type. Requires a variable of the appropriate type called swap_temp.
385
    #define swap(a,b) { swap_temp=a; a=b; b=swap_temp; }
386

    
387
    // Macro to do one bubble sorting step (compare & swap)
388
    #define bubble \
389
        if(a->time > b->time) \
390
        { \
391
            swap (a->time, b->time); \
392
            swap (a->mask, b->mask); \
393
            done=false; \
394
        }
395
    
396
    // Macro to move to the next bubble sort pair
397
    #define next { a++; b++; }
398

    
399
    // Whether no change was made during the last run, which means that all values are already in correct order.
400
    bool done;
401
    
402
    // A temporary variable for swapping.
403
    uint8_t swap_temp;
404
    
405
    // Precompute the first PWM channel (tested faster).
406
    struct pwm_channel_t *first=&(pwm_write_buffer->channel[0]);
407

    
408
    // Pointers to the two PWM channels under inspection
409
    struct pwm_channel_t *a, *b;
410

    
411
    // The actual sorting
412
    a=first; b=a+1; done=true;
413
    bubble next bubble next bubble next bubble next bubble
414
    if (done) return;
415

    
416
    a=first; b=a+1; done=true;
417
    bubble next bubble next bubble next bubble
418
    if (done) return;
419

    
420
    a=first; b=a+1; done=true;
421
    bubble next bubble next bubble
422
    if (done) return;
423

    
424
    a=first; b=a+1; done=true;
425
    bubble next bubble
426
    if (done) return;
427

    
428
    a=first; b=a+1; done=true;
429
    bubble
430
    if (done) return;
431

    
432
    // Undefine the macros so they do not disturb some other function.
433
    #undef next
434
    #undef bubble
435
    #undef swap
436
}
437

    
438
static void fill_orbs_buffer (void) {
439
    // We do not use a loop here because it introduces 27us overhead, which is quite much, given the total time for
440
    // optimized copying and sorting of 34us (elements already in correct order) to 71 us (elements in reverse order).
441
    
442
    #define copy_value(orb, color) \
443
        index=NUM_COLORS*orb+color; \
444
        time=orb_values[orb][color]; \
445
        mask=orb_mask[orb][color]; \
446
        \
447
        pwm_write_buffer->channel[index].time=time-1; \
448
        pwm_write_buffer->channel[index].mask=mask; \
449
        \
450
        if (time!=0) \
451
            pwm_write_buffer->init_mask &= ~mask; \
452

    
453
    uint8_t index, time, mask;
454
    copy_value(0,0); copy_value(0,1); copy_value(0,2);
455
    copy_value(1,0); copy_value(1,1); copy_value(1,2);
456
    
457
    #undef copy_value
458
}
459

    
460
static void apply_orbs (void) {
461
        /*
462
         * Some timing tests: Time for apply_orbs with interrupts disabled, in us:
463
     *             Values in:     Correct order      Reverse order
464
     * Naive bubble sort:         148                217
465
     * Aborting bubble sort:       71                232
466
     * Only count to top:          73                189
467
     *
468
     * Loops rolled out:           61                120
469
     * Using pointers:             62                 98
470
     * Copy loop also rolled out:  35                 72
471
         *
472
         * Note that rolling out both loops and using pointers saves 52%/62% of time! 27us were spent on loop overhead,
473
         * which is quite much, considering an optimized total time for copying and sorting or 35us.
474
         */
475

    
476
#ifdef LIGHTS_DEBUG
477
    LIGHTS_DEBUG_APPLY_START
478
#endif
479

    
480
    if (enable_orb_pwm) {
481
        // PWM mode
482
        
483
        pwm_write_buffer->init_mask=~0;
484
    
485
        // 1. Write the orb values and corresponding masks to the pwm channels
486
        // array unsorted.
487
        fill_orbs_buffer ();
488

    
489
        // 2. sort the buffer.
490
        sort_orbs_buffer ();
491

    
492
        // Flip the write buffer with the free buffer.
493
        SYNC {
494
            struct pwm_t *temp = pwm_write_buffer;
495
            pwm_write_buffer   = pwm_free_buffer;
496
            pwm_free_buffer    = temp;
497
        }
498
        
499
        // On the next overflow, flip the read buffer with the free buffer.
500
        pwm_page_flip=true;
501
    }
502
    else {
503
        // Binary mode.
504
        // The outputs are inverted.
505
        uint8_t on=0;
506
        
507
        if (orb_values[0][0]) on |= orb_mask[0][0];
508
        if (orb_values[0][1]) on |= orb_mask[0][1];
509
        if (orb_values[0][2]) on |= orb_mask[0][2];
510
        if (orb_values[1][0]) on |= orb_mask[1][0];
511
        if (orb_values[1][1]) on |= orb_mask[1][1];
512
        if (orb_values[1][2]) on |= orb_mask[1][2];
513
    
514
        // Write the new orb states to the output port. Synchronized because it is a RMW operation.
515
        SYNC {
516
            uint8_t value=ORBPORT;
517
            value |= all_orbs_mask; // All orbs off
518
            value &= ~on; // Selected orbs on
519
            ORBPORT=value;
520
        }
521
    }
522
            
523
#ifdef LIGHTS_DEBUG
524
    LIGHTS_DEBUG_APPLY_END
525
#endif
526
}
527

    
528
static void set_orb_values (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
529
    // Write the values to the array, but do not sort them yet, as we might want to write the other orb values first so
530
    // we don't have to sort twice.
531
    // Any function calling this function will probably want to call apply_orbs() afterwards.
532
    orb_values[num][0]=red;
533
    orb_values[num][1]=green;
534
    orb_values[num][2]=blue;
535
}
536

    
537

    
538
// ***********************
539
// ** RGB color setting **
540
// ***********************
541

    
542
// All of these functions use set_orb_values to set the actual values, and then call apply_orbs() to apply the changes.
543
// set_orb_values should be used (even though it would be faster to set the array directly) because the binary/pwm mode
544
// has to be handled.
545
// All of these functions must be 
546

    
547
uint8_t orb_lock=0;
548

    
549
/**
550
 * Sets the specified orb to the specified color. The orbs must be initialized before this function may be used.
551
 * Note that, when setting both orbs, using orbs_set is faster then setting the orbs individually because the values are
552
 * only sorted once.
553
 *
554
 * @param num the number of the orb to set (0 or 1)
555
 * @param red the red value for the specified orb
556
 * @param green the green value for the specified orb
557
 * @param blue the blue value for the specified orb
558
 * @see 
559
 */
560
void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
561
        REQUIRE_LOCK_OR_RETURN(orb_lock);
562
        
563
    set_orb_values (num, red, green, blue);
564
    apply_orbs ();
565
        
566
        RELEASE_LOCK(orb_lock);
567
}
568

    
569
/**
570
 * Set orb1 to the color specified. The orbs must be initialized before this function may be used. Note that, when
571
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
572
 * once.
573
 *
574
 * @param red the red component of the color
575
 * @param green the green component of the color
576
 * @param blue the blue component of the color
577
 *
578
 * @see orb_init
579
 **/
580
void orb1_set (uint8_t red, uint8_t green, uint8_t blue) {
581
        REQUIRE_LOCK_OR_RETURN(orb_lock);
582
        
583
    set_orb_values (0, red, green, blue);
584
    apply_orbs ();
585

    
586
        RELEASE_LOCK(orb_lock);
587
}
588

    
589
/**
590
 * Set orb2 to the color specified. The orbs must be initialized before this function may be used. Note that, when
591
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
592
 * once.
593
 *
594
 * @param red_led the red component of the color
595
 * @param green_led the green component of the color
596
 * @param blue_led the blue component of the color
597
 *
598
 * @see orb_init
599
 **/
600
void orb2_set (uint8_t red, uint8_t green, uint8_t blue) {
601
        REQUIRE_LOCK_OR_RETURN(orb_lock);
602
        
603
    set_orb_values (1, red, green, blue);
604
    apply_orbs ();
605

    
606
        RELEASE_LOCK(orb_lock);
607
}
608

    
609
/**
610
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
611
 *
612
 * @param red_led the red component of the color
613
 * @param green_led the green component of the color
614
 * @param blue_led the blue component of the color
615
 *
616
 * @see orb_init, orb1_set, orb2_set
617
 **/
618
void orb_set (uint8_t red, uint8_t green, uint8_t blue) {
619
        REQUIRE_LOCK_OR_RETURN(orb_lock);
620
        
621
    set_orb_values (0, red, green, blue);
622
    set_orb_values (1, red, green, blue);
623
    apply_orbs ();
624

    
625
        RELEASE_LOCK(orb_lock);
626
}
627

    
628
/**
629
 * Set the orbs to the respective values. The orbs must be initialized before this function may be used. Note that, when
630
 * setting both orbs, this function is faster than calling orb1_set and orb2_set (or orb_n_set) because the values are
631
 * only sorted once.
632
 *
633
 * @param red1
634
 * @param green1
635
 * @param blue1
636
 * @param red2
637
 * @param green2
638
 * @param blue2
639
 * @see orb1_set
640
 * @see orb2_set
641
 * @see orb_n_set
642
 **/
643
void orbs_set (
644
    uint8_t red1, uint8_t green1, uint8_t blue1,
645
    uint8_t red2, uint8_t green2, uint8_t blue2) {
646

    
647
        REQUIRE_LOCK_OR_RETURN(orb_lock);
648

    
649
    set_orb_values (0, red1, green1, blue1);
650
    set_orb_values (1, red2, green2, blue2);
651
    apply_orbs ();
652

    
653
        RELEASE_LOCK(orb_lock);
654
}
655

    
656

    
657
// ******************************
658
// ** Predefined color setting **
659
// ******************************
660

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

    
665
// Macros for extracting a color.
666
#define C_RED(col)   (((col & 0xE0) >> 5) * 36)
667
#define C_GREEN(col) (((col & 0x1C) >> 2) * 36)
668
#define C_BLUE(col)  (((col & 0x03)     ) * 85)
669

    
670
/**
671
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
672
 *
673
 * @param num the number of the orb to set (0 or 1)
674
 * @param col the color to set the orbs to
675
 **/
676
void orb_n_set_color(uint8_t num, uint8_t col) {
677
    orb_n_set(num, C_RED(col), C_GREEN(col), C_BLUE(col));
678
}
679

    
680
/**
681
 * Set orb1 to the specified color. This function is intended to be used with the predefined colors.
682
 *
683
 * @param col the color to set the orbs to
684
 **/
685
void orb1_set_color(uint8_t col) {
686
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
687
}
688

    
689
/**
690
 * Set orb2 to the specified color. This function is intended to be used with the predefined colors.
691
 *
692
 * @param col the color to set the orbs to
693
 **/
694
void orb2_set_color(uint8_t col) {
695
    orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
696
}
697

    
698
/**
699
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
700
 *
701
 * @param col the color to set the orbs to
702
 **/
703
void orb_set_color(uint8_t col) {
704
    orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
705
}
706

    
707
/**
708
 * Set the orbs to the respective color. This function is intended to be used with the predefined colors.
709
 *
710
 * @param col1 the color to set orb 1 to
711
 * @param col2 the color to set orb 2 to
712
 **/
713
void orbs_set_color(uint8_t col1, uint8_t col2) {
714
    orbs_set (C_RED(col1), C_GREEN(col1), C_BLUE(col1), C_RED(col2), C_GREEN(col2), C_BLUE(col2));
715
}
716

    
717
#undef C_BLUE
718
#undef C_GREEN
719
#undef C_RED2
720

    
721

    
722
// ******************
723
// ** Mode setting **
724
// ******************
725

    
726
/**
727
 * Enables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
728
 * @see orb_set_mode
729
 **/
730
void orb_enable_timer (void) {
731
    // Use 8 bit TC0.
732
    //
733
    // Timer mode: We cannot use CTC mode because it can only clear on OCR0 (in contrast to the 16 bit timers which can
734
    // also use the ICR for that) and OCR0 is already used for generating output compare interrupts. We also need
735
    // immediate (non double buffered) update of OCR0, so the only mode left is "Normal".
736
    //
737
    // Note that for a timer counting from 0 to 255, there are 256 states and thus 257 output possibilities
738
    // (0/256...256/256)! However, there are only 256 values in the byte used to specify the PWM value. Possible ways
739
    // to deal with that:
740
    //   1. use a 16 bit variable for the PWM value (memory waste, overhead)
741
    //   2. use an additional flag for the 257th value (inconvenient)
742
    //   3. use 1/256...256/256 (skip 0, never complete off)
743
    //   4. use 0/256...256/256 (skip 256, never complete on)
744
    //   5. skip a value somewhere in the middle
745
    //   6. reload the timer after 254
746
    // For this implementation, variant 4 was chosen.
747
    //
748
    // Using an 8 bit timer has the added advantage that all the comparisons are faster.
749
    
750
    // Normal mode, Compare match output off, Prescaler
751
    TCCR0=_BV(CS02) | _BV(CS01); // 256, 120 Hz
752

    
753
    // Enable the interrupts
754
    TIMSK|= _BV(OCIE0) | _BV(TOIE0);
755
}
756

    
757
/**
758
 * Disables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
759
 * @see orb_set_mode
760
 **/
761
void orb_disable_timer (void) {
762
    // Disable the interrupts
763
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
764
}
765

    
766

    
767
void orb_set_mode (orb_mode_t mode) {
768
    // Set enable_orb_pwm to the appropriate value and disable or enable the timer.
769
    if (mode==orb_mode_binary) {
770
        orb_disable_timer ();
771

    
772
        enable_orb_pwm=false;
773
        apply_orbs ();
774
    }
775
    else { // orb_mode_pwm
776
        enable_orb_pwm=true;
777
        apply_orbs ();
778

    
779
        orb_enable_timer ();
780
    }
781
}
782

    
783

    
784
// ********************
785
// ** Initialization **
786
// ********************
787

    
788
// Orb initialization code common to all modes.
789
static void orb_init_common (void) {
790
    // Enable the output ports and turn off the LEDs
791
    ORBPORT |=  all_orbs_mask;
792
    ORBDDR  |=  all_orbs_mask;
793

    
794
    // Set all orbs to "off"
795
    orb_set (0, 0, 0);
796
    
797
#ifdef LIGHTS_DEBUG
798
    LIGHTS_DEBUG_INIT
799
#endif
800
}
801

    
802
/**
803
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
804
 * 
805
 * @see orb_init_pwm
806
 **/
807
void orb_init_binary (void) {
808
    orb_init_common ();
809
    orb_set_mode (orb_mode_binary);
810
}
811

    
812
/**
813
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
814
 * 
815
 * @see orb_init_binary
816
 **/
817
void orb_init_pwm (void) {
818
    orb_init_common ();
819
    orb_set_mode (orb_mode_pwm);
820
}
821

    
822
/**
823
 * Initializes the orbs in default mode. One of the orb_init* functions must be called before the orbs can be used. Use
824
 * the orb_init_binary or orb_init_pwm function if you want one specific mode.
825
 *
826
 * @see orb_init_pwm
827
 * @see orb_init_binary
828
 **/
829
void orb_init () {
830
    orb_init_pwm ();
831
}