Revision 1104
Implemented lights sorting, interrupt handlers
branches/library_refactor/projects/libdragonfly/lights.c | ||
---|---|---|
58 | 58 |
*/ |
59 | 59 |
|
60 | 60 |
/* |
61 |
TODO: |
|
62 |
- do the inversion of flags when computing then, not in the ISR |
|
63 |
- do the timing right |
|
64 |
- handle 0 values (use the pwm_init_mask) |
|
65 |
- Determine sorting time, possibly improve sorting algorithm |
|
66 |
*/ |
|
67 |
|
|
68 |
/* |
|
61 | 69 |
* Random notes: |
62 | 70 |
* - Current motor frequency is 32 us/30 KHz |
63 | 71 |
* - AVR suckage: there is not timer mode with immediate OCR update and |
64 | 72 |
* overflow interrupt at TOP (CTC value) |
65 | 73 |
* - AVR suckage: Set on compare match/Clear on overflow not available with |
66 | 74 |
* non-PWM modes (especially not with immediate OCR update) |
75 |
* - Frequency is 120 Hz (8 ms) next lower (prescaler) is 30 Hz which flickers |
|
76 |
* Not that we could still use the slower prescaler and manually reload |
|
77 |
* after 127. This would still cost resolution, but 128 steps should be |
|
78 |
* enough. |
|
79 |
* - Overflow interrupt 2.5 us (0.03%), compare interrupts are 6*10us (when |
|
80 |
* using all different values) (0.75%) or 1*26 us (when using all same |
|
81 |
* values) |
|
82 |
* - Where to put the time base? |
|
83 |
* - buzzer => doesn't work because of varying frequency |
|
84 |
* - motors => possible? would trigger often (?) |
|
85 |
* - lights => must put lights on 16 bit timer (no OCR left) |
|
67 | 86 |
*/ |
68 | 87 |
|
69 | 88 |
#include "lights.h" |
... | ... | |
120 | 139 |
{ orb2_red_mask, orb2_green_mask, orb2_blue_mask } |
121 | 140 |
}; |
122 | 141 |
|
123 |
|
|
124 | 142 |
// *********** |
125 | 143 |
// ** Types ** |
126 | 144 |
// *********** |
... | ... | |
138 | 156 |
|
139 | 157 |
#define num_pwm_channels NUM_ORBS*NUM_COLORS |
140 | 158 |
struct pwm_channel_t pwm_channels[num_pwm_channels]; |
159 |
uint8_t orb_values[NUM_ORBS][NUM_COLORS]; |
|
141 | 160 |
uint8_t pwm_init_mask; |
161 |
// TODO use everywhere, add methods to switch on/off, add init function |
|
162 |
bool enable_orb_pwm=true; |
|
142 | 163 |
|
143 | 164 |
|
144 |
// ******************** |
|
145 |
// ** Initialization ** |
|
146 |
// ******************** |
|
147 |
|
|
148 |
/** |
|
149 |
* Initializes the PWM for Orb control. This must be called before |
|
150 |
* the orbs are used for them to function. |
|
151 |
**/ |
|
152 |
void orb_init () |
|
153 |
{ |
|
154 |
// Use 8 bit TC0. Timer mode: |
|
155 |
// We cannot use CTC mode because it can only clear on OCR0 (in contrast |
|
156 |
// to the 16 bit timers which can also use the ICR for that) and OCR0 is |
|
157 |
// already used for generating output compare interrupts. We also need |
|
158 |
// immediate (non double buffered) update of OCR0, so the only mode left |
|
159 |
// is "Normal". |
|
160 |
// Note that for a timer counting from 0 to 255, there are 256 states and |
|
161 |
// thus 257 output possibilities (0/256...256/256)! Possible ways to deal |
|
162 |
// with that: |
|
163 |
// 1. use a 16 bit variable for the PWM value (memory waste, overhead) |
|
164 |
// 2. use an additional flag for the 257th value (inconvenient) |
|
165 |
// 3. use 1/256...256/256 (skip 0, never complete off) |
|
166 |
// 4. use 0/256...256/256 (skip 256, never complete on) |
|
167 |
// 5. skip a value somewhere in the middle |
|
168 |
// For this implementation, variant 4 was chosen. |
|
169 |
|
|
170 |
// Enable the output ports and turn off the LEDs |
|
171 |
ORBDDR |= all_orbs_mask; |
|
172 |
ORBPORT |= all_orbs_mask; |
|
173 |
|
|
174 |
// Set all orbs to "off" |
|
175 |
orb_set (0, 0, 0); |
|
176 |
|
|
177 |
// *** Set up the timer |
|
178 |
|
|
179 |
// Normal mode, Compare match output off, Prescaler 1024 |
|
180 |
TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); |
|
181 |
|
|
182 |
// Enable compare match and overflow interrupts |
|
183 |
TIMSK=_BV(OCIE0) | _BV(TOIE0); |
|
184 |
|
|
185 |
// Debug |
|
186 |
DDRF=6; |
|
187 |
|
|
188 |
|
|
189 |
// The output compare flag (and interrupt) is set at the next timer clock |
|
190 |
// cycle after compare match. So time=pwm_value-1 (for pwm_value==0: don't |
|
191 |
// switch on at all) |
|
192 |
// ORB1: 255/127/1 |
|
193 |
// ORB2: 0/126/254 |
|
194 |
|
|
195 |
// Init mask: all masks where value>0 |
|
196 |
pwm_init_mask= |
|
197 |
orb1_red_mask | |
|
198 |
orb1_green_mask | |
|
199 |
orb1_blue_mask | |
|
200 |
orb2_green_mask | |
|
201 |
orb2_blue_mask |
|
202 |
; |
|
203 |
|
|
204 |
// Channels: value-1 |
|
205 |
pwm_channels[0].mask=orb2_red_mask ; pwm_channels[0].time= 10-1;//0 |
|
206 |
pwm_channels[1].mask=orb1_blue_mask ; pwm_channels[1].time= 20-1;//1 |
|
207 |
pwm_channels[2].mask=orb2_green_mask ; pwm_channels[2].time= 30-1;//126 |
|
208 |
pwm_channels[3].mask=orb1_green_mask ; pwm_channels[3].time=200-1;//127 |
|
209 |
pwm_channels[4].mask=orb2_blue_mask ; pwm_channels[4].time=220-1;//254 |
|
210 |
pwm_channels[5].mask=orb1_red_mask ; pwm_channels[5].time=230-1;//255 |
|
211 |
} |
|
212 |
|
|
213 |
|
|
214 | 165 |
// **************** |
215 | 166 |
// ** Timer ISRs ** |
216 | 167 |
// **************** |
... | ... | |
219 | 170 |
|
220 | 171 |
SIGNAL (SIG_OVERFLOW0) |
221 | 172 |
{ |
173 |
PORTF=2; |
|
222 | 174 |
// Turn all appropriate PWM channels on |
223 |
// TODO invert earlier |
|
224 | 175 |
ORBPORT&=~pwm_init_mask; |
225 | 176 |
|
226 | 177 |
// Start at the first channel |
... | ... | |
228 | 179 |
|
229 | 180 |
// Load the first OCR |
230 | 181 |
OCR0=pwm_channels[current_pwm_channel].time; |
182 |
PORTF=0; |
|
231 | 183 |
} |
232 | 184 |
|
233 | 185 |
SIGNAL(SIG_OUTPUT_COMPARE0) |
234 | 186 |
{ |
187 |
PORTF=4; |
|
235 | 188 |
// TODO: |
236 | 189 |
// - delayed interrupt |
237 |
// - identical values
|
|
190 |
// - synchronization OK?
|
|
238 | 191 |
|
239 |
// Turn the current channel off |
|
240 |
// TODO invert earlier |
|
241 |
ORBPORT|=pwm_channels[current_pwm_channel].mask; |
|
192 |
// If the interrupt is executed w/o delay, TCNT0 == time+1 (and TIME=OCR0) |
|
242 | 193 |
|
243 |
// Increment the channel |
|
244 |
current_pwm_channel++; |
|
194 |
// TODO improve (check overflow; maybe use return after last; fix |
|
195 |
// 0/1/254/255 values) |
|
196 |
while (TCNT0==pwm_channels[current_pwm_channel].time+1) |
|
197 |
{ |
|
198 |
// Turn the current channel off |
|
199 |
ORBPORT|=pwm_channels[current_pwm_channel].mask; |
|
245 | 200 |
|
246 |
// If there is a next channel, load its OCR value |
|
247 |
if (current_pwm_channel<=(num_pwm_channels-1)) |
|
248 |
if (pwm_channels[current_pwm_channel].time<255) |
|
249 |
OCR0=pwm_channels[current_pwm_channel].time; |
|
201 |
// Increment the channel |
|
202 |
current_pwm_channel++; |
|
203 |
|
|
204 |
// If there is a next channel, load its OCR value |
|
205 |
if (current_pwm_channel<=(num_pwm_channels-1)) |
|
206 |
if (pwm_channels[current_pwm_channel].time<255) |
|
207 |
OCR0=pwm_channels[current_pwm_channel].time; |
|
208 |
} |
|
209 |
PORTF=0; |
|
250 | 210 |
} |
251 | 211 |
|
252 | 212 |
|
253 | 213 |
|
254 |
// ************************ |
|
255 |
// ** Setting RGB colors **
|
|
256 |
// ************************ |
|
214 |
// ************************************
|
|
215 |
// ** Internal orb setting functions **
|
|
216 |
// ************************************
|
|
257 | 217 |
|
258 |
static void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) |
|
218 |
// TODO: make a public version of this one, but keep a private one which does |
|
219 |
// not sort them so you can update both sides and update only once. |
|
220 |
|
|
221 |
static void apply_orbs (void) |
|
259 | 222 |
{ |
260 |
// FIXME implement PWM code (this is only binary on/off) |
|
261 |
// FIXME Synchronization |
|
223 |
if (enable_orb_pwm) |
|
224 |
{ |
|
225 |
// PWM mode |
|
226 |
|
|
227 |
// Sort the orb values. |
|
228 |
|
|
229 |
// 1. Write the orb values and corresponding masks to the pwm channels |
|
230 |
// array unsorted |
|
231 |
for (uint8_t orb=0; orb<2; ++orb) |
|
232 |
{ |
|
233 |
for (uint8_t color=0; color<3; ++color) |
|
234 |
{ |
|
235 |
// TODO this should be faster w/o multiplication |
|
236 |
uint8_t index=NUM_COLORS*orb+color; |
|
237 |
pwm_channels[index].time=orb_values[orb][color]; |
|
238 |
pwm_channels[index].mask=orb_mask[orb][color]; |
|
239 |
} |
|
240 |
} |
|
241 |
|
|
242 |
// 2. Sort the values. Use bubble sort for now. |
|
243 |
for (uint8_t count=num_pwm_channels-1; count>0; --count) |
|
244 |
{ |
|
245 |
for (uint8_t i=num_pwm_channels-1; i>0; --i) |
|
246 |
{ |
|
247 |
if (pwm_channels[i].time<pwm_channels[i-1].time) |
|
248 |
{ |
|
249 |
uint8_t temp; |
|
250 |
|
|
251 |
// Swap the times |
|
252 |
temp=pwm_channels[i].time; |
|
253 |
pwm_channels[i].time=pwm_channels[i-1].time; |
|
254 |
pwm_channels[i-1].time=temp; |
|
255 |
|
|
256 |
// Swap the masks |
|
257 |
temp=pwm_channels[i].mask; |
|
258 |
pwm_channels[i].mask=pwm_channels[i-1].mask; |
|
259 |
pwm_channels[i-1].mask=temp; |
|
260 |
} |
|
261 |
} |
|
262 |
} |
|
262 | 263 |
|
263 |
// Oh, and of course the outputs are inverted. |
|
264 |
if (!red) ORBPORT|=orb_mask[num][0]; else ORBPORT&=~orb_mask[num][0]; |
|
265 |
if (!green) ORBPORT|=orb_mask[num][1]; else ORBPORT&=~orb_mask[num][1]; |
|
266 |
if (!blue) ORBPORT|=orb_mask[num][2]; else ORBPORT&=~orb_mask[num][2]; |
|
264 |
pwm_init_mask=all_orbs_mask; // FIXME use real values |
|
265 |
|
|
266 |
// Sort the values from orb_values[NUM_ORBS][NUM_COLORS]. |
|
267 |
|
|
268 |
} |
|
269 |
else |
|
270 |
{ |
|
271 |
// Binary mode. |
|
272 |
// Don't do anything, the orbs pins are set in orb_n_set. |
|
273 |
// It would be more consistent to set them here (because you could |
|
274 |
// update them independently and then apply the changes at once), but it |
|
275 |
// is faster this way, and being fast is the whole point of using the |
|
276 |
// binary orb mode anyway. |
|
277 |
} |
|
267 | 278 |
} |
268 | 279 |
|
280 |
static void orb_n_set (uint8_t num, uint8_t red, uint8_t green, uint8_t blue) |
|
281 |
{ |
|
282 |
if (enable_orb_pwm) |
|
283 |
{ |
|
284 |
// PWM mode |
|
285 |
orb_values[num][0]=red; |
|
286 |
orb_values[num][1]=green; |
|
287 |
orb_values[num][2]=blue; |
|
288 |
} |
|
289 |
else |
|
290 |
{ |
|
291 |
// Binary mode |
|
292 |
// The outputs are inverted. |
|
293 |
if (!red) ORBPORT|=orb_mask[num][0]; else ORBPORT&=~orb_mask[num][0]; |
|
294 |
if (!green) ORBPORT|=orb_mask[num][1]; else ORBPORT&=~orb_mask[num][1]; |
|
295 |
if (!blue) ORBPORT|=orb_mask[num][2]; else ORBPORT&=~orb_mask[num][2]; |
|
296 |
} |
|
297 |
} |
|
298 |
|
|
299 |
// ************************************ |
|
300 |
// ** Frontend orb setting functions ** |
|
301 |
// ************************************ |
|
302 |
|
|
303 |
// All of these functions use orb_n_set to set the actual values, and then call |
|
304 |
// apply_orbs() to apply the changes. orb_n_set should be used (although it |
|
305 |
// would be faster to set the array directly) because the binary/pwm mode has |
|
306 |
// to be handled. |
|
307 |
|
|
269 | 308 |
/** |
270 | 309 |
* Set orb1 to the color specified. orb_init must be called before this function |
271 | 310 |
* may be used. |
... | ... | |
279 | 318 |
void orb1_set (uint8_t red, uint8_t green, uint8_t blue) |
280 | 319 |
{ |
281 | 320 |
orb_n_set (0, red, green, blue); |
321 |
apply_orbs (); |
|
282 | 322 |
} |
283 | 323 |
|
284 | 324 |
/** |
... | ... | |
294 | 334 |
void orb2_set (uint8_t red, uint8_t green, uint8_t blue) |
295 | 335 |
{ |
296 | 336 |
orb_n_set (1, red, green, blue); |
337 |
apply_orbs (); |
|
297 | 338 |
} |
298 | 339 |
|
299 | 340 |
/** |
... | ... | |
308 | 349 |
**/ |
309 | 350 |
void orb_set (uint8_t red, uint8_t green, uint8_t blue) |
310 | 351 |
{ |
311 |
orb1_set (red, green, blue); |
|
312 |
orb2_set (red, green, blue); |
|
352 |
orb_n_set (0, red, green, blue); |
|
353 |
orb_n_set (1, red, green, blue); |
|
354 |
apply_orbs (); |
|
313 | 355 |
} |
314 | 356 |
|
357 |
void orbs_set ( |
|
358 |
uint8_t red1, uint8_t green1, uint8_t blue1, |
|
359 |
uint8_t red2, uint8_t green2, uint8_t blue2) |
|
360 |
{ |
|
361 |
orb_n_set (0, red1, green1, blue1); |
|
362 |
orb_n_set (1, red2, green2, blue2); |
|
363 |
apply_orbs (); |
|
364 |
} |
|
315 | 365 |
|
316 | 366 |
|
317 | 367 |
|
368 |
|
|
318 | 369 |
////////////////////////////////////////////////////////////////////////////////////////////////////////// |
319 | 370 |
|
320 | 371 |
//#define ORB_RESET 1025 |
... | ... | |
572 | 623 |
|
573 | 624 |
/** @} **/ //end group |
574 | 625 |
|
626 |
|
|
627 |
// ******************** |
|
628 |
// ** Initialization ** |
|
629 |
// ******************** |
|
630 |
|
|
631 |
/** |
|
632 |
* Initializes the PWM for Orb control. This must be called before |
|
633 |
* the orbs are used for them to function. |
|
634 |
**/ |
|
635 |
void orb_init () |
|
636 |
{ |
|
637 |
// Use 8 bit TC0. Timer mode: |
|
638 |
// We cannot use CTC mode because it can only clear on OCR0 (in contrast |
|
639 |
// to the 16 bit timers which can also use the ICR for that) and OCR0 is |
|
640 |
// already used for generating output compare interrupts. We also need |
|
641 |
// immediate (non double buffered) update of OCR0, so the only mode left |
|
642 |
// is "Normal". |
|
643 |
// Note that for a timer counting from 0 to 255, there are 256 states and |
|
644 |
// thus 257 output possibilities (0/256...256/256)! Possible ways to deal |
|
645 |
// with that: |
|
646 |
// 1. use a 16 bit variable for the PWM value (memory waste, overhead) |
|
647 |
// 2. use an additional flag for the 257th value (inconvenient) |
|
648 |
// 3. use 1/256...256/256 (skip 0, never complete off) |
|
649 |
// 4. use 0/256...256/256 (skip 256, never complete on) |
|
650 |
// 5. skip a value somewhere in the middle |
|
651 |
// For this implementation, variant 4 was chosen. |
|
652 |
// Using and 8 bit timer has the added advantage that all the comparisons |
|
653 |
// are faster. |
|
654 |
|
|
655 |
// Enable the output ports and turn off the LEDs |
|
656 |
ORBDDR |= all_orbs_mask; |
|
657 |
ORBPORT |= all_orbs_mask; |
|
658 |
|
|
659 |
// Set all orbs to "off" |
|
660 |
orb_set (0, 0, 0); |
|
661 |
|
|
662 |
// *** Set up the timer |
|
663 |
|
|
664 |
// Normal mode, Compare match output off, Prescaler |
|
665 |
TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); // 1024, 30 Hz |
|
666 |
TCCR0=_BV(CS02) | _BV(CS01); // 1024, 30 Hz |
|
667 |
|
|
668 |
// Enable compare match and overflow interrupts |
|
669 |
TIMSK=_BV(OCIE0) | _BV(TOIE0); |
|
670 |
|
|
671 |
// Debug |
|
672 |
DDRF=6; |
|
673 |
|
|
674 |
|
|
675 |
// The output compare flag (and interrupt) is set at the next timer clock |
|
676 |
// cycle after compare match. So time=pwm_value-1 (for pwm_value==0: don't |
|
677 |
// switch on at all) |
|
678 |
// ORB1: red |
|
679 |
// ORB2: green |
|
680 |
|
|
681 |
// Left: greenish red, Right: greenish blue |
|
682 |
// For testing, set some pretty colors |
|
683 |
orbs_set (250, 127, 3, 3, 127, 250); |
|
684 |
//orbs_set (252, 129, 5, 3, 127, 250); |
|
685 |
//orbs_set (127, 127, 127, 127, 127, 127); |
|
686 |
|
|
687 |
// // Init mask: all masks where value>0 |
|
688 |
// pwm_init_mask= |
|
689 |
// orb1_red_mask | |
|
690 |
// orb1_green_mask | |
|
691 |
// orb1_blue_mask | |
|
692 |
// orb2_green_mask | |
|
693 |
// orb2_blue_mask |
|
694 |
// ; |
|
695 |
// |
|
696 |
// // Channels: value-1 |
|
697 |
// pwm_channels[0].mask=orb2_red_mask ; pwm_channels[0].time= 3-1;//0 |
|
698 |
// pwm_channels[1].mask=orb1_blue_mask ; pwm_channels[1].time= 3-1;//1 |
|
699 |
// pwm_channels[2].mask=orb2_green_mask ; pwm_channels[2].time=127-1;//126 |
|
700 |
// pwm_channels[3].mask=orb1_green_mask ; pwm_channels[3].time=127-1;//127 |
|
701 |
// pwm_channels[4].mask=orb2_blue_mask ; pwm_channels[4].time=250-1;//254 |
|
702 |
// pwm_channels[5].mask=orb1_red_mask ; pwm_channels[5].time=250-1;//255 |
|
703 |
} |
|
704 |
|
|
705 |
|
Also available in: Unified diff