55 |
55 |
- switch off LEDs (according to mask)
|
56 |
56 |
- load the next output compare value
|
57 |
57 |
|
|
58 |
Ad triple buffering:
|
|
59 |
- The buffer the ISR is reading from may only be changed at timer overflow,
|
|
60 |
before the next PWM sequence is started, because otherwise, the next OCR
|
|
61 |
value may be set to a value smaller than the current timer value, resulting
|
|
62 |
in the remaining channels not being turned off in that PWM period (flash to
|
|
63 |
on).
|
|
64 |
- When using two buffers, the copying (or switching) would have to wait until
|
|
65 |
the next timer overflow. During this time, neither of the buffers could be
|
|
66 |
modified because one is used by the ISR and the other may be copied/switched
|
|
67 |
at any time. Thus, the main thread would possibly be delayed by up to one
|
|
68 |
full PWM period (8ms in the current implementation, but 20ms-50ms would be a
|
|
69 |
reasonable value to expect here.
|
|
70 |
- Triple buffering For Teh Win!
|
58 |
71 |
*/
|
59 |
72 |
|
60 |
73 |
/*
|
... | ... | |
62 |
75 |
- test the timing
|
63 |
76 |
- Determine sorting time, possibly improve sorting algorithm (111 us sorted,
|
64 |
77 |
143 us reverse sorted)
|
|
78 |
- stop sorting when finished (no change made) (and measure performance gain)
|
65 |
79 |
- enable_orb_pwm use everywhere, add methods to switch on/off, add init
|
66 |
80 |
function
|
|
81 |
- test old code: continuously setting the orbs
|
|
82 |
- n-buffering
|
|
83 |
- fix sync/volatile
|
67 |
84 |
*/
|
68 |
85 |
|
69 |
86 |
/*
|
... | ... | |
84 |
101 |
* - buzzer => doesn't work because of varying frequency
|
85 |
102 |
* - motors => possible? would trigger often (?)
|
86 |
103 |
* - lights => must put lights on 16 bit timer (no OCR left)
|
|
104 |
* - Syncronization test case: set orb A to 1,1,1 (not 0 because they will
|
|
105 |
* not be turned on) and orb B to 254,254,254. Do this in a loop, with
|
|
106 |
* some delay d between.
|
|
107 |
* * d=1ms => occasional flickering
|
|
108 |
* * d=400us => frequent flickering
|
|
109 |
* * d=0 => no usable orb output
|
|
110 |
* Without syncronization, both LEDs flicker (because the wrong values are
|
|
111 |
* in the channels array while sorting). When the sorting code ist
|
|
112 |
* synchronized, only orb A flickers, because the timing is disrupted by the
|
|
113 |
* large synchronized block.
|
87 |
114 |
*/
|
88 |
115 |
|
89 |
116 |
#include "lights.h"
|
|
117 |
|
90 |
118 |
#include <avr/interrupt.h>
|
|
119 |
|
91 |
120 |
#include "dragonfly_lib.h"
|
92 |
121 |
|
93 |
122 |
|
|
123 |
// ***************
|
|
124 |
// ** Constants **
|
|
125 |
// ***************
|
|
126 |
|
|
127 |
#define NUM_ORBS 2 // Number or orbs
|
|
128 |
#define NUM_COLORS 3 // Number of colors per orb
|
|
129 |
#define num_pwm_channels NUM_ORBS*NUM_COLORS
|
|
130 |
|
|
131 |
|
94 |
132 |
// *********
|
95 |
133 |
// ** I/O **
|
96 |
134 |
// *********
|
97 |
135 |
|
98 |
|
#define NUM_ORBS 2 // Number or orbs
|
99 |
|
#define NUM_COLORS 3 // Number of colors per orb
|
100 |
|
|
101 |
136 |
// Orb port
|
102 |
137 |
#define ORBPORT PORTC
|
103 |
138 |
#define ORBDDR DDRC
|
... | ... | |
150 |
185 |
uint8_t mask;
|
151 |
186 |
};
|
152 |
187 |
|
|
188 |
sutrct pwm_t
|
|
189 |
{
|
|
190 |
uint8_t init_mask;
|
|
191 |
pwm_channels_t channel[num_pwm_channels];
|
|
192 |
}
|
153 |
193 |
|
|
194 |
|
154 |
195 |
// ***************
|
155 |
196 |
// ** Variables **
|
156 |
197 |
// ***************
|
157 |
198 |
|
158 |
|
#define num_pwm_channels NUM_ORBS*NUM_COLORS
|
159 |
|
struct pwm_channel_t pwm_channels[num_pwm_channels];
|
160 |
|
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
|
161 |
|
uint8_t pwm_init_mask;
|
|
199 |
// Whether to use PWM (true) or binary (false) orb mode
|
162 |
200 |
bool enable_orb_pwm=true;
|
163 |
201 |
|
|
202 |
// The PWM channels and the buffer pointers. This data structure is triple
|
|
203 |
// buffered, see above for the reasons.
|
|
204 |
struct pwm_t pwm_buffer[3];
|
|
205 |
// TODO using pointers might be faster (or might be slower because pointers are
|
|
206 |
// 16 bit long).
|
|
207 |
uint8_t read_buffer=0; // The buffer the ISR reads from
|
|
208 |
uint8_t next_buffer=0; // The buffer the ISR will switch to on the next overflow
|
|
209 |
uint8_t
|
|
210 |
uint8_t inactive_buffer=2;
|
164 |
211 |
|
|
212 |
|
|
213 |
|
|
214 |
// The orb value array. Orb values are written here to be sorted into
|
|
215 |
// pwm_channels.
|
|
216 |
uint8_t orb_values[NUM_ORBS][NUM_COLORS];
|
|
217 |
|
|
218 |
// TODO: random note: what happens if the main process is sorting and a different
|
|
219 |
// interrupt calls set_orb?
|
|
220 |
|
|
221 |
|
165 |
222 |
// ****************
|
166 |
223 |
// ** Timer ISRs **
|
167 |
224 |
// ****************
|
... | ... | |
170 |
227 |
|
171 |
228 |
SIGNAL (SIG_OVERFLOW0)
|
172 |
229 |
{
|
173 |
|
//PORTF=2;
|
|
230 |
PORTF|=4;
|
174 |
231 |
// Turn all appropriate PWM channels on
|
175 |
232 |
ORBPORT&=pwm_init_mask;
|
176 |
233 |
|
... | ... | |
179 |
236 |
|
180 |
237 |
// Load the first OCR
|
181 |
238 |
OCR0=pwm_channels[current_pwm_channel].time;
|
182 |
|
//PORTF=0;
|
|
239 |
PORTF&=~4;
|
183 |
240 |
}
|
184 |
241 |
|
185 |
242 |
SIGNAL(SIG_OUTPUT_COMPARE0)
|
186 |
243 |
{
|
187 |
|
//PORTF=4;
|
|
244 |
PORTF|=4;
|
188 |
245 |
// TODO:
|
189 |
246 |
// - delayed interrupt
|
190 |
247 |
// - synchronization OK?
|
191 |
248 |
|
192 |
249 |
// If the interrupt is executed w/o delay, TCNT0 == time+1 (and TIME=OCR0)
|
193 |
250 |
|
194 |
|
// TODO improve (check overflow; maybe use return after last
|
|
251 |
// TODO improve (check overflow; maybe use return after last, maybe use
|
|
252 |
// pointers instead of indicies)
|
195 |
253 |
while (TCNT0==pwm_channels[current_pwm_channel].time+1)
|
196 |
254 |
{
|
197 |
255 |
// Turn the current channel off
|
... | ... | |
205 |
263 |
if (pwm_channels[current_pwm_channel].time<255)
|
206 |
264 |
OCR0=pwm_channels[current_pwm_channel].time;
|
207 |
265 |
}
|
208 |
|
//PORTF=0;
|
|
266 |
PORTF&=~4;
|
209 |
267 |
}
|
210 |
268 |
|
211 |
269 |
|
... | ... | |
217 |
275 |
// TODO: make a public version of this one, but keep a private one which does
|
218 |
276 |
// not sort them so you can update both sides and update only once.
|
219 |
277 |
|
|
278 |
#define SYNC_START uint8_t tmp_sreg; do { tmp_sreg=SREG; cli (); } while (false)
|
|
279 |
#define SYNC_RESTART do { tmp_sreg=SREG; cli (); } while (false)
|
|
280 |
#define SYNC_END do { SREG=tmp_sreg; } while (false)
|
|
281 |
|
220 |
282 |
static void apply_orbs (void)
|
221 |
283 |
{
|
222 |
|
PORTF=2;
|
223 |
284 |
if (enable_orb_pwm)
|
224 |
285 |
{
|
225 |
286 |
// PWM mode
|
226 |
287 |
|
227 |
288 |
// Sort the orb values.
|
228 |
289 |
|
|
290 |
PORTF|=2;
|
|
291 |
SYNC
|
|
292 |
{
|
229 |
293 |
pwm_init_mask=~0;
|
230 |
294 |
|
231 |
295 |
// 1. Write the orb values and corresponding masks to the pwm channels
|
... | ... | |
268 |
332 |
}
|
269 |
333 |
}
|
270 |
334 |
}
|
271 |
|
|
272 |
|
//pwm_init_mask=all_orbs_mask; // FIXME use real values
|
273 |
|
|
274 |
|
// Sort the values from orb_values[NUM_ORBS][NUM_COLORS].
|
275 |
|
|
|
335 |
}
|
|
336 |
//SYNC_END;
|
|
337 |
PORTF&=~2;
|
|
338 |
|
276 |
339 |
}
|
277 |
340 |
else
|
278 |
341 |
{
|
... | ... | |
283 |
346 |
// is faster this way, and being fast is the whole point of using the
|
284 |
347 |
// binary orb mode anyway.
|
285 |
348 |
}
|
286 |
|
PORTF=0;
|
287 |
349 |
}
|
288 |
350 |
|
289 |
351 |
static void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue)
|
... | ... | |
375 |
437 |
|
376 |
438 |
|
377 |
439 |
|
378 |
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
379 |
|
|
380 |
|
//#define ORB_RESET 1025
|
381 |
|
//#define ORBMASK 0x77
|
382 |
|
//
|
383 |
|
///***** Port and Pin Definitions ****/
|
384 |
|
//
|
385 |
|
//
|
386 |
|
//// an orb node
|
387 |
|
//struct ORB_NODE {
|
388 |
|
// uint8_t num;
|
389 |
|
// uint16_t angle;
|
390 |
|
//};
|
391 |
|
//
|
392 |
|
//the change in an orb
|
393 |
|
//struct ORB_CHANGE {
|
394 |
|
// uint16_t port_val;
|
395 |
|
// uint16_t split_time_period;
|
396 |
|
//};
|
397 |
|
//
|
398 |
|
//// the status of an orb
|
399 |
|
//struct ORB_STATUS_STRUCT {
|
400 |
|
// struct ORB_NODE orbs[ORB_COUNT];
|
401 |
|
// uint16_t orb_angles[ORB_COUNT];
|
402 |
|
// struct ORB_CHANGE changes[ORB_COUNT+1];
|
403 |
|
// uint8_t change_count;
|
404 |
|
// uint8_t new_angles;
|
405 |
|
// uint8_t current_orb;
|
406 |
|
//
|
407 |
|
//} ORB_STATUS;
|
408 |
|
//
|
409 |
|
//void orb_sort(void);
|
410 |
|
//void orb_setup_pulse(void);
|
411 |
|
//
|
412 |
|
//SIGNAL (SIG_OUTPUT_COMPARE3C){
|
413 |
|
//
|
414 |
|
// //pull the correct ones down
|
415 |
|
// ORBPORT &= (~ORBMASK)|(ORB_STATUS.changes[ORB_STATUS.current_orb].port_val);
|
416 |
|
//
|
417 |
|
// ++ORB_STATUS.current_orb; //now look at next orb transition
|
418 |
|
//
|
419 |
|
// if (ORB_STATUS.current_orb < ORB_STATUS.change_count) { //if it isnt the end...
|
420 |
|
//
|
421 |
|
// //setup timer for next pull down
|
422 |
|
// OCR3C = TCNT3+ORB_STATUS.changes[ORB_STATUS.current_orb].split_time_period;
|
423 |
|
//
|
424 |
|
// }
|
425 |
|
// else { //we are done with these pulses
|
426 |
|
// orb_setup_pulse();
|
427 |
|
// }
|
428 |
|
//
|
429 |
|
//}
|
430 |
|
//
|
431 |
|
//
|
432 |
|
////sets a channel to a value
|
433 |
|
//void orb_set_angle(uint16_t orb, uint16_t angle) {
|
434 |
|
// uint8_t mysreg;
|
435 |
|
//
|
436 |
|
// orb=orb&0x07; //only have 8
|
437 |
|
// angle=angle&0xff; //only accept 0-255
|
438 |
|
// angle=255-angle; //inverse intensity
|
439 |
|
// angle=angle<<2; //scale up so that we dont run it too often
|
440 |
|
// angle+=3; //0 values dont really work
|
441 |
|
// if (ORB_STATUS.orb_angles[orb] != angle) { //if the angle has changed
|
442 |
|
// mysreg=SREG;
|
443 |
|
// cli(); //disable interrupts
|
444 |
|
// ORB_STATUS.orb_angles[orb] = angle; //update angle
|
445 |
|
// ORB_STATUS.new_angles = 1;
|
446 |
|
// SREG=mysreg; //put interrupt status back
|
447 |
|
// }
|
448 |
|
//}
|
449 |
|
//
|
450 |
|
//
|
451 |
|
//void orb_sort(void) {
|
452 |
|
// uint16_t done = 0, i;
|
453 |
|
//
|
454 |
|
// while (! done) {
|
455 |
|
// done = 1;
|
456 |
|
//
|
457 |
|
// for (i = 0; i < ORB_COUNT - 1; ++i) { //loop through all
|
458 |
|
//
|
459 |
|
// //if they are out of order, swap them
|
460 |
|
// if (ORB_STATUS.orbs[i].angle > ORB_STATUS.orbs[i+1].angle) {
|
461 |
|
// ORB_STATUS.orbs[i].angle ^= ORB_STATUS.orbs[i+1].angle;
|
462 |
|
// ORB_STATUS.orbs[i+1].angle ^= ORB_STATUS.orbs[i].angle;
|
463 |
|
// ORB_STATUS.orbs[i].angle ^= ORB_STATUS.orbs[i+1].angle;
|
464 |
|
//
|
465 |
|
// ORB_STATUS.orbs[i].num ^= ORB_STATUS.orbs[i+1].num;
|
466 |
|
// ORB_STATUS.orbs[i+1].num ^= ORB_STATUS.orbs[i].num;
|
467 |
|
// ORB_STATUS.orbs[i].num ^= ORB_STATUS.orbs[i+1].num;
|
468 |
|
//
|
469 |
|
// done = 0;
|
470 |
|
// }
|
471 |
|
// }
|
472 |
|
// }
|
473 |
|
//}
|
474 |
|
//
|
475 |
|
////calculate the split times
|
476 |
|
//void orb_setup_pulse(void) {
|
477 |
|
// uint16_t i;
|
478 |
|
// uint16_t my_port;
|
479 |
|
// uint16_t sum = 0;
|
480 |
|
// uint16_t split_time;
|
481 |
|
//
|
482 |
|
// my_port = 0xff; //all on
|
483 |
|
//
|
484 |
|
// if (ORB_STATUS.new_angles) {
|
485 |
|
//
|
486 |
|
// ORB_STATUS.change_count = 0;
|
487 |
|
// for (i = 0; i < ORB_COUNT; ++i) { //get the new values
|
488 |
|
// ORB_STATUS.orbs[i].angle = ORB_STATUS.orb_angles[ORB_STATUS.orbs[i].num];
|
489 |
|
// }
|
490 |
|
//
|
491 |
|
// orb_sort(); //sort them
|
492 |
|
// ORB_STATUS.new_angles = 0;
|
493 |
|
//
|
494 |
|
// for (i = 0; i < ORB_COUNT; ++i) { //calculate split times
|
495 |
|
// split_time = ORB_STATUS.orbs[i].angle - sum;
|
496 |
|
// my_port &= ~_BV(ORB_STATUS.orbs[i].num);
|
497 |
|
//
|
498 |
|
// for (; i < ORB_COUNT - 1 && ORB_STATUS.orbs[i].angle == ORB_STATUS.orbs[i+1].angle; ++i) {
|
499 |
|
// my_port &= ~_BV(ORB_STATUS.orbs[i+1].num); //look for doups
|
500 |
|
// }
|
501 |
|
//
|
502 |
|
// ORB_STATUS.changes[ORB_STATUS.change_count].port_val = my_port; //which pins are low
|
503 |
|
// ORB_STATUS.changes[ORB_STATUS.change_count].split_time_period = split_time;
|
504 |
|
//
|
505 |
|
// ++ORB_STATUS.change_count;
|
506 |
|
//
|
507 |
|
// sum += split_time;
|
508 |
|
// }
|
509 |
|
//
|
510 |
|
// ORB_STATUS.changes[ORB_STATUS.change_count].port_val = my_port;
|
511 |
|
// ORB_STATUS.changes[ORB_STATUS.change_count].split_time_period = ORB_RESET - sum; //get a constant period
|
512 |
|
//
|
513 |
|
// ++ORB_STATUS.change_count;
|
514 |
|
//
|
515 |
|
// }
|
516 |
|
//
|
517 |
|
//
|
518 |
|
//
|
519 |
|
// ORB_STATUS.current_orb = 0;
|
520 |
|
//
|
521 |
|
// ORBPORT |= ORBMASK; //start with all high
|
522 |
|
// OCR3C = TCNT3 + ORB_STATUS.changes[0].split_time_period; //wait for first split
|
523 |
|
//
|
524 |
|
//}
|
525 |
|
//
|
526 |
|
///**
|
527 |
|
// * @defgroup orbs Orbs
|
528 |
|
// * @brief Functions for controlling the color of the orbs.
|
529 |
|
// *
|
530 |
|
// * Functions for controlling the color and lighting of the orbs.
|
531 |
|
// *
|
532 |
|
// * @{
|
533 |
|
// **/
|
534 |
|
//
|
535 |
|
|
536 |
440 |
/**
|
537 |
441 |
* Set both orbs to the specified color. This function
|
538 |
442 |
* is intended to be used with the predefined
|
... | ... | |
694 |
598 |
//orbs_set (255, 127, 0, 0, 127, 255); // Pretty colors with extreme values
|
695 |
599 |
//orbs_set (0, 1, 2, 253, 254, 255); // Timing tests
|
696 |
600 |
|
|
601 |
orbs_set (255, 255, 255, 0, 0, 0);
|
|
602 |
delay_ms (1000);
|
|
603 |
|
|
604 |
while (1)
|
|
605 |
{
|
|
606 |
//orbs_set (250, 127, 3, 3, 127, 250); // Pretty colors
|
|
607 |
orbs_set (255, 255, 255, 1, 1, 1);
|
|
608 |
_delay_us(400);
|
|
609 |
}
|
|
610 |
|
697 |
611 |
// Test the time of the sorting routine
|
698 |
612 |
//while (1)
|
699 |
613 |
//{
|