Revision 1102
Basic orb PWM functionality on Timer0
branches/library_refactor/behaviors/library_test/main.c | ||
---|---|---|
78 | 78 |
|
79 | 79 |
//DDRF=2; |
80 | 80 |
|
81 |
//motors_init (); |
|
82 |
//motor2_set (FORWARD, 64); |
|
83 |
|
|
81 | 84 |
//orb_init (); |
82 | 85 |
//orb_enable (); |
83 | 86 |
|
... | ... | |
85 | 88 |
//delay_ms (1000); |
86 | 89 |
while (1) |
87 | 90 |
{ |
88 |
orb_tick (0); |
|
89 | 91 |
delay_ms (1000); |
90 | 92 |
} |
91 | 93 |
|
branches/library_refactor/projects/libdragonfly/lights.c | ||
---|---|---|
57 | 57 |
|
58 | 58 |
*/ |
59 | 59 |
|
60 |
/* |
|
61 |
* Random notes: |
|
62 |
* - Current motor frequency is 32 us/30 KHz |
|
63 |
* - AVR suckage: there is not timer mode with immediate OCR update and |
|
64 |
* overflow interrupt at TOP (CTC value) |
|
65 |
* - AVR suckage: Set on compare match/Clear on overflow not available with |
|
66 |
* non-PWM modes (especially not with immediate OCR update) |
|
67 |
*/ |
|
68 |
|
|
60 | 69 |
#include "lights.h" |
61 | 70 |
#include <avr/interrupt.h> |
62 | 71 |
#include "dragonfly_lib.h" |
... | ... | |
81 | 90 |
#define ORB2_GREEN 5 |
82 | 91 |
#define ORB2_BLUE 6 |
83 | 92 |
|
93 |
|
|
84 | 94 |
// *********** |
85 | 95 |
// ** Masks ** |
86 | 96 |
// *********** |
... | ... | |
115 | 125 |
// ** Types ** |
116 | 126 |
// *********** |
117 | 127 |
|
118 |
struct pwm_channel |
|
128 |
struct pwm_channel_t
|
|
119 | 129 |
{ |
120 | 130 |
uint8_t time; |
121 | 131 |
uint8_t mask; |
... | ... | |
126 | 136 |
// ** Variables ** |
127 | 137 |
// *************** |
128 | 138 |
|
129 |
struct pwm_channel channels[NUM_ORBS*NUM_COLORS]; |
|
139 |
#define num_pwm_channels NUM_ORBS*NUM_COLORS |
|
140 |
struct pwm_channel_t pwm_channels[num_pwm_channels]; |
|
141 |
uint8_t pwm_init_mask; |
|
130 | 142 |
|
131 | 143 |
|
132 | 144 |
// ******************** |
133 | 145 |
// ** Initialization ** |
134 | 146 |
// ******************** |
135 | 147 |
|
136 |
static void dset(uint8_t num) |
|
137 |
{ |
|
138 |
PORTF|=1<<num; |
|
139 |
} |
|
140 |
|
|
141 |
static void dclear(uint8_t num) |
|
142 |
{ |
|
143 |
PORTF&=~(1<<num); |
|
144 |
} |
|
145 |
|
|
146 | 148 |
/** |
147 | 149 |
* Initializes the PWM for Orb control. This must be called before |
148 | 150 |
* the orbs are used for them to function. |
149 | 151 |
**/ |
150 | 152 |
void orb_init () |
151 | 153 |
{ |
152 |
// Timer mode: |
|
153 |
// OCRnx update "immediate" is required. This leaves "Normal" mode and the |
|
154 |
// "CTC" modes. "Normal" doesn't allow setting a reload value, and the |
|
155 |
// prescaler is too limited. So CTC is used. CTC can match to OCRnA and |
|
156 |
// ICRn. For the motor, OCRnA is already in use for the compare match |
|
157 |
// output unit. So ICRn is used. |
|
158 |
// This leaves: WGMn3:0=1100. |
|
159 |
// This mode does not provide an overflow interrupt, so Output Compare |
|
160 |
// has to be used instead. |
|
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. |
|
161 | 169 |
|
162 | 170 |
// Enable the output ports and turn off the LEDs |
163 | 171 |
ORBDDR |= all_orbs_mask; |
164 |
ORBPORT &= ~all_orbs_mask;
|
|
172 |
ORBPORT |= all_orbs_mask;
|
|
165 | 173 |
|
166 | 174 |
// Set all orbs to "off" |
167 | 175 |
orb_set (0, 0, 0); |
168 | 176 |
|
169 |
// Set up the timer |
|
170 |
// Prescaler 8 is 66 ms or 15 Hz (8 MHz/prescaler 8/2^16) |
|
171 |
|
|
172 |
|
|
173 |
|
|
174 |
TCCR3A=0; |
|
175 |
TCCR3B=_BV(CS31); // Prescaler 8 |
|
176 |
//TCCR3B=_BV(CS31) | _BV(CS30); // Prescaler 64 |
|
177 |
TCCR3C=0; |
|
178 |
ETIMSK |= _BV(TOIE3) | _BV(OCIE3C); // Enable Overflow Interrupt and Output compare 3 interrupt |
|
179 |
|
|
180 |
// Clear Timer on Compare match: |
|
181 |
// Either from OCR3A (WGM3 3:0=4) or from ICR3 (WGM3 3:0=12 |
|
177 |
// *** Set up the timer |
|
182 | 178 |
|
183 |
// DOING: Select a good reload so |
|
184 |
// - there are 256 values (not so important) |
|
185 |
// - the refresh rate is around 20..25 Hz |
|
186 |
// Then, check if it can be co-used w/ the compare match output unit. |
|
187 |
|
|
188 |
ICR3=10000; // Refresh rate (period): 10ms |
|
189 |
OCR3C=0; |
|
190 |
|
|
191 |
//TCCR3A |= _BV(WGM31); |
|
192 |
TCCR3B |= _BV(WGM33) | _BV(WGM32); |
|
193 |
|
|
194 |
// TOP is 6, counter counts 0 1 2 3 4 5 6 (7 intervals) => 8 possibilities |
|
195 |
// OCR=0 => 1/8, OCR=7 => 8/8 |
|
196 |
|
|
197 |
//OCR3C=1; // Compare after (?) 1 |
|
179 |
// Normal mode, Compare match output off, Prescaler 1024 |
|
180 |
TCCR0=_BV(CS02) | _BV(CS01) | _BV(CS00); |
|
198 | 181 |
|
182 |
// Enable compare match and overflow interrupts |
|
183 |
TIMSK=_BV(OCIE0) | _BV(TOIE0); |
|
184 |
|
|
199 | 185 |
// Debug |
200 | 186 |
DDRF=6; |
201 | 187 |
|
202 |
// In PWM mode, the OCR is double buffered (updated at TOP or BOTTOM) |
|
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 |
|
203 | 194 |
|
204 |
// //init timer3 |
|
205 |
// TCCR3A = 0; |
|
206 |
// TCCR3B = _BV(CS31); //prescale = 8 |
|
207 |
// TCCR3C = 0; |
|
208 |
// ETIMSK |= _BV(OCIE3C); //turn on oc3c interrupt |
|
209 |
// OCR3C = TCNT3+ORB_RESET; |
|
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 |
|
210 | 211 |
} |
211 |
// |
|
212 | 212 |
|
213 | 213 |
|
214 | 214 |
// **************** |
215 | 215 |
// ** Timer ISRs ** |
216 | 216 |
// **************** |
217 | 217 |
|
218 |
volatile uint8_t x; |
|
219 |
volatile uint8_t i; |
|
218 |
volatile uint8_t current_pwm_channel=0; |
|
220 | 219 |
|
221 |
void orb_tick (uint8_t t)
|
|
220 |
SIGNAL (SIG_OVERFLOW0)
|
|
222 | 221 |
{ |
223 |
x++; |
|
222 |
// Turn all appropriate PWM channels on |
|
223 |
// TODO invert earlier |
|
224 |
ORBPORT&=~pwm_init_mask; |
|
225 |
|
|
226 |
// Start at the first channel |
|
227 |
current_pwm_channel=0; |
|
228 |
|
|
229 |
// Load the first OCR |
|
230 |
OCR0=pwm_channels[current_pwm_channel].time; |
|
224 | 231 |
} |
225 | 232 |
|
226 |
SIGNAL (SIG_OVERFLOW3)
|
|
233 |
SIGNAL(SIG_OUTPUT_COMPARE0)
|
|
227 | 234 |
{ |
228 |
} |
|
235 |
// TODO: |
|
236 |
// - delayed interrupt |
|
237 |
// - identical values |
|
238 |
|
|
239 |
// Turn the current channel off |
|
240 |
// TODO invert earlier |
|
241 |
ORBPORT|=pwm_channels[current_pwm_channel].mask; |
|
229 | 242 |
|
230 |
// Timer values: 0 1 2 3 4 5 6 7 8 9 |
|
243 |
// Increment the channel |
|
244 |
current_pwm_channel++; |
|
231 | 245 |
|
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; |
|
250 |
} |
|
232 | 251 |
|
233 | 252 |
|
234 |
SIGNAL (SIG_OUTPUT_COMPARE3C) |
|
235 |
{ |
|
236 |
if (i==0) |
|
237 |
{ |
|
238 |
dset (1); |
|
239 |
dset (2); |
|
240 |
|
|
241 |
i=1; |
|
242 |
OCR3C=1000; |
|
243 |
} |
|
244 |
else if (i==1) |
|
245 |
{ |
|
246 |
dclear (1); |
|
247 |
|
|
248 |
OCR3C=2000; |
|
249 |
i=2; |
|
250 |
} |
|
251 |
else if (i==2) |
|
252 |
{ |
|
253 |
dclear (2); |
|
254 |
|
|
255 |
OCR3C=0; |
|
256 |
i=0; |
|
257 |
} |
|
258 |
} |
|
259 | 253 |
|
260 |
|
|
261 | 254 |
// ************************ |
262 | 255 |
// ** Setting RGB colors ** |
263 | 256 |
// ************************ |
Also available in: Unified diff