Revision 1107
Implemented triple buffering
Optimized sorting
lights.c | ||
---|---|---|
72 | 72 |
|
73 | 73 |
/* |
74 | 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) |
|
75 |
- Find out the interrupt time |
|
76 |
- Optimize the OC interrupt |
|
79 | 77 |
- enable_orb_pwm use everywhere, add methods to switch on/off, add init |
80 | 78 |
function |
81 | 79 |
- test old code: continuously setting the orbs |
82 |
- n-buffering |
|
83 | 80 |
- fix sync/volatile |
84 | 81 |
*/ |
85 | 82 |
|
... | ... | |
185 | 182 |
uint8_t mask; |
186 | 183 |
}; |
187 | 184 |
|
188 |
sutrct pwm_t
|
|
185 |
struct pwm_t
|
|
189 | 186 |
{ |
190 | 187 |
uint8_t init_mask; |
191 |
pwm_channels_t channel[num_pwm_channels];
|
|
192 |
} |
|
188 |
struct pwm_channel_t channel[num_pwm_channels];
|
|
189 |
};
|
|
193 | 190 |
|
194 | 191 |
|
195 | 192 |
// *************** |
... | ... | |
204 | 201 |
struct pwm_t pwm_buffer[3]; |
205 | 202 |
// TODO using pointers might be faster (or might be slower because pointers are |
206 | 203 |
// 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;
|
|
204 |
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.
|
|
205 |
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.
|
|
206 |
struct pwm_t *pwm_free_buffer =&pwm_buffer[2]; // The back buffer to flip with.
|
|
207 |
bool pwm_page_flip=false; // Whether to do a page flip on the next overflow
|
|
211 | 208 |
|
212 | 209 |
|
213 |
|
|
214 | 210 |
// The orb value array. Orb values are written here to be sorted into |
215 | 211 |
// pwm_channels. |
216 | 212 |
uint8_t orb_values[NUM_ORBS][NUM_COLORS]; |
... | ... | |
228 | 224 |
SIGNAL (SIG_OVERFLOW0) |
229 | 225 |
{ |
230 | 226 |
PORTF|=4; |
227 |
|
|
228 |
if (pwm_page_flip) |
|
229 |
{ |
|
230 |
// Flip the read buffer with the free buffer |
|
231 |
// We are in an ISR, so we don't have to synchronize explicitly. |
|
232 |
struct pwm_t *temp = pwm_read_buffer; |
|
233 |
pwm_read_buffer = pwm_free_buffer; |
|
234 |
pwm_free_buffer = temp; |
|
235 |
pwm_page_flip=false; |
|
236 |
} |
|
237 |
|
|
231 | 238 |
// Turn all appropriate PWM channels on |
232 |
ORBPORT&=pwm_init_mask; |
|
239 |
ORBPORT&=pwm_read_buffer->init_mask;
|
|
233 | 240 |
|
234 |
// Start at the first channel |
|
241 |
// Start at the first channel (TODO faster w/ pointers?)
|
|
235 | 242 |
current_pwm_channel=0; |
236 | 243 |
|
237 | 244 |
// Load the first OCR |
238 |
OCR0=pwm_channels[current_pwm_channel].time;
|
|
245 |
OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
|
|
239 | 246 |
PORTF&=~4; |
240 | 247 |
} |
241 | 248 |
|
... | ... | |
250 | 257 |
|
251 | 258 |
// TODO improve (check overflow; maybe use return after last, maybe use |
252 | 259 |
// pointers instead of indicies) |
253 |
while (TCNT0==pwm_channels[current_pwm_channel].time+1)
|
|
260 |
while (TCNT0==pwm_read_buffer->channel[current_pwm_channel].time+1)
|
|
254 | 261 |
{ |
255 | 262 |
// Turn the current channel off |
256 |
ORBPORT|=pwm_channels[current_pwm_channel].mask;
|
|
263 |
ORBPORT|=pwm_read_buffer->channel[current_pwm_channel].mask;
|
|
257 | 264 |
|
258 | 265 |
// Increment the channel |
259 | 266 |
current_pwm_channel++; |
260 | 267 |
|
261 | 268 |
// If there is a next channel, load its OCR value |
262 | 269 |
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;
|
|
270 |
if (pwm_read_buffer->channel[current_pwm_channel].time<255)
|
|
271 |
OCR0=pwm_read_buffer->channel[current_pwm_channel].time;
|
|
265 | 272 |
} |
266 | 273 |
PORTF&=~4; |
267 | 274 |
} |
... | ... | |
288 | 295 |
// Sort the orb values. |
289 | 296 |
|
290 | 297 |
PORTF|=2; |
291 |
SYNC |
|
292 |
{ |
|
293 |
pwm_init_mask=~0; |
|
298 |
pwm_write_buffer->init_mask=~0; |
|
294 | 299 |
|
295 | 300 |
// 1. Write the orb values and corresponding masks to the pwm channels |
296 | 301 |
// array unsorted |
302 |
|
|
297 | 303 |
for (uint8_t orb=0; orb<2; ++orb) |
298 | 304 |
{ |
299 | 305 |
for (uint8_t color=0; color<3; ++color) |
... | ... | |
303 | 309 |
uint8_t time=orb_values[orb][color]; |
304 | 310 |
uint8_t mask=orb_mask[orb][color]; |
305 | 311 |
|
306 |
pwm_channels[index].time=time-1;
|
|
307 |
pwm_channels[index].mask=mask;
|
|
312 |
pwm_write_buffer->channel[index].time=time-1;
|
|
313 |
pwm_write_buffer->channel[index].mask=mask;
|
|
308 | 314 |
|
309 | 315 |
if (time!=0) |
310 |
pwm_init_mask &= ~mask; |
|
316 |
pwm_write_buffer->init_mask &= ~mask;
|
|
311 | 317 |
} |
312 | 318 |
} |
313 | 319 |
|
314 |
// 2. Sort the values. Use bubble sort for now. |
|
315 |
for (uint8_t count=num_pwm_channels-1; count>0; --count) |
|
320 |
// 2. Sort the values. Use bubble sort. |
|
321 |
// Considering the low number of data points, more |
|
322 |
// sophisticated algorithms are unlikely to be faster. |
|
323 |
bool done; |
|
324 |
// For 6 channels, we count i=0..4 and compare i with i+1 |
|
325 |
uint8_t top=num_pwm_channels-1; |
|
326 |
do |
|
316 | 327 |
{ |
317 |
for (uint8_t i=num_pwm_channels-1; i>0; --i) |
|
328 |
done=true; // We are done unless we do some swapping |
|
329 |
|
|
330 |
for (uint8_t i=0; i<top; ++i) |
|
318 | 331 |
{ |
319 |
if (pwm_channels[i].time<pwm_channels[i-1].time) |
|
332 |
#define channel_a pwm_write_buffer->channel[i] |
|
333 |
#define channel_b pwm_write_buffer->channel[i+1] |
|
334 |
|
|
335 |
if (channel_a.time>channel_b.time) |
|
320 | 336 |
{ |
321 | 337 |
uint8_t temp; |
322 | 338 |
|
339 |
|
|
323 | 340 |
// 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;
|
|
341 |
temp = channel_a.time;
|
|
342 |
channel_a.time = channel_b.time;
|
|
343 |
channel_b.time = temp;
|
|
327 | 344 |
|
328 | 345 |
// 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; |
|
346 |
temp = channel_a.mask; |
|
347 |
channel_a.mask = channel_b.mask; |
|
348 |
channel_b.mask = temp; |
|
349 |
|
|
350 |
done=false; // No, we're not done yet. |
|
332 | 351 |
} |
352 |
|
|
353 |
#undef channel_a |
|
354 |
#undef channel_b |
|
333 | 355 |
} |
356 |
|
|
357 |
// On the next iteration, we need one less comparison |
|
358 |
top--; |
|
359 |
} while (!done); |
|
360 |
|
|
361 |
// Flip the write buffer with the free buffer |
|
362 |
SYNC |
|
363 |
{ |
|
364 |
struct pwm_t *temp = pwm_write_buffer; |
|
365 |
pwm_write_buffer = pwm_free_buffer; |
|
366 |
pwm_free_buffer = temp; |
|
334 | 367 |
} |
335 |
} |
|
336 |
//SYNC_END; |
|
368 |
|
|
369 |
// On the next overflow, do the page flip. |
|
370 |
pwm_page_flip=true; |
|
371 |
|
|
337 | 372 |
PORTF&=~2; |
338 |
|
|
339 | 373 |
} |
340 | 374 |
else |
341 | 375 |
{ |
... | ... | |
598 | 632 |
//orbs_set (255, 127, 0, 0, 127, 255); // Pretty colors with extreme values |
599 | 633 |
//orbs_set (0, 1, 2, 253, 254, 255); // Timing tests |
600 | 634 |
|
601 |
orbs_set (255, 255, 255, 0, 0, 0); |
|
602 |
delay_ms (1000); |
|
635 |
// orbs_set (255, 255, 255, 0, 0, 0);
|
|
636 |
// delay_ms (1000);
|
|
603 | 637 |
|
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 | 638 |
//while (1) |
613 | 639 |
//{ |
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);
|
|
640 |
// orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
|
|
641 |
// //orbs_set (255, 255, 255, 1, 1, 1);
|
|
642 |
// //_delay_us(400);
|
|
617 | 643 |
//} |
644 |
|
|
645 |
// Test the time of the sorting routine |
|
646 |
// while (1) |
|
647 |
// { |
|
648 |
// orbs_set (10, 20, 30, 40, 50, 60); // Correct order |
|
649 |
// //orbs_set (60, 50, 40, 30, 20, 10); // Reverse order |
|
650 |
// delay_ms (10); |
|
651 |
// } |
|
618 | 652 |
} |
619 | 653 |
|
620 |
|
|
654 |
// Pure sorting time/us (interrupts disabled!) (difference to naive bs): |
|
655 |
// Correct order Reverse order |
|
656 |
// Naive bubble sort: 147 216 |
|
657 |
// Aborting bubble sort: 70 (-52%) 231 (+7%) |
|
658 |
// Aborting w/ top: 72 (-51%) 188 (-13%) |
Also available in: Unified diff