Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (20.4 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
39
for servos in the future.
40

41
author: CMU Robotics Club, Colony Project
42

43
Change Log:
44
3/31/2009 - Martin
45
    Rewritten from scratch (mostly), fixes some code duplication, long ISRs,
46
        bugs, unnecessary synchronized code, memory waste
47
*/
48

    
49
/*
50
Operation:
51
On timer overflow:
52
  - switch on LEDs (where value>0, according to a pre-determined mask)
53
  - load the first output compare value
54
At compare match:
55
  - switch off LEDs (according to mask)
56
  - load the next output compare value
57

58
Ad triple buffering:
59
  - The buffer the ISR is reading from may only be changed at timer overflow,
60
    before the next PWM sequence is started, because otherwise, the next OCR
61
        value may be set to a value smaller than the current timer value, resulting
62
        in the remaining channels not being turned off in that PWM period (flash to
63
        on).
64
  - When using two buffers, the copying (or switching) would have to wait until
65
    the next timer overflow. During this time, neither of the buffers could be
66
        modified because one is used by the ISR and the other may be copied/switched
67
        at any time. Thus, the main thread would possibly be delayed by up to one
68
        full PWM period (8ms in the current implementation, but 20ms-50ms would be a
69
        reasonable value to expect here.
70
  - Triple buffering For Teh Win!
71
 */
72

    
73
/*
74
  TODO:
75
        - Find out the interrupt time
76
        - Optimize the OC interrupt
77
        - test old code: continuously setting the orbs
78
        - fix sync/volatile
79
        - make functions static
80
 */
81

    
82
/*
83
 * Random notes:
84
 *   - Current motor frequency is 32 us/30 KHz
85
 *   - AVR suckage: there is not timer mode with immediate OCR update and 
86
 *     overflow interrupt at TOP (CTC value)
87
 *   - AVR suckage: Set on compare match/Clear on overflow not available with
88
 *     non-PWM modes (especially not with immediate OCR update)
89
 *   - Frequency is 120 Hz (8 ms) next lower (prescaler) is 30 Hz which flickers
90
 *     Not that we could still use the slower prescaler and manually reload
91
 *     after 127. This would still cost resolution, but 128 steps should be
92
 *     enough.
93
 *   - Overflow interrupt 2.5 us (0.03%), compare interrupts are 6*10us (when
94
 *     using all different values) (0.75%) or 1*26 us (when using all same
95
 *     values)
96
 *   - Where to put the time base?
97
 *     - buzzer => doesn't work because of varying frequency
98
 *     - motors => possible? would trigger often (?)
99
 *     - lights => must put lights on 16 bit timer (no OCR left)
100
 *   - Syncronization test case: set orb A to 1,1,1 (not 0 because they will
101
 *     not be turned on) and orb B to 254,254,254. Do this in a loop, with
102
 *     some delay d between.
103
 *      * d=1ms => occasional flickering
104
 *      * d=400us => frequent flickering
105
 *      * d=0 => no usable orb output
106
 *     Without syncronization, both LEDs flicker (because the wrong values are
107
 *     in the channels array while sorting). When the sorting code ist
108
 *     synchronized, only orb A flickers, because the timing is disrupted by the
109
 *     large synchronized block.
110
 */
111
 
112
#include "lights.h"
113

    
114
#include <avr/interrupt.h>
115

    
116
#include "dragonfly_lib.h"
117

    
118

    
119
// ***************
120
// ** Constants **
121
// ***************
122

    
123
#define NUM_ORBS 2   // Number or orbs
124
#define NUM_COLORS 3 // Number of colors per orb
125
#define num_pwm_channels NUM_ORBS*NUM_COLORS
126

    
127

    
128
// *********
129
// ** I/O **
130
// *********
131

    
132
// Orb port
133
#define ORBPORT PORTC
134
#define ORBDDR  DDRC
135

    
136
// Orb pins
137
#define ORB1_RED   0
138
#define ORB1_GREEN 1
139
#define ORB1_BLUE  2
140
#define ORB2_RED   4
141
#define ORB2_GREEN 5
142
#define ORB2_BLUE  6
143

    
144

    
145
// ***********
146
// ** Masks **
147
// ***********
148

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

    
153
// Masks for the individual LEDs
154
#define orb1_red_mask    _BV (ORB1_RED  )
155
#define orb1_green_mask  _BV (ORB1_GREEN)
156
#define orb1_blue_mask   _BV (ORB1_BLUE )
157
#define orb2_red_mask    _BV (ORB2_RED  )
158
#define orb2_green_mask  _BV (ORB2_GREEN)
159
#define orb2_blue_mask   _BV (ORB2_BLUE )
160

    
161
// Mask for all LEDs
162
#define all_orbs_mask \
163
        orb1_red_mask | orb1_green_mask | orb1_blue_mask | \
164
        orb2_red_mask | orb2_green_mask | orb2_blue_mask;
165

    
166
// Mask for the individual LEDs, organized as an array for programmatic access.
167
// The layout of this array is orb_mask[orb_num, color_num]
168
const uint8_t orb_mask[NUM_ORBS][NUM_COLORS]=
169
{
170
        { orb1_red_mask, orb1_green_mask, orb1_blue_mask },
171
        { orb2_red_mask, orb2_green_mask, orb2_blue_mask }
172
};
173

    
174
// ***********
175
// ** Types **
176
// ***********
177

    
178
struct pwm_channel_t // 2 bytes
179
{
180
    uint8_t time;
181
    uint8_t mask;
182
};
183

    
184
struct pwm_t // 13 bytes
185
{
186
        uint8_t init_mask;
187
        struct pwm_channel_t channel[num_pwm_channels];
188
};
189

    
190

    
191
// ***************
192
// ** Variables **
193
// ***************
194

    
195
// Whether to use PWM (true) or binary (false) orb mode
196
bool enable_orb_pwm=true;
197

    
198
// The PWM channels and the buffer pointers. This data structure is triple
199
// buffered, see above for the reasons.
200
struct pwm_t pwm_buffer[3];
201
struct pwm_t *pwm_read_buffer =&pwm_buffer[0]; // The front buffer the ISR reads from. Other thread may not touch this pointer or the buffer it points to.
202
struct pwm_t *pwm_write_buffer=&pwm_buffer[1]; // The back buffer we can write to. The ISR may not touch this pointer or the buffer it points to.
203
struct pwm_t *pwm_free_buffer =&pwm_buffer[2]; // The back buffer to flip with.
204
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
205

    
206

    
207
// The orb value array. Orb values are written here to be sorted into
208
// pwm_channels.
209
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
210

    
211

    
212
// ****************
213
// ** Timer ISRs **
214
// ****************
215

    
216
// Not volatile - only accessed in the interrupt handler
217
uint8_t current_pwm_channel=0;
218

    
219
SIGNAL (SIG_OVERFLOW0)
220
{
221
PORTF|=4;
222

    
223
        if (pwm_page_flip)
224
        {
225
                // Flip the read buffer with the free buffer
226
                // We are in an ISR, so we don't have to synchronize explicitly.
227
                struct pwm_t *temp = pwm_read_buffer;
228
                pwm_read_buffer    = pwm_free_buffer;
229
                pwm_free_buffer    = temp;
230
                pwm_page_flip=false;
231
        }
232

    
233
        // Turn only the appropriate PWM channels on
234
        // TODO: why do we have to turn them off? It should be done in the oc isr.
235
        // Test code: set (0); set (255); delay (100ms); set (0) => blu/green is on
236
        // Does it also happen with 254 instead of 255? But isn't it turned off with
237
        // 255? Better turn them off anyway.
238
        ORBPORT|=all_orbs_mask;
239
        ORBPORT&=pwm_read_buffer->init_mask;
240
        
241
        // Start at the first channel (TODO faster w/ pointers?)
242
        current_pwm_channel=0;
243
        
244
        // Load the first OCR
245
        OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
246
PORTF&=~4;
247
}
248

    
249
SIGNAL(SIG_OUTPUT_COMPARE0)
250
{
251
PORTF|=4;
252
        // TODO:
253
        //   - delayed interrupt
254
        
255
        // If the interrupt is executed w/o delay, TCNT0 == time+1 (and TIME=OCR0)
256

    
257
        // TODO improve (check overflow; maybe use return after last, maybe use
258
        // pointers instead of indicies)
259
        // (but find out interrupt time before to measure improvement)
260
        while (TCNT0==pwm_read_buffer->channel[current_pwm_channel].time+1)
261
        {
262
                // Turn the current channel off
263
                ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
264

    
265
                // Increment the channel
266
                current_pwm_channel++;
267

    
268
                // If there is a next channel, load its OCR value
269
                if (current_pwm_channel<=(num_pwm_channels-1))
270
                        if (pwm_read_buffer->channel[current_pwm_channel].time<255)
271
                                OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
272
        }
273
PORTF&=~4;
274
}
275

    
276

    
277

    
278
// ************************************
279
// ** Internal orb setting functions **
280
// ************************************
281

    
282
static void sort_orbs_buffer (void)
283
{
284
        // This function applies an optimized bubble sort. TODO document, and other
285
        // methods would probably not be faster.
286
                // Considering the low number of data points, more
287
                // sophisticated algorithms are unlikely to be faster,
288
                // especially as this function is fairly optimized.
289
        
290
        // Macro to swap two values of any type. Requires a temp variable of the
291
        // appropriate type. 
292
        #define swap(a,b) { temp=a; a=b; b=temp; }
293

    
294
        // Macro to do one bubble sorting step (compare & swap)
295
        #define bubble \
296
                if(a->time > b->time) \
297
                { \
298
                        swap (a->time, b->time); \
299
                        swap (a->mask, b->mask); \
300
                        done=false; \
301
                }
302
        
303
        // Macro to move to the next bubble sort pair
304
        #define next { a++; b++; }
305

    
306
        // Whether no change was made during the last run, which means that all
307
        // values are in correct order.
308
        bool done;
309
        
310
        // A temporary variable for swapping.
311
        uint8_t temp;
312
        
313
        // Precompute the first PWM channel (tested faster).
314
        struct pwm_channel_t *first=&(pwm_write_buffer->channel[0]);
315

    
316
        // Pointers to the two PWM channels under inspection
317
        struct pwm_channel_t *a, *b;
318

    
319
        // The actual sorting
320
        a=first; b=a+1; done=true;
321
        bubble next bubble next bubble next bubble next bubble
322
        if (done) return;
323

    
324
        a=first; b=a+1; done=true;
325
        bubble next bubble next bubble next bubble
326
        if (done) return;
327

    
328
        a=first; b=a+1; done=true;
329
        bubble next bubble next bubble
330
        if (done) return;
331

    
332
        a=first; b=a+1; done=true;
333
        bubble next bubble
334
        if (done) return;
335

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

    
340
        // Undefine the macros so they do not disturb some other function.
341
        #undef next
342
        #undef bubble
343
        #undef swap
344
}
345

    
346
static void fill_orbs_buffer (void)
347
{
348
        #define copy_value(orb, color) \
349
                index=NUM_COLORS*orb+color; \
350
                time=orb_values[orb][color]; \
351
                mask=orb_mask[orb][color]; \
352
                \
353
                pwm_write_buffer->channel[index].time=time-1; \
354
                pwm_write_buffer->channel[index].mask=mask; \
355
                \
356
                if (time!=0) \
357
                        pwm_write_buffer->init_mask &= ~mask; \
358
        
359
        // TODO try using pointers. Might make it even faster.
360
        uint8_t index, time, mask;
361
        copy_value(0,0); copy_value(0,1); copy_value(0,2);
362
        copy_value(1,0); copy_value(1,1); copy_value(1,2);
363
        
364
        #undef copy_value
365
}
366

    
367
static void apply_orbs (void)
368
{
369
// Time for apply_orbs (enable_orb_pwm block only), with interrupts disabled
370
// (difference to naive bs):
371
//                            Correct order      Reverse order
372
// Naive bubble sort:         147                216
373
// Aborting bubble sort:       70 (-52%)         231 (+7%)
374
// Aborting w/ top:            72 (-51%)         188 (-13%)
375

    
376
// Rolled out with aborting:   60 (-59%)         119 (-45%)
377
// +pointers:                  61 (-59%)          97 (-55%)
378
// Also unrolled copy loop:    34 (-77%)          71 (-67%) (turns out 27us were spent on loop overhead)
379

    
380
// Improvement of rolling out + pointers: -53%/-62%
381

    
382
        if (enable_orb_pwm)
383
        {
384
                // PWM mode
385
                
386
                // Sort the orb values.
387

    
388
PORTF|=2;
389
                pwm_write_buffer->init_mask=~0;
390
        
391
                // 1. Write the orb values and corresponding masks to the pwm channels
392
                // array unsorted.
393
                fill_orbs_buffer ();
394

    
395
                // 2. sort the buffer.
396
                sort_orbs_buffer ();
397

    
398
                // Flip the write buffer with the free buffer.
399
                SYNC
400
                {
401
                        struct pwm_t *temp = pwm_write_buffer;
402
                        pwm_write_buffer   = pwm_free_buffer;
403
                        pwm_free_buffer    = temp;
404
                }
405
                
406
                // On the next overflow, flip the read buffer with the free buffer.
407
                pwm_page_flip=true;
408
                
409
PORTF&=~2;
410
        }
411
        else
412
        {
413
                // Binary mode.
414
                // The outputs are inverted.
415
                uint8_t on=0;
416
                
417
                if (orb_values[0][0]) on |= orb_mask[0][0];
418
                if (orb_values[0][1]) on |= orb_mask[0][1];
419
                if (orb_values[0][2]) on |= orb_mask[0][2];
420
                if (orb_values[1][0]) on |= orb_mask[1][0];
421
                if (orb_values[1][1]) on |= orb_mask[1][1];
422
                if (orb_values[1][2]) on |= orb_mask[1][2];
423
                
424
                ORBPORT |= all_orbs_mask; // All orbs off
425
                ORBPORT &= ~on; // Selected orbs on
426
        }
427
}
428

    
429
static void set_orb_values (uint8_t num, uint8_t red, uint8_t green, uint8_t blue)
430
{
431
        // PWM mode
432
        orb_values[num][0]=red;
433
        orb_values[num][1]=green;
434
        orb_values[num][2]=blue;
435
}
436

    
437

    
438
// ***********************
439
// ** RGB color setting **
440
// ***********************
441

    
442
// All of these functions use set_orb_values to set the actual values, and then call apply_orbs() to apply the changes.
443
// set_orb_values should be used (even though it would be faster to set the array directly) because the binary/pwm mode
444
// has to be handled.
445

    
446
/**
447
 * @param num the number of the orb to set (0 or 1)
448
 */
449
void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue)
450
{
451
        set_orb_values (num, red, green, blue);
452
        apply_orbs ();
453
}
454

    
455
/**
456
 * Set orb1 to the color specified. orb_init must be called before this function
457
 * may be used.
458
 *
459
 * @param red the red component of the color
460
 * @param green the green component of the color
461
 * @param blue the blue component of the color
462
 *
463
 * @see orb_init
464
 **/
465
void orb1_set (uint8_t red, uint8_t green, uint8_t blue)
466
{
467
        set_orb_values (0, red, green, blue);
468
        apply_orbs ();
469
}
470

    
471
/**
472
 * Set orb2 to the color specified. orb_init must be called before this function
473
 * may be used.
474
 *
475
 * @param red_led the red component of the color
476
 * @param green_led the green component of the color
477
 * @param blue_led the blue component of the color
478
 *
479
 * @see orb_init
480
 **/
481
void orb2_set (uint8_t red, uint8_t green, uint8_t blue)
482
{
483
        set_orb_values (1, red, green, blue);
484
        apply_orbs ();
485
}
486

    
487
/**
488
 * Set both orbs to the color specified. orb_init must be called before this
489
 * function may be used.
490
 *
491
 * @param red_led the red component of the color
492
 * @param green_led the green component of the color
493
 * @param blue_led the blue component of the color
494
 *
495
 * @see orb_init, orb1_set, orb2_set
496
 **/
497
void orb_set (uint8_t red, uint8_t green, uint8_t blue)
498
{
499
        set_orb_values (0, red, green, blue);
500
        set_orb_values (1, red, green, blue);
501
        apply_orbs ();
502
}
503

    
504
void orbs_set (
505
        uint8_t red1, uint8_t green1, uint8_t blue1,
506
        uint8_t red2, uint8_t green2, uint8_t blue2)
507
{
508
        set_orb_values (0, red1, green1, blue1);
509
        set_orb_values (1, red2, green2, blue2);
510
        apply_orbs ();
511
}
512

    
513

    
514
// ******************************
515
// ** Predefined color setting **
516
// ******************************
517

    
518
// Macros for extracting a color.
519
#define C_RED(col)   (((col & 0xE0) >> 5) * 36)
520
#define C_GREEN(col) (((col & 0x1C) >> 2) * 36)
521
#define C_BLUE(col)  (((col & 0x03)     ) * 85)
522

    
523
/**
524
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
525
 *
526
 * @param col the color to set the orbs to
527
 **/
528
void orb_set_color(uint8_t col)
529
{
530
        orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
531
}
532

    
533
/**
534
 * Set orb1 to the specified color. This function is intended to be used with the predefined colors.
535
 *
536
 * @param col the color to set the orbs to
537
 **/
538
void orb1_set_color(uint8_t col)
539
{
540
        orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
541
}
542

    
543
/**
544
 * Set orb2 to the specified color. This function is intended to be used with the predefined colors.
545
 *
546
 * @param col the color to set the orbs to
547
 **/
548
void orb2_set_color(uint8_t col)
549
{
550
        orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
551
}
552

    
553
/**
554
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
555
 *
556
 * @param num the number of the orb to set (0 or 1)
557
 * @param col the color to set the orbs to
558
 **/
559
void orb_n_set_color(uint8_t num, uint8_t col)
560
{
561
        orb_n_set(num, C_RED(col), C_GREEN(col), C_BLUE(col));
562
}
563

    
564
/**
565
 * Set the orbs to the respecitve color. This function is intended to be used with the predefined colors.
566
 *
567
 * @param col1 the color to set orb 1 to
568
 * @param col2 the color to set orb 2 to
569
 **/
570
void orbs_set_color(uint8_t col1, uint8_t col2)
571
{
572
        orbs_set (C_RED(col1), C_GREEN(col1), C_BLUE(col1), C_RED(col2), C_GREEN(col2), C_BLUE(col2));
573
}
574

    
575
#undef C_BLUE
576
#undef C_GREEN
577
#undef C_RED2
578

    
579

    
580
// ******************
581
// ** Mode setting **
582
// ******************
583

    
584
void orb_enable_timer (void)
585
{
586
        // Use 8 bit TC0. Timer mode:
587
        //   We cannot use CTC mode because it can only clear on OCR0 (in contrast
588
        //   to the 16 bit timers which can also use the ICR for that) and OCR0 is
589
        //   already used for generating output compare interrupts. We also need
590
        //   immediate (non double buffered) update of OCR0, so the only mode left
591
        //   is "Normal".
592
        //   Note that for a timer counting from 0 to 255, there are 256 states and
593
        //   thus 257 output possibilities (0/256...256/256)! Possible ways to deal
594
        //   with that:
595
        //     1. use a 16 bit variable for the PWM value (memory waste, overhead)
596
        //     2. use an additional flag for the 257th value (inconvenient)
597
        //     3. use 1/256...256/256 (skip 0, never complete off)
598
        //     4. use 0/256...256/256 (skip 256, never complete on)
599
        //     5. skip a value somewhere in the middle
600
        //     6. reload the timer after 254
601
        //   For this implementation, variant 4 was chosen.
602
        // Using and 8 bit timer has the added advantage that all the comparisons
603
        // are faster.
604
        
605

    
606
        // Normal mode, Compare match output off, Prescaler
607
        TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); // 1024, 30 Hz
608
        TCCR0=_BV(CS02) | _BV(CS01); // 1024, 30 Hz
609

    
610
        TIMSK|= _BV(OCIE0) | _BV(TOIE0);
611
}
612

    
613
void orb_disable_timer (void)
614
{
615
        TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
616
}
617

    
618

    
619
void orb_set_mode (orb_mode_t mode)
620
{
621
        if (mode==orb_mode_binary)
622
        {
623
                enable_orb_pwm=false;
624
                orb_disable_timer ();
625
        }
626
        else // orb_mode_pwm
627
        {
628
                enable_orb_pwm=true;
629
                orb_enable_timer ();
630
        }
631

    
632
        apply_orbs ();
633
}
634

    
635

    
636
// ********************
637
// ** Initialization **
638
// ********************
639

    
640
// Orb initialization code common to all modes.
641
static void orb_init_common (void)
642
{
643
        // Enable the output ports and turn off the LEDs
644
        ORBDDR  |=  all_orbs_mask;
645
        ORBPORT |=  all_orbs_mask;
646

    
647
        // Set all orbs to "off"
648
        orb_set (0, 0, 0);
649
        
650
    // Debug
651
    DDRF=6; // TODO remove
652
}
653

    
654
/**
655
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
656
 * 
657
 * @see orb_init_pwm
658
 **/
659
void orb_init_binary (void)
660
{
661
        orb_init_common ();
662
        orb_set_mode (orb_mode_binary);
663
}
664

    
665
/**
666
 * Initializes the orbs in PWM mode. One of the orb_init* functions must be called before the orbs can be used.
667
 * 
668
 * @see orb_init_binary
669
 **/
670
void orb_init_pwm (void)
671
{
672
        orb_init_common ();
673
        orb_set_mode (orb_mode_pwm);
674
}
675

    
676
/**
677
 * A synonym for orb_init_pwm
678
 *
679
 * @see orb_init_pwm
680
 **/
681
void orb_init ()
682
{
683
        orb_init_pwm ();
684
}
685

    
686

    
687

    
688

    
689

    
690

    
691

    
692

    
693
// ***************
694
// ** Debugging **
695
// ***************
696

    
697
void orb_test (void)
698
{
699
        
700
        // The output compare flag (and interrupt) is set at the next timer clock
701
        // cycle after compare match. So time=pwm_value-1 (for pwm_value==0: don't
702
        // switch on at all)
703
        // ORB1: red
704
        // ORB2: green
705

    
706
        // Left: greenish red, Right: greenish blue
707
        // For testing, set some pretty colors
708
        //orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
709
        //orbs_set (255, 127, 0, 0, 127, 255); // Pretty colors with extreme values
710
        //orbs_set (0, 1, 2, 253, 254, 255); // Timing tests
711

    
712
//        orbs_set (255, 255, 255, 0, 0, 0);
713
//        delay_ms (1000);
714

    
715
        //while (1)
716
        //{
717
        //        orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
718
        //        //orbs_set (255, 255, 255, 1, 1, 1);
719
        //        //_delay_us(400);
720
        //}
721

    
722
        // Test the time of the sorting routine
723
//        while (1)
724
//        {
725
//                orbs_set (10, 20, 30, 40, 50, 60);        // Correct order
726
//                //orbs_set (60, 50, 40, 30, 20, 10); // Reverse order
727
//                delay_ms (10);
728
//        }
729
}