Project

General

Profile

Revision 1132

Cleanup/Documentation

View differences:

lights.c
35 35

  
36 36
/*
37 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.
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 44
3/31/2009 - Martin
45
    Rewritten from scratch (mostly), fixes some code duplication, long ISRs,
46
	bugs, unnecessary synchronized code, memory waste
45
    Rewritten from scratch. Fixes code duplication, long ISRs, bugs, unnecessary synchronized code, memory waste
47 46
*/
48 47

  
48
/**
49
 * Quick start: call orb_init_pwm or orb_init_binary, depending on which mode you want to use. Call orb*set or
50
 * orb*set_color to set the orbs.
51
 * 
52
 * The orbs have two modes of operation: PWM mode and binary mode. In PWM mode, a pwm signal is generated by a hardware
53
 * timer and the orbs can be set to a value of 0 through 255. In binary mode, the orbs can only be turned on or off and
54
 * a value of 0 means "off" and any other value means "on". The mode can be chosen on initialization and can be changed
55
 * at runtime using the orb_set_mode function.
56
 *
57
 * Operation (PWM mode): On timer overflow, all LEDs with a value>0 are turned on and the output compare value for the
58
 * first LED is loaded. On compare match, the corresponding LED is turned off and the next output compare value is
59
 * loaded. All masks are precomputed and sorted by time when setting the values.
60
 *
61
 * The data structure (pwm_t) containing the PWM times and masks is triple buffered. This is because the buffer the ISR
62
 * is reading from may only be modified on timer overflow before the next PWM sequence is started, because otherwise the
63
 * next OCR value might be sed to a value smaller than the current timer value, resulting in the remaining channels not
64
 * being turned off in that PWM period (flash to on). When using two buffers, the page flip can only occur on a timer
65
 * overflow for the same reason. So after writing a buffer and marking it for page flip, neither of the buffers could be
66
 * modified because the front buffer is read by the ISR and the back buffer could be switched at any time. So the
67
 * calling thread would have to be delayed by up to one full PWM period (8ms in the current implementation, but
68
 * 20ms-50ms would be a reasonable value to expect here). To avoid this, triple buffering is used.
69
 *
70
 * The code for applying the orbs is fairly optimized. See the apply_orbs function for some time measurements and
71
 * further nodes.
72
 *
73
 * The PWM frequency is 120Hz (8ms period time). The next lower frequency (determined by the prescaler) is 30 Hz which
74
 * is too slow (orbs flicker).
75
 *
76
 * The orbs code is thread safe, which means that the functions may be called from another interrupt handler. If there
77
 * are multiple concurrent calls to the orb*set* functions, one of them is ignored and the orbs are never left in an
78
 * inconsistent state. For example, if the orbs are set to green by the main thread and to red by an interrupt handler,
79
 * the resulting color will be either red or green, but never yellow.
80
 *
81
 * Some performance measurements:
82
 *   - Time for setting new orb values (PWM mode): 35us-72us (depending on the degree to which the array is already in
83
 *     order)
84
 *   - Time for setting new orb values (binary mode): 5.5us
85
 *   - Maximum interrupt time (PWM mode only): overflow 2.5us, compare 6*10us (all values different) to 1*26us (all
86
 *     values identical). (TODO old values)
87
 *   - Total CPU use for interrupts (PWM mode only): 0.03%+0.75% max (TODO old values)
88
 *   - Maximum time spent in synchronized code: TODO
89
 * There are some possible optimizations. See the source code for more information.
90
 **/
91

  
49 92
/*
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
93
 * Test cases:
94
 *   - The following code has to work without flickering:
95
 *     orb_init_pwm(); while(1) { orbs_set(1,1,1,254,254,254); }
96
 */
57 97

  
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!
98
/*
99
 * Possible optimizations:
100
 *   - Use pointers instead of indicies for current_pwm_channel
101
 *   - Optimize the output compare interrupt
102
 *   - Use a different sorting algorithm (see sort_orbs_buffer for further comments on this issue)
103
 *   - Use pointers in fill_orbs_buffer
104
 *   - Optimized orb_set (use the knowledge that there are only 3 distinct values)
105
 *   - Use a lower update frequency. The next higher prescaler value leads to a frequency of 30Hz which is too low (the
106
 *     orbs are flickering). So the timer would have to be reloaded manually after 127 to generate 60Hz. This would
107
 *     decrease the resolution from 8 to 7 bit, but 128 steps should still be enough.
71 108
 */
72 109

  
110

  
73 111
/*
74 112
  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
113
    - Make thread safe
114
	- Check delayed interrupts
115
	
116
	- Timing tests
117
	  - How long do the interrupt handlers take?
118
      - How long may interrupts be blocked?
119
	
120
    - Complete Doxygen comments (after merge, no docs in the branch)
121
      - Introduction included?
80 122
 */
81 123

  
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
 
124

  
112 125
#include "lights.h"
113 126

  
114 127
#include <avr/interrupt.h>
......
142 155
#define ORB2_BLUE  6
143 156

  
144 157

  
158
// ***************
159
// ** Debugging **
160
// ***************
161

  
162
#define LIGHTS_DEBUG
163

  
164
#define LIGHTS_DEBUG_INIT                             DDRF=6;
165
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START         PORTF|=4;
166
#define LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END           PORTF&=~4;
167
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START   PORTF|=4;
168
#define LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END     PORTF&=~4;
169
#define LIGHTS_DEBUG_APPLY_START                      PORTF|=2;
170
#define LIGHTS_DEBUG_APPLY_END                        PORTF&=~2;
171

  
172

  
145 173
// ***********
146 174
// ** Masks **
147 175
// ***********
148 176

  
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).
177
// Some useful bit masks. All of them are are calculated from the I/O definitions above. The calculations should be done
178
// at compile time (even if they are not, they are only executed once at startup).
152 179

  
153 180
// Masks for the individual LEDs
154 181
#define orb1_red_mask    _BV (ORB1_RED  )
......
160 187

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

  
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 }
193
// Mask for the individual LEDs, organized as an array for programmatic access. The layout of this array is
194
// orb_mask[orb_num, color_num]
195
const uint8_t orb_mask[NUM_ORBS][NUM_COLORS]={
196
    { orb1_red_mask, orb1_green_mask, orb1_blue_mask },
197
    { orb2_red_mask, orb2_green_mask, orb2_blue_mask }
172 198
};
173 199

  
174 200
// ***********
175 201
// ** Types **
176 202
// ***********
177 203

  
178
struct pwm_channel_t // 2 bytes
179
{
204
struct pwm_channel_t { // 2 bytes
180 205
    uint8_t time;
181 206
    uint8_t mask;
182 207
};
183 208

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

  
190 214

  
......
192 216
// ** Variables **
193 217
// ***************
194 218

  
195
// Whether to use PWM (true) or binary (false) orb mode
219
// Whether to use PWM (true) or binary (false) orb mode. Not volatile because it's only read once per function.
196 220
bool enable_orb_pwm=true;
197 221

  
198
// The PWM channels and the buffer pointers. This data structure is triple
199
// buffered, see above for the reasons.
222
// The PWM channels and the buffer pointers. This data structure is triple buffered, see above for the reasons. Not
223
// volatile because they are not modified asynchronously (the read buffer is never written to asynchronously).
200 224
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.
225

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

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

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

  
237
// Whether to perform a page flip on the beginning of the next PWM cycle. Not volatile because it is only read once
238
// per function.
204 239
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
205 240

  
206

  
207
// The orb value array. Orb values are written here to be sorted into
208
// pwm_channels.
241
// The orb value array. Orb values are written here to be sorted into pwm_channels. Not volatile because all accesses
242
// are from guarded (thread safe) functions.
209 243
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
210 244

  
211 245

  
......
213 247
// ** Timer ISRs **
214 248
// ****************
215 249

  
216
// Not volatile - only accessed in the interrupt handler
250
// Not volatile because it is only accessed in the interrupt handler.
217 251
uint8_t current_pwm_channel=0;
218 252

  
219
SIGNAL (SIG_OVERFLOW0)
220
{
221
PORTF|=4;
253
SIGNAL (SIG_OVERFLOW0) {
254
#ifdef LIGHTS_DEBUG
255
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_START
256
#endif
222 257

  
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
	}
258
    if (pwm_page_flip) {
259
        // Flip the read buffer with the free buffer. We are in an ISR (and we didn't re-enable interrupts), so we don't
260
        // have to synchronize explicitly.
261
        struct pwm_t *temp = pwm_read_buffer;
262
        pwm_read_buffer    = pwm_free_buffer;
263
        pwm_free_buffer    = temp;
264
        pwm_page_flip=false;
265
    }
232 266

  
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;
267
    // Turn only the appropriate PWM channels on. Do this directly on the orb port because at this point all orbs should
268
    // be off anyway.
269
    ORBPORT|=all_orbs_mask;
270
    ORBPORT&=pwm_read_buffer->init_mask;
271
    
272
    // Start at the first channel
273
    current_pwm_channel=0;
274
    
275
    // Load the first OCR
276
    OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
277

  
278
#ifdef LIGHTS_DEBUG
279
    LIGHTS_DEBUG_OVERFLOW_INTERRUPT_END
280
#endif
247 281
}
248 282

  
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)
283
SIGNAL(SIG_OUTPUT_COMPARE0) {
284
#ifdef LIGHTS_DEBUG
285
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_START
286
#endif
256 287

  
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;
288
    // If this interrupt is executed without delay, TCNT0==time+1 (where time==OCR0)
264 289

  
265
		// Increment the channel
266
		current_pwm_channel++;
290
    // TODO delayed interrupt
291
    while (TCNT0==pwm_read_buffer->channel[current_pwm_channel].time+1) {
292
        // Turn the current channel off
293
        ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
267 294

  
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;
295
        // Increment the channel
296
        current_pwm_channel++;
297

  
298
        // If there is a next channel, load its OCR value
299
        if (current_pwm_channel<=(num_pwm_channels-1))
300
            if (pwm_read_buffer->channel[current_pwm_channel].time<255)
301
                OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
302
    }
303
    
304
#ifdef LIGHTS_DEBUG
305
    LIGHTS_DEBUG_OUTPUT_COMPARE_INTERRUPT_END
306
#endif
274 307
}
275 308

  
276 309

  
......
279 312
// ** Internal orb setting functions **
280 313
// ************************************
281 314

  
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; }
315
static void sort_orbs_buffer (void) {
316
    // This function applies a bubble sort to sort the elements of the pwm_write_buffer->channel array by the time
317
    // field.
318
    // This implementation is heavily optimized. Note that due to the low (and constant) number of elements to be
319
    // sorted, the runtime complexity (O(n^2) for bubble sort) is not relevant here. In fact, a more advanced algorithm
320
    // like quick sort or merge sort might even be slower due to higher overhead.
321
    // That said, it is possible that selection sort (which is also in O(n^2)) would be faster that bubble sort because
322
    // 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
323
    // if the elements are already in the correct order would either have to be left out (doing the full search every
324
    // time, even if the array is already sorted) or done explicitly, so selection sort might actually be slower than
325
    // bubble sort, especially if the array is already sorted or almost sorted.
326
    
327
    // This implementation uses macros to make the algorithm more clear because the loop is rolled out and the function
328
    // would become quite long without macros.
329
    
330
    // Macro to swap two values of any type. Requires a variable of the appropriate type called swap_temp.
331
    #define swap(a,b) { swap_temp=a; a=b; b=swap_temp; }
293 332

  
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++; }
333
    // Macro to do one bubble sorting step (compare & swap)
334
    #define bubble \
335
        if(a->time > b->time) \
336
        { \
337
            swap (a->time, b->time); \
338
            swap (a->mask, b->mask); \
339
            done=false; \
340
        }
341
    
342
    // Macro to move to the next bubble sort pair
343
    #define next { a++; b++; }
305 344

  
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]);
345
    // Whether no change was made during the last run, which means that all values are already in correct order.
346
    bool done;
347
    
348
    // A temporary variable for swapping.
349
    uint8_t swap_temp;
350
    
351
    // Precompute the first PWM channel (tested faster).
352
    struct pwm_channel_t *first=&(pwm_write_buffer->channel[0]);
315 353

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

  
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;
357
    // The actual sorting
358
    a=first; b=a+1; done=true;
359
    bubble next bubble next bubble next bubble next bubble
360
    if (done) return;
323 361

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

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

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

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

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

  
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
384
static void fill_orbs_buffer (void) {
385
    // We do not use a loop here because it introduces 27us overhead, which is quite much, given the total time for
386
    // optimized copying and sorting of 34us (elements already in correct order) to 71 us (elements in reverse order).
387
    
388
    #define copy_value(orb, color) \
389
        index=NUM_COLORS*orb+color; \
390
        time=orb_values[orb][color]; \
391
        mask=orb_mask[orb][color]; \
392
        \
393
        pwm_write_buffer->channel[index].time=time-1; \
394
        pwm_write_buffer->channel[index].mask=mask; \
395
        \
396
        if (time!=0) \
397
            pwm_write_buffer->init_mask &= ~mask; \
398

  
399
    uint8_t index, time, mask;
400
    copy_value(0,0); copy_value(0,1); copy_value(0,2);
401
    copy_value(1,0); copy_value(1,1); copy_value(1,2);
402
    
403
    #undef copy_value
365 404
}
366 405

  
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%)
406
static void apply_orbs (void) {
407
	/*
408
	 * Some timing tests: Time for apply_orbs with interrupts disabled, in us:
409
     *             Values in:     Correct order      Reverse order
410
     * Naive bubble sort:         148                217
411
     * Aborting bubble sort:       71                232
412
     * Only count to top:          73                189
413
     *
414
     * Loops rolled out:           61                120
415
     * Using pointers:             62                 98
416
     * Copy loop also rolled out:  35                 72
417
	 *
418
	 * Note that rolling out both loops and using pointers saves 52%/62% of time! 27us were spent on loop overhead,
419
	 * which is quite much, considering an optimized total time for copying and sorting or 35us.
420
	 */
375 421

  
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)
422
#ifdef LIGHTS_DEBUG
423
    LIGHTS_DEBUG_APPLY_START
424
#endif
379 425

  
380
// Improvement of rolling out + pointers: -53%/-62%
426
    if (enable_orb_pwm) {
427
        // PWM mode
428
        
429
        pwm_write_buffer->init_mask=~0;
430
    
431
        // 1. Write the orb values and corresponding masks to the pwm channels
432
        // array unsorted.
433
        fill_orbs_buffer ();
381 434

  
382
	if (enable_orb_pwm)
383
	{
384
		// PWM mode
385
		
386
		// Sort the orb values.
435
        // 2. sort the buffer.
436
        sort_orbs_buffer ();
387 437

  
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
	}
438
        // Flip the write buffer with the free buffer.
439
        SYNC {
440
            struct pwm_t *temp = pwm_write_buffer;
441
            pwm_write_buffer   = pwm_free_buffer;
442
            pwm_free_buffer    = temp;
443
        }
444
        
445
        // On the next overflow, flip the read buffer with the free buffer.
446
        pwm_page_flip=true;
447
    }
448
    else {
449
        // Binary mode.
450
        // The outputs are inverted.
451
        uint8_t on=0;
452
        
453
        if (orb_values[0][0]) on |= orb_mask[0][0];
454
        if (orb_values[0][1]) on |= orb_mask[0][1];
455
        if (orb_values[0][2]) on |= orb_mask[0][2];
456
        if (orb_values[1][0]) on |= orb_mask[1][0];
457
        if (orb_values[1][1]) on |= orb_mask[1][1];
458
        if (orb_values[1][2]) on |= orb_mask[1][2];
459
    
460
        // Write the new orb states to the output port. Synchronized because it is a RMW operation.
461
        SYNC {
462
            uint8_t value=ORBPORT;
463
            value |= all_orbs_mask; // All orbs off
464
            value &= ~on; // Selected orbs on
465
            ORBPORT=value;
466
        }
467
    }
468
            
469
#ifdef LIGHTS_DEBUG
470
    LIGHTS_DEBUG_APPLY_END
471
#endif
427 472
}
428 473

  
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;
474
static void set_orb_values (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
475
    // Write the values to the array, but do not sort them yet, as we might want to write the other orb values first so
476
    // we don't have to sort twice.
477
    // Any function calling this function will probably want to call apply_orbs() afterwards.
478
    orb_values[num][0]=red;
479
    orb_values[num][1]=green;
480
    orb_values[num][2]=blue;
435 481
}
436 482

  
437 483

  
......
444 490
// has to be handled.
445 491

  
446 492
/**
493
 * Sets the specified orb to the specified color. The orbs must be initialized before this function may be used.
494
 * Note that, when setting both orbs, using orbs_set is faster then setting the orbs individually because the values are
495
 * only sorted once.
496
 *
447 497
 * @param num the number of the orb to set (0 or 1)
498
 * @param red the red value for the specified orb
499
 * @param green the green value for the specified orb
500
 * @param blue the blue value for the specified orb
501
 * @see 
448 502
 */
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 ();
503
void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) {
504
    set_orb_values (num, red, green, blue);
505
    apply_orbs ();
453 506
}
454 507

  
455 508
/**
456
 * Set orb1 to the color specified. orb_init must be called before this function
457
 * may be used.
509
 * Set orb1 to the color specified. The orbs must be initialized before this function may be used. Note that, when
510
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
511
 * once.
458 512
 *
459 513
 * @param red the red component of the color
460 514
 * @param green the green component of the color
......
462 516
 *
463 517
 * @see orb_init
464 518
 **/
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 ();
519
void orb1_set (uint8_t red, uint8_t green, uint8_t blue) {
520
    set_orb_values (0, red, green, blue);
521
    apply_orbs ();
469 522
}
470 523

  
471 524
/**
472
 * Set orb2 to the color specified. orb_init must be called before this function
473
 * may be used.
525
 * Set orb2 to the color specified. The orbs must be initialized before this function may be used. Note that, when
526
 * setting both orbs, using orbs_set is faster then setting the orbs individually because the values are only sorted
527
 * once.
474 528
 *
475 529
 * @param red_led the red component of the color
476 530
 * @param green_led the green component of the color
......
478 532
 *
479 533
 * @see orb_init
480 534
 **/
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 ();
535
void orb2_set (uint8_t red, uint8_t green, uint8_t blue) {
536
    set_orb_values (1, red, green, blue);
537
    apply_orbs ();
485 538
}
486 539

  
487 540
/**
488
 * Set both orbs to the color specified. orb_init must be called before this
489
 * function may be used.
541
 * Set both orbs to the color specified. The orbs must be initialized before this function may be used.
490 542
 *
491 543
 * @param red_led the red component of the color
492 544
 * @param green_led the green component of the color
......
494 546
 *
495 547
 * @see orb_init, orb1_set, orb2_set
496 548
 **/
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 ();
549
void orb_set (uint8_t red, uint8_t green, uint8_t blue) {
550
    set_orb_values (0, red, green, blue);
551
    set_orb_values (1, red, green, blue);
552
    apply_orbs ();
502 553
}
503 554

  
555
/**
556
 * Set the orbs to the respective values. The orbs must be initialized before this function may be used. Note that, when
557
 * setting both orbs, this function is faster than calling orb1_set and orb2_set (or orb_n_set) because the values are
558
 * only sorted once.
559
 *
560
 * @param red1
561
 * @param green1
562
 * @param blue1
563
 * @param red2
564
 * @param green2
565
 * @param blue2
566
 * @see orb1_set
567
 * @see orb2_set
568
 * @see orb_n_set
569
 **/
504 570
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 ();
571
    uint8_t red1, uint8_t green1, uint8_t blue1,
572
    uint8_t red2, uint8_t green2, uint8_t blue2) {
573
    set_orb_values (0, red1, green1, blue1);
574
    set_orb_values (1, red2, green2, blue2);
575
    apply_orbs ();
511 576
}
512 577

  
513 578

  
......
521 586
#define C_BLUE(col)  (((col & 0x03)     ) * 85)
522 587

  
523 588
/**
524
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
589
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
525 590
 *
591
 * @param num the number of the orb to set (0 or 1)
526 592
 * @param col the color to set the orbs to
527 593
 **/
528
void orb_set_color(uint8_t col)
529
{
530
	orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
594
void orb_n_set_color(uint8_t num, uint8_t col) {
595
    orb_n_set(num, C_RED(col), C_GREEN(col), C_BLUE(col));
531 596
}
532 597

  
533 598
/**
......
535 600
 *
536 601
 * @param col the color to set the orbs to
537 602
 **/
538
void orb1_set_color(uint8_t col)
539
{
540
	orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
603
void orb1_set_color(uint8_t col) {
604
    orb1_set (C_RED(col), C_GREEN(col), C_BLUE(col));
541 605
}
542 606

  
543 607
/**
......
545 609
 *
546 610
 * @param col the color to set the orbs to
547 611
 **/
548
void orb2_set_color(uint8_t col)
549
{
550
	orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
612
void orb2_set_color(uint8_t col) {
613
    orb2_set(C_RED(col), C_GREEN(col), C_BLUE(col));
551 614
}
552 615

  
553 616
/**
554
 * Set the specified orb to the specified color. This function is intended to be used with the predefined colors.
617
 * Set both orbs to the specified color. This function is intended to be used with the predefined colors.
555 618
 *
556
 * @param num the number of the orb to set (0 or 1)
557 619
 * @param col the color to set the orbs to
558 620
 **/
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));
621
void orb_set_color(uint8_t col) {
622
    orb_set (C_RED(col), C_GREEN(col), C_BLUE(col));
562 623
}
563 624

  
564 625
/**
565
 * Set the orbs to the respecitve color. This function is intended to be used with the predefined colors.
626
 * Set the orbs to the respective color. This function is intended to be used with the predefined colors.
566 627
 *
567 628
 * @param col1 the color to set orb 1 to
568 629
 * @param col2 the color to set orb 2 to
569 630
 **/
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));
631
void orbs_set_color(uint8_t col1, uint8_t col2) {
632
    orbs_set (C_RED(col1), C_GREEN(col1), C_BLUE(col1), C_RED(col2), C_GREEN(col2), C_BLUE(col2));
573 633
}
574 634

  
575 635
#undef C_BLUE
......
581 641
// ** Mode setting **
582 642
// ******************
583 643

  
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
	
644
/**
645
 * Enables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
646
 * @see orb_set_mode
647
 **/
648
void orb_enable_timer (void) {
649
    // Use 8 bit TC0.
650
    //
651
    // Timer mode: We cannot use CTC mode because it can only clear on OCR0 (in contrast to the 16 bit timers which can
652
    // also use the ICR for that) and OCR0 is already used for generating output compare interrupts. We also need
653
    // immediate (non double buffered) update of OCR0, so the only mode left is "Normal".
654
    //
655
    // Note that for a timer counting from 0 to 255, there are 256 states and thus 257 output possibilities
656
    // (0/256...256/256)! However, there are only 256 values in the byte used to specify the PWM value. Possible ways
657
    // to deal with that:
658
    //   1. use a 16 bit variable for the PWM value (memory waste, overhead)
659
    //   2. use an additional flag for the 257th value (inconvenient)
660
    //   3. use 1/256...256/256 (skip 0, never complete off)
661
    //   4. use 0/256...256/256 (skip 256, never complete on)
662
    //   5. skip a value somewhere in the middle
663
    //   6. reload the timer after 254
664
    // For this implementation, variant 4 was chosen.
665
    //
666
    // Using an 8 bit timer has the added advantage that all the comparisons are faster.
667
    
668
    // Normal mode, Compare match output off, Prescaler
669
    TCCR0=_BV(CS02) | _BV(CS01); // 1024, 30 Hz
605 670

  
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);
671
    // Enable the interrupts
672
    TIMSK|= _BV(OCIE0) | _BV(TOIE0);
611 673
}
612 674

  
613
void orb_disable_timer (void)
614
{
615
	TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
675
/**
676
 * Disables the orb timer. Note that you usually don't want to use this function directly. Instead, use orb_set_mode.
677
 * @see orb_set_mode
678
 **/
679
void orb_disable_timer (void) {
680
    // Disable the interrupts
681
    TIMSK&=~( _BV(OCIE0) | _BV(TOIE0));
616 682
}
617 683

  
618 684

  
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
	}
685
void orb_set_mode (orb_mode_t mode) {
686
    // Set enable_orb_pwm to the appropriate value and disable or enable the timer.
687
    if (mode==orb_mode_binary) {
688
        orb_disable_timer ();
631 689

  
632
	apply_orbs ();
690
        enable_orb_pwm=false;
691
        apply_orbs ();
692
    }
693
    else { // orb_mode_pwm
694
        enable_orb_pwm=true;
695
        apply_orbs ();
696

  
697
        orb_enable_timer ();
698
    }
633 699
}
634 700

  
635 701

  
......
638 704
// ********************
639 705

  
640 706
// 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;
707
static void orb_init_common (void) {
708
    // Enable the output ports and turn off the LEDs
709
    ORBPORT |=  all_orbs_mask;
710
    ORBDDR  |=  all_orbs_mask;
646 711

  
647
	// Set all orbs to "off"
648
	orb_set (0, 0, 0);
649
	
650
    // Debug
651
    DDRF=6; // TODO remove
712
    // Set all orbs to "off"
713
    orb_set (0, 0, 0);
714
    
715
#ifdef LIGHTS_DEBUG
716
    LIGHTS_DEBUG_INIT
717
#endif
652 718
}
653 719

  
654 720
/**
......
656 722
 * 
657 723
 * @see orb_init_pwm
658 724
 **/
659
void orb_init_binary (void)
660
{
661
	orb_init_common ();
662
	orb_set_mode (orb_mode_binary);
725
void orb_init_binary (void) {
726
    orb_init_common ();
727
    orb_set_mode (orb_mode_binary);
663 728
}
664 729

  
665 730
/**
......
667 732
 * 
668 733
 * @see orb_init_binary
669 734
 **/
670
void orb_init_pwm (void)
671
{
672
	orb_init_common ();
673
	orb_set_mode (orb_mode_pwm);
735
void orb_init_pwm (void) {
736
    orb_init_common ();
737
    orb_set_mode (orb_mode_pwm);
674 738
}
675 739

  
676 740
/**
677
 * A synonym for orb_init_pwm
741
 * Initializes the orbs in default mode. One of the orb_init* functions must be called before the orbs can be used. Use
742
 * the orb_init_binary or orb_init_pwm function if you want one specific mode.
678 743
 *
679 744
 * @see orb_init_pwm
745
 * @see orb_init_binary
680 746
 **/
681
void orb_init ()
682
{
683
	orb_init_pwm ();
747
void orb_init () {
748
    orb_init_pwm ();
684 749
}
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
}

Also available in: Unified diff