Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (19.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
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
    - BUG: the ACL is blue instead of green
76
        - Find out the interrupt time
77
        - Optimize the OC interrupt
78
    - enable_orb_pwm use everywhere, add methods to switch on/off, add init
79
          function
80
        - test old code: continuously setting the orbs
81
        - fix sync/volatile
82
 */
83

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

    
116
#include <avr/interrupt.h>
117

    
118
#include "dragonfly_lib.h"
119

    
120

    
121
// ***************
122
// ** Constants **
123
// ***************
124

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

    
129

    
130
// *********
131
// ** I/O **
132
// *********
133

    
134
// Orb port
135
#define ORBPORT PORTC
136
#define ORBDDR  DDRC
137

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

    
146

    
147
// ***********
148
// ** Masks **
149
// ***********
150

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

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

    
163
// Mask for all LEDs
164
const uint8_t all_orbs_mask=
165
        orb1_red_mask | orb1_green_mask | orb1_blue_mask |
166
        orb2_red_mask | orb2_green_mask | orb2_blue_mask;
167

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

    
176
// ***********
177
// ** Types **
178
// ***********
179

    
180
struct pwm_channel_t
181
{
182
    uint8_t time;
183
    uint8_t mask;
184
};
185

    
186
struct pwm_t
187
{
188
        uint8_t init_mask;
189
        struct pwm_channel_t channel[num_pwm_channels];
190
};
191

    
192

    
193
// ***************
194
// ** Variables **
195
// ***************
196

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

    
200
// The PWM channels and the buffer pointers. This data structure is triple
201
// buffered, see above for the reasons.
202
struct pwm_t pwm_buffer[3];
203
// TODO using pointers might be faster (or might be slower because pointers are
204
// 16 bit long).
205
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.
206
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.
207
struct pwm_t *pwm_free_buffer =&pwm_buffer[2]; // The back buffer to flip with.
208
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
209

    
210

    
211
// The orb value array. Orb values are written here to be sorted into
212
// pwm_channels.
213
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
214

    
215
// TODO: random note: what happens if the main process is sorting and a different
216
// interrupt calls set_orb?
217

    
218

    
219
// ****************
220
// ** Timer ISRs **
221
// ****************
222

    
223
volatile uint8_t current_pwm_channel=0;
224

    
225
SIGNAL (SIG_OVERFLOW0)
226
{
227
PORTF|=4;
228

    
229
        if (pwm_page_flip)
230
        {
231
                // Flip the read buffer with the free buffer
232
                // We are in an ISR, so we don't have to synchronize explicitly.
233
                struct pwm_t *temp = pwm_read_buffer;
234
                pwm_read_buffer    = pwm_free_buffer;
235
                pwm_free_buffer    = temp;
236
                pwm_page_flip=false;
237
        }
238

    
239
        // Turn all appropriate PWM channels on
240
        ORBPORT&=pwm_read_buffer->init_mask;
241
        
242
        // Start at the first channel (TODO faster w/ pointers?)
243
        current_pwm_channel=0;
244
        
245
        // Load the first OCR
246
        OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
247
PORTF&=~4;
248
}
249

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

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

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

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

    
277

    
278

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

    
283
// TODO: make a public version of this one, but keep a private one which does
284
// not sort them so you can update both sides and update only once.
285

    
286
#define SYNC_START uint8_t tmp_sreg; do { tmp_sreg=SREG; cli (); } while (false)
287
#define SYNC_RESTART                 do { tmp_sreg=SREG; cli (); } while (false)
288
#define SYNC_END                     do { SREG=tmp_sreg; } while (false)
289

    
290
static void apply_orbs (void)
291
{
292
        if (enable_orb_pwm)
293
        {
294
                // PWM mode
295
                
296
                // Sort the orb values.
297

    
298
PORTF|=2;
299
                pwm_write_buffer->init_mask=~0;
300
        
301
                // 1. Write the orb values and corresponding masks to the pwm channels
302
                // array unsorted
303
                
304
                for (uint8_t orb=0; orb<2; ++orb)
305
                {
306
                        for (uint8_t color=0; color<3; ++color)
307
                        {
308
                                // TODO this should be faster w/o multiplication
309
                                uint8_t index=NUM_COLORS*orb+color;
310
                                uint8_t time=orb_values[orb][color];
311
                                uint8_t mask=orb_mask[orb][color];
312
                                
313
                                pwm_write_buffer->channel[index].time=time-1;
314
                                pwm_write_buffer->channel[index].mask=mask;
315
                                
316
                                if (time!=0)
317
                                        pwm_write_buffer->init_mask &= ~mask;
318
                        }
319
                }
320

    
321
                // 2. Sort the values. Use bubble sort.
322
                // Considering the low number of data points, more
323
                // sophisticated algorithms are unlikely to be faster.
324
                bool done;
325
                // For 6 channels, we count i=0..4 and compare i with i+1
326
                uint8_t top=num_pwm_channels-1;
327
                do
328
                {
329
                        done=true; // We are done unless we do some swapping
330
                        
331
                        for (uint8_t i=0; i<top; ++i)
332
                        {
333
                                #define channel_a pwm_write_buffer->channel[i]
334
                                #define channel_b pwm_write_buffer->channel[i+1]
335

    
336
                                if (channel_a.time>channel_b.time)
337
                                {
338
                                        uint8_t temp;
339
                                        
340

    
341
                                        // Swap the times
342
                                        temp           = channel_a.time;
343
                                        channel_a.time = channel_b.time;
344
                                        channel_b.time = temp;
345
                                        
346
                                        // Swap the masks
347
                                        temp           = channel_a.mask;
348
                                        channel_a.mask = channel_b.mask;
349
                                        channel_b.mask = temp;
350
                                        
351
                                        done=false; // No, we're not done yet.
352
                                }
353
                                
354
                                #undef channel_a
355
                                #undef channel_b
356
                        }
357
                        
358
                        // On the next iteration, we need one less comparison
359
                        top--;
360
                } while (!done);
361
                
362
                // Flip the write buffer with the free buffer
363
                SYNC
364
                {
365
                        struct pwm_t *temp = pwm_write_buffer;
366
                        pwm_write_buffer   = pwm_free_buffer;
367
                        pwm_free_buffer    = temp;
368
                }
369
                
370
                // On the next overflow, do the page flip.
371
                pwm_page_flip=true;
372
                
373
PORTF&=~2;
374
        }
375
        else
376
        {
377
                // Binary mode.
378
                // Don't do anything, the orbs pins are set in orb_n_set.
379
                // It would be more consistent to set them here (because you could
380
                // update them independently and then apply the changes at once), but it
381
                // is faster this way, and being fast is the whole point of using the
382
                // binary orb mode anyway.
383
        }
384
}
385

    
386
static void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue)
387
{
388
        if (enable_orb_pwm)
389
        {
390
                // PWM mode
391
                orb_values[num][0]=red;
392
                orb_values[num][1]=green;
393
                orb_values[num][2]=blue;
394
        }
395
        else
396
        {
397
                // Binary mode
398
                // The outputs are inverted.
399
                if (!red)   ORBPORT|=orb_mask[num][0]; else ORBPORT&=~orb_mask[num][0];
400
                if (!green) ORBPORT|=orb_mask[num][1]; else ORBPORT&=~orb_mask[num][1];
401
                if (!blue)  ORBPORT|=orb_mask[num][2]; else ORBPORT&=~orb_mask[num][2];
402
        }
403
}
404

    
405
// ************************************
406
// ** Frontend orb setting functions **
407
// ************************************
408

    
409
// All of these functions use orb_n_set to set the actual values, and then call
410
// apply_orbs() to apply the changes. orb_n_set should be used (although it
411
// would be faster to set the array directly) because the binary/pwm mode has
412
// to be handled.
413

    
414
/**
415
 * Set orb1 to the color specified. orb_init must be called before this function
416
 * may be used.
417
 *
418
 * @param red the red component of the color
419
 * @param green the green component of the color
420
 * @param blue the blue component of the color
421
 *
422
 * @see orb_init
423
 **/
424
void orb1_set (uint8_t red, uint8_t green, uint8_t blue)
425
{
426
        orb_n_set (0, red, green, blue);
427
        apply_orbs ();
428
}
429

    
430
/**
431
 * Set orb2 to the color specified. orb_init must be called before this function
432
 * may be used.
433
 *
434
 * @param red_led the red component of the color
435
 * @param green_led the green component of the color
436
 * @param blue_led the blue component of the color
437
 *
438
 * @see orb_init
439
 **/
440
void orb2_set (uint8_t red, uint8_t green, uint8_t blue)
441
{
442
        orb_n_set (1, red, green, blue);
443
        apply_orbs ();
444
}
445

    
446
/**
447
 * Set both orbs to the color specified. orb_init must be called before this
448
 * function may be used.
449
 *
450
 * @param red_led the red component of the color
451
 * @param green_led the green component of the color
452
 * @param blue_led the blue component of the color
453
 *
454
 * @see orb_init, orb1_set, orb2_set
455
 **/
456
void orb_set (uint8_t red, uint8_t green, uint8_t blue)
457
{
458
        orb_n_set (0, red, green, blue);
459
        orb_n_set (1, red, green, blue);
460
        apply_orbs ();
461
}
462

    
463
void orbs_set (
464
        uint8_t red1, uint8_t green1, uint8_t blue1,
465
        uint8_t red2, uint8_t green2, uint8_t blue2)
466
{
467
        orb_n_set (0, red1, green1, blue1);
468
        orb_n_set (1, red2, green2, blue2);
469
        apply_orbs ();
470
}
471

    
472

    
473

    
474

    
475
/**
476
 * Set both orbs to the specified color. This function
477
 * is intended to be used with the predefined
478
 * colors. orb_init must be called before this
479
 * function may be used.
480
 *
481
 * @param col the color to set the orbs to
482
 *
483
 * @see orb_init
484
 **/
485
void orb_set_color(uint8_t col)
486
{
487
// uint16_t red, green, blue;
488
//
489
// red = ((col & 0xE0) >> 5) * 36;
490
// green = ((col & 0x1C) >> 2) * 36;
491
// blue = (col & 0x03) * 85;
492
//
493
// orb_set(red, green, blue);
494
}
495

    
496
/**
497
 * Set orb1 to the specified color. This function
498
 * is intended to be used with the predefined
499
 * colors. orb_init must be called before this
500
 * function may be used.
501
 *
502
 * @param col the color to set the orbs to
503
 *
504
 * @see orb_init
505
 **/
506
void orb1_set_color(uint8_t  col)
507
{
508
// uint16_t red, green, blue;
509
//
510
// red = ((col & 0xE0) >> 5) * 36;
511
// green = ((col & 0x1C) >> 2) * 36;
512
// blue = (col & 0x03) * 85;
513
//
514
// orb1_set(red, green, blue);
515
}
516

    
517
/**
518
 * Set orb2 to the specified color. This function
519
 * is intended to be used with the predefined
520
 * colors. orb_init must be called before this
521
 * function may be used.
522
 *
523
 * @param col the color to set the orbs to
524
 *
525
 * @see orb_init
526
 **/
527
void orb2_set_color(uint8_t  col)
528
{
529
// uint16_t red, green, blue;
530
//
531
// red = ((col & 0xE0) >> 5) * 36;
532
// green = ((col & 0x1C) >> 2) * 36;
533
// blue = (col & 0x03) * 85;
534
//
535
// orb2_set(red, green, blue);
536
}
537

    
538
//DOES THIS WORK?
539
// Disables the timer1 interrupt, disabling the Orb's color fading capabilities
540
// You can still turn the red, green, and blue leds on and off with set_orb_dio
541
/* If we use the PWM for anything else besides the ORB, this implementation needs to be done better */
542
/**
543
 * Disables the orb color fading capabilities
544
 * by disabling the timer1 interrupt.
545
 *
546
 * @see orb_init
547
 **/
548
void orb_disable()
549
{
550
//        TCCR3B &= 0;          //Turn off everything
551
//        ORB_PORT |= _BV(ORB1_RED);
552
//        ORB_PORT |= _BV(ORB1_GREEN);
553
//        ORB_PORT |= _BV(ORB1_BLUE);
554
//        ORB_PORT |= _BV(ORB2_RED);
555
//        ORB_PORT |= _BV(ORB2_GREEN);
556
//        ORB_PORT |= _BV(ORB2_BLUE);
557
}
558

    
559
//DOES THIS WORK?
560
// Enables the timer1 interrupt, enabling the Orb's color fading capabilities
561
/**
562
 * Enables the orb's color fading capabilities.
563
 *
564
 * @see orb_init
565
 **/
566
void orb_enable()
567
{
568
////        TCCR0 |= _BV(COM01) | _BV(COM00)  | _BV(WGM00) | _BV(CS01);        //Toggle OC Pin on match, FAST PWM Mode, clock/8
569
//        TCCR3B =_BV(CS31);
570
}
571

    
572
/** @} **/ //end group
573

    
574

    
575
// ********************
576
// ** Initialization **
577
// ********************
578

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

    
608
        // Set all orbs to "off"
609
        orb_set (0, 0, 0);
610

    
611
        // *** Set up the timer
612

    
613
        // Normal mode, Compare match output off, Prescaler
614
        TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); // 1024, 30 Hz
615
        TCCR0=_BV(CS02) | _BV(CS01); // 1024, 30 Hz
616

    
617
        // Enable compare match and overflow interrupts
618
        TIMSK=_BV(OCIE0) | _BV(TOIE0);
619

    
620
    // Debug
621
    DDRF=6;
622

    
623
        
624
        // The output compare flag (and interrupt) is set at the next timer clock
625
        // cycle after compare match. So time=pwm_value-1 (for pwm_value==0: don't
626
        // switch on at all)
627
        // ORB1: red
628
        // ORB2: green
629

    
630
        // Left: greenish red, Right: greenish blue
631
        // For testing, set some pretty colors
632
        //orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
633
        //orbs_set (255, 127, 0, 0, 127, 255); // Pretty colors with extreme values
634
        //orbs_set (0, 1, 2, 253, 254, 255); // Timing tests
635

    
636
//        orbs_set (255, 255, 255, 0, 0, 0);
637
//        delay_ms (1000);
638

    
639
        //while (1)
640
        //{
641
        //        orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
642
        //        //orbs_set (255, 255, 255, 1, 1, 1);
643
        //        //_delay_us(400);
644
        //}
645

    
646
        // Test the time of the sorting routine
647
//        while (1)
648
//        {
649
//                orbs_set (10, 20, 30, 40, 50, 60);        // Correct order
650
//                //orbs_set (60, 50, 40, 30, 20, 10); // Reverse order
651
//                delay_ms (10);
652
//        }
653
}
654

    
655
// Pure sorting time/us (interrupts disabled!) (difference to naive bs):
656
//                        Correct order    Reverse order
657
// Naive bubble sort:     147              216
658
// Aborting bubble sort:   70 (-52%)       231 (+7%)
659
// Aborting w/ top:        72 (-51%)       188 (-13%)