Project

General

Profile

Revision 1104

Implemented lights sorting, interrupt handlers

View differences:

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