Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (17.6 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
        - test the timing
76
        - Determine sorting time, possibly improve sorting algorithm (111 us sorted,
77
          143 us reverse sorted)
78
        - stop sorting when finished (no change made) (and measure performance gain)
79
    - enable_orb_pwm use everywhere, add methods to switch on/off, add init
80
          function
81
        - test old code: continuously setting the orbs
82
        - n-buffering
83
        - fix sync/volatile
84
 */
85

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

    
118
#include <avr/interrupt.h>
119

    
120
#include "dragonfly_lib.h"
121

    
122

    
123
// ***************
124
// ** Constants **
125
// ***************
126

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

    
131

    
132
// *********
133
// ** I/O **
134
// *********
135

    
136
// Orb port
137
#define ORBPORT PORTC
138
#define ORBDDR  DDRC
139

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

    
148

    
149
// ***********
150
// ** Masks **
151
// ***********
152

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

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

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

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

    
178
// ***********
179
// ** Types **
180
// ***********
181

    
182
struct pwm_channel_t
183
{
184
    uint8_t time;
185
    uint8_t mask;
186
};
187

    
188
sutrct pwm_t
189
{
190
        uint8_t init_mask;
191
        pwm_channels_t channel[num_pwm_channels];
192
}
193

    
194

    
195
// ***************
196
// ** Variables **
197
// ***************
198

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

    
202
// The PWM channels and the buffer pointers. This data structure is triple
203
// buffered, see above for the reasons.
204
struct pwm_t pwm_buffer[3];
205
// TODO using pointers might be faster (or might be slower because pointers are
206
// 16 bit long).
207
uint8_t read_buffer=0; // The buffer the ISR reads from
208
uint8_t next_buffer=0; // The buffer the ISR will switch to on the next overflow
209
uint8_t 
210
uint8_t inactive_buffer=2;
211

    
212

    
213

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

    
218
// TODO: random note: what happens if the main process is sorting and a different
219
// interrupt calls set_orb?
220

    
221

    
222
// ****************
223
// ** Timer ISRs **
224
// ****************
225

    
226
volatile uint8_t current_pwm_channel=0;
227

    
228
SIGNAL (SIG_OVERFLOW0)
229
{
230
PORTF|=4;
231
        // Turn all appropriate PWM channels on
232
        ORBPORT&=pwm_init_mask;
233
        
234
        // Start at the first channel
235
        current_pwm_channel=0;
236
        
237
        // Load the first OCR
238
        OCR0=pwm_channels[current_pwm_channel].time;
239
PORTF&=~4;
240
}
241

    
242
SIGNAL(SIG_OUTPUT_COMPARE0)
243
{
244
PORTF|=4;
245
        // TODO:
246
        //   - delayed interrupt
247
        //   - synchronization OK?
248
        
249
        // If the interrupt is executed w/o delay, TCNT0 == time+1 (and TIME=OCR0)
250

    
251
        // TODO improve (check overflow; maybe use return after last, maybe use
252
        // pointers instead of indicies)
253
        while (TCNT0==pwm_channels[current_pwm_channel].time+1)
254
        {
255
                // Turn the current channel off
256
                ORBPORT|=pwm_channels[current_pwm_channel].mask;
257

    
258
                // Increment the channel
259
                current_pwm_channel++;
260

    
261
                // If there is a next channel, load its OCR value
262
                if (current_pwm_channel<=(num_pwm_channels-1))
263
                        if (pwm_channels[current_pwm_channel].time<255)
264
                                OCR0=pwm_channels[current_pwm_channel].time;
265
        }
266
PORTF&=~4;
267
}
268

    
269

    
270

    
271
// ************************************
272
// ** Internal orb setting functions **
273
// ************************************
274

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

    
278
#define SYNC_START uint8_t tmp_sreg; do { tmp_sreg=SREG; cli (); } while (false)
279
#define SYNC_RESTART                 do { tmp_sreg=SREG; cli (); } while (false)
280
#define SYNC_END                     do { SREG=tmp_sreg; } while (false)
281

    
282
static void apply_orbs (void)
283
{
284
        if (enable_orb_pwm)
285
        {
286
                // PWM mode
287
                
288
                // Sort the orb values.
289

    
290
PORTF|=2;
291
SYNC
292
{
293
                pwm_init_mask=~0;
294
        
295
                // 1. Write the orb values and corresponding masks to the pwm channels
296
                // array unsorted
297
                for (uint8_t orb=0; orb<2; ++orb)
298
                {
299
                        for (uint8_t color=0; color<3; ++color)
300
                        {
301
                                // TODO this should be faster w/o multiplication
302
                                uint8_t index=NUM_COLORS*orb+color;
303
                                uint8_t time=orb_values[orb][color];
304
                                uint8_t mask=orb_mask[orb][color];
305
                                
306
                                pwm_channels[index].time=time-1;
307
                                pwm_channels[index].mask=mask;
308
                                
309
                                if (time!=0)
310
                                        pwm_init_mask &= ~mask;
311
                        }
312
                }
313

    
314
                // 2. Sort the values. Use bubble sort for now.
315
                for (uint8_t count=num_pwm_channels-1; count>0; --count)
316
                {
317
                        for (uint8_t i=num_pwm_channels-1; i>0; --i)
318
                        {
319
                                if (pwm_channels[i].time<pwm_channels[i-1].time)
320
                                {
321
                                        uint8_t temp;
322
                                        
323
                                        // Swap the times
324
                                        temp=pwm_channels[i].time;
325
                                        pwm_channels[i].time=pwm_channels[i-1].time;
326
                                        pwm_channels[i-1].time=temp;
327
                                        
328
                                        // Swap the masks
329
                                        temp=pwm_channels[i].mask;
330
                                        pwm_channels[i].mask=pwm_channels[i-1].mask;
331
                                        pwm_channels[i-1].mask=temp;
332
                                }
333
                        }
334
                }
335
}
336
//SYNC_END;
337
PORTF&=~2;
338

    
339
        }
340
        else
341
        {
342
                // Binary mode.
343
                // Don't do anything, the orbs pins are set in orb_n_set.
344
                // It would be more consistent to set them here (because you could
345
                // update them independently and then apply the changes at once), but it
346
                // is faster this way, and being fast is the whole point of using the
347
                // binary orb mode anyway.
348
        }
349
}
350

    
351
static void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue)
352
{
353
        if (enable_orb_pwm)
354
        {
355
                // PWM mode
356
                orb_values[num][0]=red;
357
                orb_values[num][1]=green;
358
                orb_values[num][2]=blue;
359
        }
360
        else
361
        {
362
                // Binary mode
363
                // The outputs are inverted.
364
                if (!red)   ORBPORT|=orb_mask[num][0]; else ORBPORT&=~orb_mask[num][0];
365
                if (!green) ORBPORT|=orb_mask[num][1]; else ORBPORT&=~orb_mask[num][1];
366
                if (!blue)  ORBPORT|=orb_mask[num][2]; else ORBPORT&=~orb_mask[num][2];
367
        }
368
}
369

    
370
// ************************************
371
// ** Frontend orb setting functions **
372
// ************************************
373

    
374
// All of these functions use orb_n_set to set the actual values, and then call
375
// apply_orbs() to apply the changes. orb_n_set should be used (although it
376
// would be faster to set the array directly) because the binary/pwm mode has
377
// to be handled.
378

    
379
/**
380
 * Set orb1 to the color specified. orb_init must be called before this function
381
 * may be used.
382
 *
383
 * @param red the red component of the color
384
 * @param green the green component of the color
385
 * @param blue the blue component of the color
386
 *
387
 * @see orb_init
388
 **/
389
void orb1_set (uint8_t red, uint8_t green, uint8_t blue)
390
{
391
        orb_n_set (0, red, green, blue);
392
        apply_orbs ();
393
}
394

    
395
/**
396
 * Set orb2 to the color specified. orb_init must be called before this function
397
 * may be used.
398
 *
399
 * @param red_led the red component of the color
400
 * @param green_led the green component of the color
401
 * @param blue_led the blue component of the color
402
 *
403
 * @see orb_init
404
 **/
405
void orb2_set (uint8_t red, uint8_t green, uint8_t blue)
406
{
407
        orb_n_set (1, red, green, blue);
408
        apply_orbs ();
409
}
410

    
411
/**
412
 * Set both orbs to the color specified. orb_init must be called before this
413
 * function may be used.
414
 *
415
 * @param red_led the red component of the color
416
 * @param green_led the green component of the color
417
 * @param blue_led the blue component of the color
418
 *
419
 * @see orb_init, orb1_set, orb2_set
420
 **/
421
void orb_set (uint8_t red, uint8_t green, uint8_t blue)
422
{
423
        orb_n_set (0, red, green, blue);
424
        orb_n_set (1, red, green, blue);
425
        apply_orbs ();
426
}
427

    
428
void orbs_set (
429
        uint8_t red1, uint8_t green1, uint8_t blue1,
430
        uint8_t red2, uint8_t green2, uint8_t blue2)
431
{
432
        orb_n_set (0, red1, green1, blue1);
433
        orb_n_set (1, red2, green2, blue2);
434
        apply_orbs ();
435
}
436

    
437

    
438

    
439

    
440
/**
441
 * Set both orbs to the specified color. This function
442
 * is intended to be used with the predefined
443
 * colors. orb_init must be called before this
444
 * function may be used.
445
 *
446
 * @param col the color to set the orbs to
447
 *
448
 * @see orb_init
449
 **/
450
void orb_set_color(uint8_t col)
451
{
452
// uint16_t red, green, blue;
453
//
454
// red = ((col & 0xE0) >> 5) * 36;
455
// green = ((col & 0x1C) >> 2) * 36;
456
// blue = (col & 0x03) * 85;
457
//
458
// orb_set(red, green, blue);
459
}
460

    
461
/**
462
 * Set orb1 to the specified color. This function
463
 * is intended to be used with the predefined
464
 * colors. orb_init must be called before this
465
 * function may be used.
466
 *
467
 * @param col the color to set the orbs to
468
 *
469
 * @see orb_init
470
 **/
471
void orb1_set_color(uint8_t  col)
472
{
473
// uint16_t red, green, blue;
474
//
475
// red = ((col & 0xE0) >> 5) * 36;
476
// green = ((col & 0x1C) >> 2) * 36;
477
// blue = (col & 0x03) * 85;
478
//
479
// orb1_set(red, green, blue);
480
}
481

    
482
/**
483
 * Set orb2 to the specified color. This function
484
 * is intended to be used with the predefined
485
 * colors. orb_init must be called before this
486
 * function may be used.
487
 *
488
 * @param col the color to set the orbs to
489
 *
490
 * @see orb_init
491
 **/
492
void orb2_set_color(uint8_t  col)
493
{
494
// uint16_t red, green, blue;
495
//
496
// red = ((col & 0xE0) >> 5) * 36;
497
// green = ((col & 0x1C) >> 2) * 36;
498
// blue = (col & 0x03) * 85;
499
//
500
// orb2_set(red, green, blue);
501
}
502

    
503
//DOES THIS WORK?
504
// Disables the timer1 interrupt, disabling the Orb's color fading capabilities
505
// You can still turn the red, green, and blue leds on and off with set_orb_dio
506
/* If we use the PWM for anything else besides the ORB, this implementation needs to be done better */
507
/**
508
 * Disables the orb color fading capabilities
509
 * by disabling the timer1 interrupt.
510
 *
511
 * @see orb_init
512
 **/
513
void orb_disable()
514
{
515
//        TCCR3B &= 0;          //Turn off everything
516
//        ORB_PORT |= _BV(ORB1_RED);
517
//        ORB_PORT |= _BV(ORB1_GREEN);
518
//        ORB_PORT |= _BV(ORB1_BLUE);
519
//        ORB_PORT |= _BV(ORB2_RED);
520
//        ORB_PORT |= _BV(ORB2_GREEN);
521
//        ORB_PORT |= _BV(ORB2_BLUE);
522
}
523

    
524
//DOES THIS WORK?
525
// Enables the timer1 interrupt, enabling the Orb's color fading capabilities
526
/**
527
 * Enables the orb's color fading capabilities.
528
 *
529
 * @see orb_init
530
 **/
531
void orb_enable()
532
{
533
////        TCCR0 |= _BV(COM01) | _BV(COM00)  | _BV(WGM00) | _BV(CS01);        //Toggle OC Pin on match, FAST PWM Mode, clock/8
534
//        TCCR3B =_BV(CS31);
535
}
536

    
537
/** @} **/ //end group
538

    
539

    
540
// ********************
541
// ** Initialization **
542
// ********************
543

    
544
/**
545
 * Initializes the PWM for Orb control. This must be called before 
546
 * the orbs are used for them to function.
547
 **/
548
void orb_init ()
549
{
550
        // Use 8 bit TC0. Timer mode:
551
        //   We cannot use CTC mode because it can only clear on OCR0 (in contrast
552
        //   to the 16 bit timers which can also use the ICR for that) and OCR0 is
553
        //   already used for generating output compare interrupts. We also need
554
        //   immediate (non double buffered) update of OCR0, so the only mode left
555
        //   is "Normal".
556
        //   Note that for a timer counting from 0 to 255, there are 256 states and
557
        //   thus 257 output possibilities (0/256...256/256)! Possible ways to deal
558
        //   with that:
559
        //     1. use a 16 bit variable for the PWM value (memory waste, overhead)
560
        //     2. use an additional flag for the 257th value (inconvenient)
561
        //     3. use 1/256...256/256 (skip 0, never complete off)
562
        //     4. use 0/256...256/256 (skip 256, never complete on)
563
        //     5. skip a value somewhere in the middle
564
        //     6. reload the timer after 254
565
        //   For this implementation, variant 4 was chosen.
566
        // Using and 8 bit timer has the added advantage that all the comparisons
567
        // are faster.
568
        
569
        // Enable the output ports and turn off the LEDs
570
        ORBDDR  |=  all_orbs_mask;
571
        ORBPORT |=  all_orbs_mask;
572

    
573
        // Set all orbs to "off"
574
        orb_set (0, 0, 0);
575

    
576
        // *** Set up the timer
577

    
578
        // Normal mode, Compare match output off, Prescaler
579
        TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); // 1024, 30 Hz
580
        TCCR0=_BV(CS02) | _BV(CS01); // 1024, 30 Hz
581

    
582
        // Enable compare match and overflow interrupts
583
        TIMSK=_BV(OCIE0) | _BV(TOIE0);
584

    
585
    // Debug
586
    DDRF=6;
587

    
588
        
589
        // The output compare flag (and interrupt) is set at the next timer clock
590
        // cycle after compare match. So time=pwm_value-1 (for pwm_value==0: don't
591
        // switch on at all)
592
        // ORB1: red
593
        // ORB2: green
594

    
595
        // Left: greenish red, Right: greenish blue
596
        // For testing, set some pretty colors
597
        //orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
598
        //orbs_set (255, 127, 0, 0, 127, 255); // Pretty colors with extreme values
599
        //orbs_set (0, 1, 2, 253, 254, 255); // Timing tests
600

    
601
        orbs_set (255, 255, 255, 0, 0, 0);
602
        delay_ms (1000);
603

    
604
        while (1)
605
        {
606
                //orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
607
        orbs_set (255, 255, 255, 1, 1, 1);
608
                _delay_us(400);
609
        }
610

    
611
        // Test the time of the sorting routine
612
        //while (1)
613
        //{
614
        //        orbs_set (10, 20, 30, 40, 50, 60);        // Correct order
615
        //        //orbs_set (60, 50, 40, 30, 20, 10); // Reverse order
616
        //        delay_ms (10);
617
        //}
618
}
619

    
620