Project

General

Profile

Revision 1142

Merged new orbs code
Added atomic.h
Fixed whitespace in eeprom.h

View differences:

lights.c
23 23
 * OTHER DEALINGS IN THE SOFTWARE.
24 24
 **/
25 25

  
26

  
27 26
/**
28 27
 * @file ligths.c
29 28
 * @brief Orbs
......
31 30
 * Implemenation for the orbs (tri-colored LEDs)
32 31
 *
33 32
 * @author Colony Project, CMU Robotics Club
34
 * @bug Colors are incorrect, seems to not work with wireless library
33
 * @bug Unfinished
35 34
 **/
36 35

  
37 36
/*
38 37
lights.c
39
Controls orb1 and orb2. Also contains the framework for a software PWM that may be used for servos in the future.
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 40

  
41 41
author: CMU Robotics Club, Colony Project
42 42

  
43 43
Change Log:
44
2.4.07 - Aaron
45
	Revamped orb code so it works.  Need to check interaction with rtc, and tweak some colors.
44
3/31/2009 - Martin
45
    Rewritten from scratch. Fixes code duplication, long ISRs, bugs, unnecessary synchronized code, memory waste
46
*/
46 47

  
47
2.1.07 - James
48
	Modified sort_buffer() to prune for repeats.  PWM now uses orb_buf_size for the number of orb values in orb_time_arr[].
49
		Changed sorting algorithm used in sort_buffer() to selection sort (faster). And it works now.
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
 **/
50 110

  
51
1.25.07 - KWoo
52
	Deleted old FF+ code to make it cleaner. Commented code. This all works. Note however that if you ever plan to use the
53
		software PWM (which is this) you will need to change the implementation of orb_enable() and orb_disable() to not
54
		shutdown the PWM.
111
/*
55 112

  
113
All different:
114

  
115
Overflow: 8us
116

  
117
OC: 5*10+1*6
118

  
119
All same:
120
OC: 30us
121

  
56 122
*/
57 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

  
58 150
#include "lights.h"
59
#include "dragonfly_lib.h"
151

  
60 152
#include <avr/interrupt.h>
61 153

  
62
#define ORB_RESET 1025
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
63 171
#define ORBPORT PORTC
64
#define ORBDDR DDRC
65
#define ORBMASK 0x77
172
#define ORBDDR  DDRC
66 173

  
67
/***** Port and Pin Definitions ****/
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
68 181

  
69
//Orb Ports and Registers
70
#define ORB_PORT        PORTC
71
#define ORB_DDR         DDRC
72 182

  
73
//Orb Pins
74
#define ORB1_RED        0x00
75
#define ORB1_GREEN      0x01
76
#define ORB1_BLUE       0x02
77
#define ORB2_RED        0x04
78
#define ORB2_GREEN      0x05
79
#define ORB2_BLUE       0x06
183
// ***************
184
// ** Debugging **
185
// ***************
80 186

  
187
//#define LIGHTS_DEBUG
188
#undef LIGHTS_DEBUG
81 189

  
82
#define ORB_COUNT 8	//please dont change this, or bad things might happen
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;
83 197

  
84
// an orb node
85
struct ORB_NODE {
86
   uint8_t num;
87
   uint16_t angle;
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 }
88 224
};
89 225

  
90
//the change in an orb
91
struct ORB_CHANGE {
92
   uint16_t port_val;
93
   uint16_t split_time_period;
226
// ***********
227
// ** Types **
228
// ***********
229

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

  
96
// the status of an orb
97
struct ORB_STATUS_STRUCT {
98
   struct ORB_NODE orbs[ORB_COUNT];
99
   uint16_t orb_angles[ORB_COUNT];
100
   struct ORB_CHANGE changes[ORB_COUNT+1];
101
   uint8_t change_count;
102
   uint8_t new_angles;
103
   uint8_t current_orb;
235
struct pwm_t { // 13 bytes
236
    uint8_t init_mask;
237
    struct pwm_channel_t channel[num_pwm_channels];
238
};
104 239

  
105
} ORB_STATUS;
106 240

  
107
void orb_sort(void);
108
void orb_setup_pulse(void);
241
// ***************
242
// ** Variables **
243
// ***************
109 244

  
110
SIGNAL (SIG_OUTPUT_COMPARE3C){
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;
111 247

  
112
		//pull the correct ones down
113
      ORBPORT &= (~ORBMASK)|(ORB_STATUS.changes[ORB_STATUS.current_orb].port_val);
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];
114 251

  
115
      ++ORB_STATUS.current_orb; //now look at next orb transition
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];
116 255

  
117
      if (ORB_STATUS.current_orb < ORB_STATUS.change_count) { //if it isnt the end...
118
	  
119
			//setup timer for next pull down
120
         OCR3C = TCNT3+ORB_STATUS.changes[ORB_STATUS.current_orb].split_time_period;
121
		 
122
     }
123
      else { //we are done with these pulses
124
		orb_setup_pulse();
125
      }
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]; 
126 259

  
127
}
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]; 
128 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
129 266

  
130
//sets a channel to a value
131
void orb_set_angle(int orb, int angle) {
132
	uint8_t mysreg;
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.
133 282
	
134
	orb=orb&0x07; //only have 8
135
	angle=angle&0xff; //only accept 0-255
136
	angle=255-angle; //inverse intensity
137
	angle=angle<<2; //scale up so that we dont run it too often
138
	angle+=3; //0 values dont really work
139
   if (ORB_STATUS.orb_angles[orb] != angle) { //if the angle has changed
140
	  mysreg=SREG; 
141
	  cli(); //disable interrupts
142
      ORB_STATUS.orb_angles[orb] = angle; //update angle
143
      ORB_STATUS.new_angles = 1;
144
	  SREG=mysreg; //put interrupt status back
145
   }
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
    }
146 315
}
147 316

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

  
149
void orb_sort(void) {
150
   int done = 0, i;
151
   
152
   while (! done) {
153
      done = 1;
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
    }
154 330

  
155
      for (i = 0; i < ORB_COUNT - 1; ++i) {  //loop through all
156
	  
157
			//if they are out of order, swap them
158
         if (ORB_STATUS.orbs[i].angle > ORB_STATUS.orbs[i+1].angle) {
159
            ORB_STATUS.orbs[i].angle ^= ORB_STATUS.orbs[i+1].angle;
160
            ORB_STATUS.orbs[i+1].angle ^= ORB_STATUS.orbs[i].angle;
161
            ORB_STATUS.orbs[i].angle ^= ORB_STATUS.orbs[i+1].angle;
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;
162 341

  
163
            ORB_STATUS.orbs[i].num ^= ORB_STATUS.orbs[i+1].num;
164
            ORB_STATUS.orbs[i+1].num ^= ORB_STATUS.orbs[i].num;
165
            ORB_STATUS.orbs[i].num ^= ORB_STATUS.orbs[i+1].num;
342
	// If this interrupt was delayed, we might already have an output compare condition.
343
	output_compare ();
166 344

  
167
            done = 0;
168
         }
169
      }
170
   }
345
#ifdef LIGHTS_DEBUG
346
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
347
#endif
171 348
}
172 349

  
173
//calculate the split times
174
void orb_setup_pulse(void) {
175
   int i;
176
   uint16_t my_port;
177
   uint16_t sum = 0;
178
   uint16_t split_time;
350
SIGNAL(SIG_OUTPUT_COMPARE0) {
351
#ifdef LIGHTS_DEBUG
352
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
353
#endif
179 354

  
180
   my_port = 0xff; //all on
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
}
181 362

  
182
   if (ORB_STATUS.new_angles) {
183 363

  
184
      ORB_STATUS.change_count = 0;
185
	  for (i = 0; i < ORB_COUNT; ++i) { //get the new values
186
         ORB_STATUS.orbs[i].angle = ORB_STATUS.orb_angles[ORB_STATUS.orbs[i].num];
187
      }
188 364

  
189
      orb_sort(); //sort them
190
      ORB_STATUS.new_angles = 0;
365
// ************************************
366
// ** Internal orb setting functions **
367
// ************************************
191 368

  
192
      for (i = 0; i < ORB_COUNT; ++i) { //calculate split times
193
         split_time = ORB_STATUS.orbs[i].angle - sum;
194
         my_port &= ~_BV(ORB_STATUS.orbs[i].num);
195
		 
196
         for (; i < ORB_COUNT - 1 && ORB_STATUS.orbs[i].angle == ORB_STATUS.orbs[i+1].angle; ++i) {
197
            my_port &= ~_BV(ORB_STATUS.orbs[i+1].num); //look for doups
198
         }
199
		 
200
         ORB_STATUS.changes[ORB_STATUS.change_count].port_val = my_port; //which pins are low
201
         ORB_STATUS.changes[ORB_STATUS.change_count].split_time_period = split_time;
202
         
203
		 ++ORB_STATUS.change_count;
204
		 
205
         sum += split_time;
206
      }
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; }
207 386

  
208
      ORB_STATUS.changes[ORB_STATUS.change_count].port_val = my_port;
209
      ORB_STATUS.changes[ORB_STATUS.change_count].split_time_period = ORB_RESET - sum; //get a constant period
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++; }
210 398

  
211
      ++ORB_STATUS.change_count;
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]);
212 407

  
213
   }
408
    // Pointers to the two PWM channels under inspection
409
    struct pwm_channel_t *a, *b;
214 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;
215 415

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

  
217
   ORB_STATUS.current_orb = 0;
420
    a=first; b=a+1; done=true;
421
    bubble next bubble next bubble
422
    if (done) return;
218 423

  
219
    ORBPORT |= ORBMASK; //start with all high
220
	OCR3C = TCNT3 + ORB_STATUS.changes[0].split_time_period; //wait for first split
424
    a=first; b=a+1; done=true;
425
    bubble next bubble
426
    if (done) return;
221 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
222 436
}
223 437

  
224
/**
225
 * @defgroup orbs Orbs
226
 * @brief Functions for controlling the color of the orbs.
227
 * 
228
 * Functions for controlling the color and lighting of the orbs.
229
 *
230
 * @{
231
 **/
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; \
232 452

  
233
/**
234
 * Initializes the PWM for Orb control. This must be called before 
235
 * the orbs are used for them to function.
236
 **/
237
void orb_init() 
238
{	
239
   int i;
240
   uint8_t mysreg;
241
   
242
   ORBDDR |= ORBMASK;	//all outputs
243
   
244
	mysreg=SREG;
245
	cli(); //turn off interrupts for now
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
}
246 459

  
247
	//init everything
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
	 */
248 475

  
249
   for (i = 0; i < ORB_COUNT; ++i) {
250
      ORB_STATUS.orbs[i].num = i;
251
      ORB_STATUS.orbs[i].angle = 1023;	//127 is a pretty stupid start angle, but oh well
252
      ORB_STATUS.orb_angles[i] = 1023;
253
   }
476
#ifdef LIGHTS_DEBUG
477
    LIGHTS_DEBUG_APPLY_START
478
#endif
254 479

  
255
   ORB_STATUS.new_angles = 1;
256
   ORB_STATUS.change_count = 0;
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 ();
257 488

  
258
	//init timer3
259
	TCCR3A = 0; 
260
	TCCR3B = _BV(CS31); //prescale = 8
261
	TCCR3C = 0;
262
	ETIMSK |= _BV(OCIE3C); //turn on oc3c interrupt
263
	OCR3C = TCNT3+ORB_RESET;
489
        // 2. sort the buffer.
490
        sort_orbs_buffer ();
264 491

  
265
	SREG=mysreg;
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
266 526
}
267 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

  
268 549
/**
269
 * Set both orbs to the color specified. orb_init must
270
 * be called before this function may be used.
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.
271 553
 *
272
 * @param red_led the red component of the color
273
 * @param green_led the green component of the color
274
 * @param blue_led the blue component of the color
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.
275 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
 *
276 578
 * @see orb_init
277 579
 **/
278
void orb_set(unsigned char red_led, unsigned char green_led, unsigned char blue_led) {
279
	orb1_set(red_led,green_led,blue_led);
280
	orb2_set(red_led,green_led,blue_led);
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 ();
281 585

  
586
	RELEASE_LOCK(orb_lock);
282 587
}
283 588

  
284 589
/**
285
 * Set orb1 to the color specified. orb_init must
286
 * be called before this function may be used.
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.
287 593
 *
288 594
 * @param red_led the red component of the color
289 595
 * @param green_led the green component of the color
......
291 597
 *
292 598
 * @see orb_init
293 599
 **/
294
void orb1_set(unsigned char red_led, unsigned char green_led, unsigned char blue_led) {
295
	orb_set_angle(0,red_led);
296
	orb_set_angle(1,green_led);
297
	orb_set_angle(2,blue_led);
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);
298 607
}
299 608

  
300 609
/**
301
 * Set orb2 to the color specified. orb_init must
302
 * be called before this function may be used.
610
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
303 611
 *
304 612
 * @param red_led the red component of the color
305 613
 * @param green_led the green component of the color
306 614
 * @param blue_led the blue component of the color
307 615
 *
308
 * @see orb_init
616
 * @see orb_init, orb1_set, orb2_set
309 617
 **/
310
void orb2_set(unsigned char red_led, unsigned char green_led, unsigned char blue_led) {	
311
	orb_set_angle(4,red_led);
312
	orb_set_angle(5,green_led);
313
	orb_set_angle(6,blue_led);
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);
314 626
}
315 627

  
316 628
/**
317
 * Set both orbs to the specified color. This function
318
 * is intended to be used with the predefined
319
 * colors. orb_init must be called before this
320
 * function may be used.
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.
321 632
 *
322
 * @param col the color to set the orbs to
323
 *
324
 * @see orb_init
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
325 642
 **/
326
void orb_set_color(int col)
327
{
328
 int red, green, blue;
643
void orbs_set (
644
    uint8_t red1, uint8_t green1, uint8_t blue1,
645
    uint8_t red2, uint8_t green2, uint8_t blue2) {
329 646

  
330
 red = ((col & 0xE0) >> 5) * 36;
331
 green = ((col & 0x1C) >> 2) * 36;
332
 blue = (col & 0x03) * 85;
647
	REQUIRE_LOCK_OR_RETURN(orb_lock);
333 648

  
334
 orb_set(red, green, blue);
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);
335 654
}
336 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

  
337 670
/**
338
 * Set orb1 to the specified color. This function
339
 * is intended to be used with the predefined
340
 * colors. orb_init must be called before this
341
 * function may be used.
671
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
342 672
 *
673
 * @param num the number of the orb to set (0 or 1)
343 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.
344 682
 *
345
 * @see orb_init
683
 * @param col the color to set the orbs to
346 684
 **/
347
void orb1_set_color(int col)
348
{
349
 int red, green, blue;
685
void orb1_set_color(uint8_t col) {
686
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
687
}
350 688

  
351
 red = ((col & 0xE0) >> 5) * 36;
352
 green = ((col & 0x1C) >> 2) * 36;
353
 blue = (col & 0x03) * 85;
354

  
355
 orb1_set(red, green, blue);
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));
356 696
}
357 697

  
358 698
/**
359
 * Set orb2 to the specified color. This function
360
 * is intended to be used with the predefined
361
 * colors. orb_init must be called before this
362
 * function may be used.
699
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
363 700
 *
364 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.
365 709
 *
366
 * @see orb_init
710
 * @param col1 the color to set orb 1 to
711
 * @param col2 the color to set orb 2 to
367 712
 **/
368
void orb2_set_color(int col)
369
{
370
 int red, green, blue;
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
}
371 716

  
372
 red = ((col & 0xE0) >> 5) * 36;
373
 green = ((col & 0x1C) >> 2) * 36;
374
 blue = (col & 0x03) * 85;
717
#undef C_BLUE
718
#undef C_GREEN
719
#undef C_RED2
375 720

  
376
 orb2_set(red, green, blue);
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);
377 755
}
378 756

  
379
//DOES THIS WORK?
380
// Disables the timer1 interrupt, disabling the Orb's color fading capabilities
381
// You can still turn the red, green, and blue leds on and off with set_orb_dio
382
/* If we use the PWM for anything else besides the ORB, this implementation needs to be done better */
383 757
/**
384
 * Disables the orb color fading capabilities
385
 * by disabling the timer1 interrupt.
386
 *
387
 * @see orb_init
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
388 760
 **/
389
void orb_disable()
390
{
391
	TCCR3B &= 0;  	//Turn off everything
392
	ORB_PORT |= _BV(ORB1_RED);
393
	ORB_PORT |= _BV(ORB1_GREEN);
394
	ORB_PORT |= _BV(ORB1_BLUE);
395
	ORB_PORT |= _BV(ORB2_RED);
396
	ORB_PORT |= _BV(ORB2_GREEN);
397
	ORB_PORT |= _BV(ORB2_BLUE);
761
void orb_disable_timer (void) {
762
    // Disable the interrupts
763
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
398 764
}
399 765

  
400
//DOES THIS WORK?
401
// Enables the timer1 interrupt, enabling the Orb's color fading capabilities
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

  
402 802
/**
403
 * Enables the orb's color fading capabilities.
404
 *
405
 * @see orb_init
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
406 806
 **/
407
void orb_enable()
408
{
409
//	TCCR0 |= _BV(COM01) | _BV(COM00)  | _BV(WGM00) | _BV(CS01);	//Toggle OC Pin on match, FAST PWM Mode, clock/8
410
	TCCR3B =_BV(CS31);
807
void orb_init_binary (void) {
808
    orb_init_common ();
809
    orb_set_mode (orb_mode_binary);
411 810
}
412 811

  
413
/** @} **/ //end group
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
}
414 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
}

Also available in: Unified diff