Initial commit for stepper controller
[radiotelescope.git] / stepper_controller / arduino / stepper / bsp.c
1 //\r
2 // Product: UCC Radiotelescope - Stepper Motor Controller\r
3 // Version: 0.1\r
4 // Date:    5 November 2010\r
5 //\r
6 //                                          +-----------------------+\r
7 //                      |   d e c i s i o n s   |\r
8 //                                          +-----------------------+\r
9 //                                          | a n d   d e s i g n s |\r
10 //                      +-----------------------+\r
11 //\r
12 // Copyright (C) 2010 Decisions and Designs Pty Ltd. All rights reserved.\r
13 //\r
14 // This software may be distributed and modified under the terms of the GNU\r
15 // General Public License version 2 (GPL) as published by the Free Software\r
16 // Foundation and appearing in the file GPL.TXT included in the packaging of\r
17 // this file. Please note that GPL Section 2[b] requires that all works based\r
18 // on this software must also be made publicly available under the terms of\r
19 // the GPL ("Copyleft").\r
20 //\r
21 // Contact information:\r
22 // Decisions and Designs Web site: http://www.decisions-and-designs.com.au\r
23 // e-mail:                         [email protected]\r
24 //\r
25 \r
26 #include "radiotelescope.h"\r
27 #include "qpn_port.h"\r
28 #include "bsp.h"\r
29 #include "signals.h"\r
30 #include "stepper.h"\r
31 #include <avr/io.h>\r
32 \r
33 /*\r
34  * All comments containing ATmega1280 "Section number and description"\r
35  * refer to the Atmel datasheet:\r
36  * ATmega640/1280/1281/2560/2561 Rev. 2549M-09/10\r
37  * available from (on 5 November 2010):\r
38  * www.atmel.com/dyn/resources/prod_documents/doc2549.PDF\r
39  *\r
40  * The following stepper driver code uses two hardware pins to drive both sides\r
41  * of an opto-transistor LED for 10mA nominal LED current:         \r
42  *                             \r
43  *                 |                    LED (one half of optotransistor)   \r
44  *      "OCB" pin  |----------/\/\/\---->|-----\r
45  * ATmega1280 MCU  |           270R           |\r
46  *      "OCA" pin  |---------------------------\r
47  *                 |\r
48  *\r
49  * This takes advantage of the AVR timer output compare logic which can be\r
50  * configured to set MCU pins high or low when the timer matches an output\r
51  * compare (OC) register; each timer has three OCs: OCA, OCB, and OCC. This\r
52  * driver uses OCA and OCB to adjust two pins so their difference is the\r
53  * active (LED has current) period for the opto-transistor:\r
54  * \r
55  * OCA pin ```````\_______________________/```````\r
56  * OCB pin ````````````````````````\_______/``````\r
57  * LED active ~~~~~XXXXXXXXXXXXXXXX~~~~~~~~~~~~~~~\r
58  *\r
59  * There are two identical drivers using Timer 3 and Timer 4 respectively.\r
60  * Each driver accepts a uint32_t step count argument from which the upper\r
61  * 16 bits are consumed as rollovers of the 16 bit hardware counter before\r
62  * the final (lower) 16 step count bits are consumed in the hardware counter.\r
63  * In both the rollover and final count, the timer output compare register\r
64  * OCA either generates interrupts on rollover or (for the final count)\r
65  * confgures the OCA latch to drive the corresponding output pin (active)\r
66  * low to initiate the step pulse. During the final count, the OCB latch\r
67  * has also been configured to drive it's corresponding output pin (inactive)\r
68  * low to complete the step pulse. Both pins are subsequently forced high to\r
69  * prepare for the next step pulse. The advantage of this design is that\r
70  * all the step pulse periods are fixed length (defined by the hardware\r
71  * timer behaviour) and setup requirements for subsequent step periods are\r
72  * easier to guarantee (less dependence on ISR delays). As the ATmega1280\r
73  * datasheet notes however, there is significant CPU overhead to manage the\r
74  * stepper signals in this way, see:\r
75  * ATmega1280 "16.9.1 Normal Mode".\r
76  *\r
77  * The following diagram shows the rollover periods, final count, the\r
78  * step pulse output period, and output restoration ready for the next step\r
79  * pulse:\r
80  *            1                          2                3       4\r
81  *       final rollover            final count         end pulse                    \r
82  *           OCA                        OCA                  OCB       \r
83  *           ISR                   empty ISR call         |--ISR--|\r
84  * \r
85  * OCA pin ``````````````````````````````\_______________________/```````\r
86  * OCB pin ```````````````````````````````````````````````\_______/``````\r
87  * LED active ~~~~~~~~~~~~~~~~~~~~~~~~~~~~XXXXXXXXXXXXXXXX~~~~~~~~~~~~~~~\r
88  *\r
89  * 1. After consuming all of the upper 16 bits of the counter, a final count\r
90  *    period remains in the lower 16 bits of the step count argument. Advance\r
91  *    OCA by the remaining step count. Also advance OCB beyond OCA by the\r
92  *    pulse period; all is ready for the hardware pulse between 2. and 3.\r
93  * 2. The hardware pulse is initiated here but only the OCB interrupt is\r
94  *    enabled so OCA ISR does not get called.\r
95  * 3. If another step pulse is queued for the driver, OCA is advanced (if\r
96  *    the step has no rollover component) or OCA interrupt is enabled for\r
97  *    rollovers. The main timer free runs so all rollovers and advancement\r
98  *    of OCA means each falling edge maintains the correct step period\r
99  *    regardless of when the ISR is called to set up the next step period.\r
100  *    The next step time set up occurs in OCB ISR so the ISR must respond\r
101  *    within MIN_SETUP_TIME-PULSE_TIME (see definitions below).\r
102  * 4. The hardware pulse is de-activated when OCB pin goes inactive high.\r
103  *    \r
104  *    Radiotelescope application:\r
105  *    ---------------------------\r
106  *    The RTA Pavia GMD 04 stepper driver board has a maximum step frequency\r
107  *    of 50KHz, clocks on the falling edge of the step input (when the opto\r
108  *    LED goes active) and suggests a 50% step clock duty cycle. See:\r
109  *    http://wiki.ucc.asn.au/RadioTelescope?action=AttachFile&do=view&target=GDMman.pdf\r
110  *    This implies that the minimum pulse period is 10 uSec so it should be\r
111  *    ok to use a 100 uSec pulse and allow for a maximum stepping period of\r
112  *    1 mSec until further performance tests are conducted.\r
113  *    In order to achieve smooth (queued) stepping, the QP stepper tasks\r
114  *    need to respond to a signal from OCB ISR to request the next queued\r
115  *    step period before the OCA pin change on final OCA (see step 2 above).\r
116  *    Because the non-premptive QP kernel is being used, all RTC code in the\r
117  *    QP tasks must run within MIN_SETUP_TIME-PULSE_TIME; or 900 uSec which\r
118  *    is feasible.\r
119  *\r
120  *    For opto-isolated hardware connection to the GDM 04, AVR pins can\r
121  *    source and sink at least 10mA; see:\r
122  *    ATmega1280 "30.1 DC Characteristics" symbols V(OL), V(OH).\r
123  *    Choose an optotransistor which can operate with 10mA LED current for\r
124  *    the required optotransistor fall time. Connect the optotransistor\r
125  *    to the stepper drive module inputs (with [TBD] external pull-up resistor\r
126  *    if required). Thus the AVR pins drive the LED into an active (lit) state\r
127  *    at the start of the step pulse period and the phototransistor conducts\r
128  *    pulling the GDM 04 stepper driver tesp input low for a falling edge\r
129  *    stepper clock; see (from wiki link above):\r
130  *    GDMman.pdf, "4 - INPUT AND OUTPUT LOGIC SIGNALS", page 7.\r
131  *    TODO See schematic of interface in project file stepper_iface.pdf\r
132  */   \r
133 \r
134 #define FINAL 1\r
135 #define TIMER_MODE_NORMAL 0\r
136 \r
137 /* ATmega1280, "16.3 Accessing 16-bit Registers" \r
138    reads:\r
139    "To do a 16-bit write, the high byte must be written before the low byte.\r
140     For a 16-bit read, the low byte must be read before the high byte." */\r
141 #define forwardOf(DEST, SRC, OSET) \\r
142     do { \\r
143         uint16_t temp; \\r
144         temp  = SRC ## L ; \\r
145         temp += (SRC ## H) << 8 ; \\r
146         temp += (OSET); \\r
147         DEST ## H = temp >> 8; \\r
148         DEST ## L = temp & 0xff; \\r
149     } \\r
150     while (0)\r
151 \r
152 /*..........................................................................*/\r
153 /* Timer globals */\r
154 QActive *count3AO = (QActive *)0;\r
155 uint32_t count3, nextCount3;\r
156 \r
157 QActive *count4AO = (QActive *)0;\r
158 uint32_t count4, nextCount4;\r
159 /*..........................................................................*/\r
160 void BSP_initStepTimer3(QActive *me)\r
161 {\r
162     count3AO = me;\r
163     nextCount3 = 0;\r
164     count3 = 0;\r
165     /* Disable power reduction for timer3; see\r
166        ATmega1280 "11.9.3 PRR1 - Power Reduction Register 1" */\r
167     PRR1 &= ~(1 << PRTIM3); \r
168     /* Disable (clocking of) the timer; see ATmega1280\r
169        "17.11.8 TCCR5B - Timer/Counter 5 Control Register B" */\r
170     TCCR3B = 0;\r
171     /* Set COM3A and COM3B to drive both OC latches high; see\r
172        ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
173     TCCR3A =   (1 << COM3A1) | (1 << COM3A0) \r
174              | (1 << COM3B1) | (1 << COM3B0) \r
175              | TIMER_MODE_NORMAL;\r
176     /* Force OC latches high; see ATmega1280\r
177        "17.11.12 TCCR5C - Timer/Counter 5 Control Register C" */\r
178     TCCR3C = (1 << FOC3A) | (1 << FOC3B);\r
179     /* Disable OC as pin source and drive pins with PORT latch */\r
180     TCCR3A = TIMER_MODE_NORMAL;\r
181     /* Set OCA and OCB PORT bits high; OC3A is PE3 and OC3B is PE4 */\r
182     PORTE |= (1 << PE3) | (1 << PE4);\r
183     /* Set DDR to drive the PORT bits for OCA and OCB pins */\r
184     DDRE |= (1 << DDE3) | (1 << DDE4);\r
185     /* Reenable timer with clkI/O (no prescaling) */\r
186     TCCR3B = 1 << CS30; \r
187     return;\r
188 }\r
189 /*..........................................................................*/\r
190 uint8_t BSP_nextStepTimer3(uint32_t count)\r
191 {\r
192     uint8_t register sreg;\r
193 \r
194     /* There is a minimum setup time so error on short counts */    \r
195     if (count < MIN_SETUP_TIME)\r
196         return 1;\r
197     /* A count is already queued so error */\r
198     if (nextCount3)\r
199         return 2;\r
200     sreg = SREG; /* save interrupt and other status */\r
201     cli(); /* and disable interrupts */\r
202     /* If step timer is still running, leave it to the ISR to load\r
203        the next step time */\r
204     if (count3) {\r
205         nextCount3 = count; /* queue the count for ISR to load */\r
206         SREG = sreg; /* restore interrupts */\r
207         return 0;\r
208     }\r
209     SREG = sreg; /* restore interrupts */\r
210     /* Request next step time from HSM */\r
211     if (count3AO) {\r
212         QActive_postISR(count3AO, NEXT_STEP_TIME_SIG, 0);\r
213     }\r
214     /* If the last OCA period is too short, run "half" in the first OCA count\r
215        so the final count is withing setup time limits */\r
216     if ((count & 0x0000ffff) < MIN_SETUP_TIME) {\r
217         count3 = count + 0x10000 - (MID_SET_TIME - MIN_SETUP_TIME);\r
218         /* Set OCA3 forward of the current TCNT3 */\r
219         forwardOf(OCR3A, TCNT3, (MID_SET_TIME - MIN_SETUP_TIME));        \r
220     }\r
221     /* If the last OCA period extends the OCB past rollover then\r
222        run "half" in the first OCA count */\r
223     else if ((count & 0x0000ffff) > MAX_SETUP_TIME) {\r
224         count3 = count + 0x10000 - (MAX_SETUP_TIME - MID_SET_TIME); \r
225         /* Set OCA3 forward of the current TCNT3 */\r
226         forwardOf(OCR3A, TCNT3, (MAX_SETUP_TIME - MID_SET_TIME));    \r
227     }\r
228     /* Final count period */\r
229     else if (!(count & 0xffff0000)) {\r
230         /* Flag nextStepTimer3() that the last time is still running\r
231            so nextCount3 is queued */\r
232         count3 = FINAL;\r
233         /* Set up the last OCRA period and OCRB pulse period */\r
234         forwardOf(OCR3A, TCNT3, (uint16_t)count);\r
235         forwardOf(OCR3B, OCR3A, PULSE_TIME);\r
236         /* Clear stale OC interrupt flags; see ATmega1280\r
237            "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
238         TIFR3 = (1 << OCF3B) | (1 << OCF3A);\r
239         /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
240            see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
241         TCCR3A = (1 << COM3A1) | (1 << COM3B1) | TIMER_MODE_NORMAL;\r
242         /* Enable the OCA interrupt to discard the interrupt during the final count.\r
243            Enable OCB interrupt to reload at end of pulse */\r
244         TIMSK3 = (1 << OCIE3A) | (1 << OCIE3B);\r
245         return 0;\r
246     }\r
247     /* Start rollover period */\r
248     else {\r
249         count3 = count;\r
250         forwardOf(OCR3A, TCNT3, 0);\r
251     }\r
252     /* Clear stale OCA interrupt flag; see ATmega1280\r
253        "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
254      TIFR3 = 1 << OCF3A;\r
255     /* For all non-FINAL timing, only enable OCA interrupt; see ATmega1280\r
256        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
257     TIMSK3 = 1 << OCIE3A;\r
258     return 0;\r
259 }\r
260 /*..........................................................................*/\r
261 /* For information about AVR Interrupts see:\r
262    http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html\r
263    OC3A Interrupt handler */\r
264 ISR(TIMER3_COMPA_vect) {\r
265     /* Adjust timer based on time to run */\r
266     if (count3 & 0xffff0000) {\r
267         count3 -= 0x10000;\r
268         if (count3 & 0xffff0000) {\r
269             /* More OCA rollovers yet */\r
270             return;\r
271         } \r
272     } else {\r
273         /* The OCA interrupt flag has been cleared on vector into here,\r
274            ISR(TIMER3_COMPA_vect), see: ATmega1280\r
275            "16.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register", but no\r
276            further action is needed. This EMPTY_INTERRUPT behaviour is to\r
277            avoid having to clear a stale OCA pending interrupt and potentially\r
278            discard an OCA on the next step time setup in ISR(TIMER3_COMPB_vect)\r
279          */\r
280         if (count3 == FINAL) {\r
281             return;\r
282         }\r
283     }\r
284     /* Last OCA time for this step. Setup to pulse opto using OCA and OCB */\r
285     forwardOf(OCR3A, OCR3A, (uint16_t)count3); \r
286     forwardOf(OCR3B, OCR3A, PULSE_TIME);\r
287     /* Maintain flag for nextStepTimer3() to queue any request */\r
288     count3 = FINAL;\r
289     /* Clear stale OCB interrupt flag; see ATmega1280\r
290        "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
291     TIFR3 = 1 << OCF3B;\r
292     /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
293        see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
294     TCCR3A = (1 << COM3A1) | (1 << COM3B1) | TIMER_MODE_NORMAL;\r
295     /* Enable the OCA interrupt to discard the interrupt during the final count.\r
296        Enable OCB interrupt to reload at end of pulse; see\r
297        ATmega1280 "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register". */\r
298     TIMSK3 = (1 << OCIE3A) | (1 << OCIE3B);\r
299     return;\r
300 }\r
301 /*..........................................................................*/\r
302 /* OC3B Interrupt handler */\r
303 ISR(TIMER3_COMPB_vect) {\r
304     /* Set COM3A and COM3B to drive both OC latches high; see\r
305        ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
306     TCCR3A =   (1 << COM3A1) | (1 << COM3A0) \r
307              | (1 << COM3B1) | (1 << COM3B0) \r
308              | TIMER_MODE_NORMAL;\r
309     /* Force OC latches high; see\r
310        ATmega1280 "17.11.12 TCCR5C - Timer/Counter 5 Control Register C"\r
311        Force OCA first followed by OCB so the LED extinguishes "glitch-free"\r
312        TODO Consider risk of reverse bias of the LED for the interval */\r
313     TCCR3C = (1 << FOC3A);\r
314     TCCR3C = (1 << FOC3B);\r
315     /* Disable OC as pin source and drive pins with PORT latch */\r
316     TCCR3A = TIMER_MODE_NORMAL;\r
317     /* Disable OCA and OCB interrupt; see ATmega1280\r
318        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
319     TIMSK3 = 0;\r
320     /* If no queued step time, idle and restart timer from nextStepTimer3() */\r
321     if (!nextCount3) {\r
322         count3 = 0;\r
323         return;\r
324     }\r
325     /* Else consume queued step time */\r
326     count3 = nextCount3;\r
327     nextCount3 = 0;\r
328     /* Request next step time from HSM */\r
329     if (count3AO) {\r
330         QActive_postISR(count3AO, NEXT_STEP_TIME_SIG, 0);\r
331     }\r
332     /* If the last OCA period is too short, run "half" in the first OCA count\r
333        so the final count is withing setup time limits */\r
334     if ((count3 & 0xffff) < MIN_SETUP_TIME) {\r
335         count3 += 0x10000 - (MID_SET_TIME - MIN_SETUP_TIME);\r
336         /* Set OCA3 forward of the current TCNT3 */\r
337         forwardOf(OCR3A, OCR3A, (MID_SET_TIME - MIN_SETUP_TIME));        \r
338     }\r
339     /* If the last OCA period extends the OCB past rollover then\r
340        run "half" in the first OCA count */\r
341     else if ((count3 & 0xffff) > MAX_SETUP_TIME) {\r
342         count3 += 0x10000 - (MAX_SETUP_TIME - MID_SET_TIME); \r
343         /* Set OCA3 forward of the current TCNT3 */\r
344         forwardOf(OCR3A, OCR3A, (MAX_SETUP_TIME - MID_SET_TIME));    \r
345     }\r
346     /* Final count period */\r
347     else if (!(count3 & 0xffff0000)) {\r
348         /* Set up the last OCA period and OCB pulse period */\r
349         forwardOf(OCR3A, OCR3A, (uint16_t)count3);\r
350         forwardOf(OCR3B, OCR3A, PULSE_TIME);\r
351         /* Flag nextStepTimer3() that the last time is still running\r
352            so nextCount3 is queued */\r
353         count3 = FINAL;\r
354         /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
355            see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
356         TCCR3A = (1 << COM3A1) | (1 << COM3B1) | TIMER_MODE_NORMAL;\r
357         /* Enable the timer OCB interrupt to reload at end of pulse */\r
358         TIMSK3 = 1 << OCIE3B;\r
359         return;\r
360     }\r
361     /* For all non-FINAL timing, only enable OCA interrupt; see ATmega1280\r
362        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
363     TIMSK3 = 1 << OCIE3A;\r
364     return;\r
365 }\r
366 /*..........................................................................*/\r
367 void BSP_initStepTimer4(QActive *me)\r
368 {\r
369     count4AO = me;\r
370     nextCount4 = 0;\r
371     count4 = 0;\r
372     /* Disable power reduction for timer4; see\r
373        ATmega1280 "11.9.3 PRR1 - Power Reduction Register 1" */\r
374     PRR1 &= ~(1 << PRTIM4); \r
375     /* Disable (clocking of) the timer; see ATmega1280\r
376        "17.11.8 TCCR5B - Timer/Counter 5 Control Register B" */\r
377     TCCR4B = 0;\r
378     /* Set COM4A and COM4B to drive both OC latches high; see\r
379        ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
380     TCCR4A =   (1 << COM4A1) | (1 << COM4A0) \r
381              | (1 << COM4B1) | (1 << COM4B0) \r
382              | TIMER_MODE_NORMAL;\r
383     /* Force OC latches high; see ATmega1280\r
384        "17.11.12 TCCR5C - Timer/Counter 5 Control Register C" */\r
385     TCCR4C = (1 << FOC4A) | (1 << FOC4B);\r
386     /* Disable OC as pin source and drive pins with PORT latch */\r
387     TCCR4A = TIMER_MODE_NORMAL;\r
388     /* Set OCA and OCB PORT bits high; OC4A is PH3 and OC4B is PH4 */\r
389     PORTH |= (1 << PH3) | (1 << PH4);\r
390     /* Set DDR to drive the PORT bits for OCA and OCB pins */\r
391     DDRH |= (1 << DDH3) | (1 << DDH4);\r
392     /* Reenable timer with clkI/O (no prescaling) */\r
393     TCCR4B = 1 << CS40; \r
394     return;\r
395 }\r
396 /*..........................................................................*/\r
397 uint8_t BSP_nextStepTimer4(uint32_t count)\r
398 {\r
399     uint8_t register sreg;\r
400 \r
401     /* There is a minimum setup time so error on short counts */    \r
402     if (count < MIN_SETUP_TIME)\r
403         return 1;\r
404     /* A count is already queued so error */\r
405     if (nextCount4)\r
406         return 2;\r
407     sreg = SREG; /* save interrupt and other status */\r
408     cli(); /* and disable interrupts */\r
409     /* If step timer is still running, leave it to the ISR to load\r
410        the next step time */\r
411     if (count4) {\r
412         nextCount4 = count; /* queue the count for ISR to load */\r
413         SREG = sreg; /* restore interrupts */\r
414         return 0;\r
415     }\r
416     SREG = sreg; /* restore interrupts */\r
417     /* Request next step time from HSM */\r
418     if (count4AO) {\r
419         QActive_postISR(count4AO, NEXT_STEP_TIME_SIG, 0);\r
420     }\r
421     /* If the last OCA period is too short, run "half" in the first OCA count\r
422        so the final count is withing setup time limits */\r
423     if ((count & 0x0000ffff) < MIN_SETUP_TIME) {\r
424         count4 = count + 0x10000 - (MID_SET_TIME - MIN_SETUP_TIME);\r
425         /* Set OCA4 forward of the current TCNT4 */\r
426         forwardOf(OCR4A, TCNT4, (MID_SET_TIME - MIN_SETUP_TIME));        \r
427     }\r
428     /* If the last OCA period extends the OCB past rollover then\r
429        run "half" in the first OCA count */\r
430     else if ((count & 0x0000ffff) > MAX_SETUP_TIME) {\r
431         count4 = count + 0x10000 - (MAX_SETUP_TIME - MID_SET_TIME); \r
432         /* Set OCA4 forward of the current TCNT4 */\r
433         forwardOf(OCR4A, TCNT4, (MAX_SETUP_TIME - MID_SET_TIME));    \r
434     }\r
435     /* Final count period */\r
436     else if (!(count & 0xffff0000)) {\r
437         /* Flag nextStepTimer4() that the last time is still running\r
438            so nextCount4 is queued */\r
439         count4 = FINAL;\r
440         /* Set up the last OCRA period and OCRB pulse period */\r
441         forwardOf(OCR4A, TCNT4, (uint16_t)count);\r
442         forwardOf(OCR4B, OCR4A, PULSE_TIME);\r
443         /* Clear stale OC interrupt flags; see ATmega1280\r
444            "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
445         TIFR4 = (1 << OCF4B) | (1 << OCF4A);\r
446         /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
447            see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
448         TCCR4A = (1 << COM4A1) | (1 << COM4B1) | TIMER_MODE_NORMAL;\r
449         /* Enable the OCA interrupt to discard the interrupt during the final count.\r
450            Enable OCB interrupt to reload at end of pulse */\r
451         TIMSK4 = (1 << OCIE4A) | (1 << OCIE4B);\r
452         return 0;\r
453     }\r
454     /* Start rollover period */\r
455     else {\r
456         count4 = count;\r
457         forwardOf(OCR4A, TCNT4, 0);\r
458     }\r
459     /* Clear stale OCA interrupt flag; see ATmega1280\r
460        "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
461      TIFR4 = 1 << OCF4A;\r
462     /* For all non-FINAL timing, only enable OCA interrupt; see ATmega1280\r
463        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
464     TIMSK4 = 1 << OCIE4A;\r
465     return 0;\r
466 }\r
467 /*..........................................................................*/\r
468 /* For information about AVR Interrupts see:\r
469    http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html\r
470    OC4A Interrupt handler */\r
471 ISR(TIMER4_COMPA_vect) {\r
472     /* Adjust timer based on time to run */\r
473     if (count4 & 0xffff0000) {\r
474         count4 -= 0x10000;\r
475         if (count4 & 0xffff0000) {\r
476             /* More OCA rollovers yet */\r
477             return;\r
478         } \r
479     } else {\r
480         /* The OCA interrupt flag has been cleared on vector into here,\r
481            ISR(TIMER4_COMPA_vect), see: ATmega1280\r
482            "16.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register", but no\r
483            further action is needed. This EMPTY_INTERRUPT behaviour is to\r
484            avoid having to clear a stale OCA pending interrupt and potentially\r
485            discard an OCA on the next step time setup in ISR(TIMER4_COMPB_vect)\r
486          */\r
487         if (count4 == FINAL) {\r
488             return;\r
489         }\r
490     }\r
491     /* Last OCA time for this step. Setup to pulse opto using OCA and OCB */\r
492     forwardOf(OCR4A, OCR4A, (uint16_t)count4); \r
493     forwardOf(OCR4B, OCR4A, PULSE_TIME);\r
494     /* Maintain flag for nextStepTimer4() to queue any request */\r
495     count4 = FINAL;\r
496     /* Clear stale OCB interrupt flag; see ATmega1280\r
497        "17.11.40 TIFR5 - Timer/Counter5 Interrupt Flag Register" */ \r
498     TIFR4 = 1 << OCF4B;\r
499     /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
500        see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
501     TCCR4A = (1 << COM4A1) | (1 << COM4B1) | TIMER_MODE_NORMAL;\r
502     /* Enable the OCA interrupt to discard the interrupt during the final count.\r
503        Enable OCB interrupt to reload at end of pulse; see\r
504        ATmega1280 "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register". */\r
505     TIMSK4 = (1 << OCIE4A) | (1 << OCIE4B);\r
506     return;\r
507 }\r
508 /*..........................................................................*/\r
509 /* OC4B Interrupt handler */\r
510 ISR(TIMER4_COMPB_vect) {\r
511     /* Set COM4A and COM4B to drive both OC latches high; see\r
512        ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
513     TCCR4A =   (1 << COM4A1) | (1 << COM4A0) \r
514              | (1 << COM4B1) | (1 << COM4B0) \r
515              | TIMER_MODE_NORMAL;\r
516     /* Force OC latches high; see\r
517        ATmega1280 "17.11.12 TCCR5C - Timer/Counter 5 Control Register C"\r
518        Force OCA first followed by OCB so the LED extinguishes "glitch-free"\r
519        TODO Consider risk of reverse bias of the LED for the interval */\r
520     TCCR4C = (1 << FOC4A);\r
521     TCCR4C = (1 << FOC4B);\r
522     /* Disable OC as pin source and drive pins with PORT latch */\r
523     TCCR4A = TIMER_MODE_NORMAL;\r
524     /* Disable OCA and OCB interrupt; see ATmega1280\r
525        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
526     TIMSK4 = 0;\r
527     /* If no queued step time, idle and restart timer from nextStepTimer4() */\r
528     if (!nextCount4) {\r
529         count4 = 0;\r
530         return;\r
531     }\r
532     /* Else consume queued step time */\r
533     count4 = nextCount4;\r
534     nextCount4 = 0;\r
535     /* Request next step time from HSM */\r
536     if (count4AO) {\r
537         QActive_postISR(count4AO, NEXT_STEP_TIME_SIG, 0);\r
538     }\r
539     /* If the last OCA period is too short, run "half" in the first OCA count\r
540        so the final count is withing setup time limits */\r
541     if ((count4 & 0xffff) < MIN_SETUP_TIME) {\r
542         count4 += 0x10000 - (MID_SET_TIME - MIN_SETUP_TIME);\r
543         /* Set OCA4 forward of the current TCNT4 */\r
544         forwardOf(OCR4A, OCR4A, (MID_SET_TIME - MIN_SETUP_TIME));        \r
545     }\r
546     /* If the last OCA period extends the OCB past rollover then\r
547        run "half" in the first OCA count */\r
548     else if ((count4 & 0xffff) > MAX_SETUP_TIME) {\r
549         count4 += 0x10000 - (MAX_SETUP_TIME - MID_SET_TIME); \r
550         /* Set OCA4 forward of the current TCNT4 */\r
551         forwardOf(OCR4A, OCR4A, (MAX_SETUP_TIME - MID_SET_TIME));    \r
552     }\r
553     /* Final count period */\r
554     else if (!(count4 & 0xffff0000)) {\r
555         /* Set up the last OCA period and OCB pulse period */\r
556         forwardOf(OCR4A, OCR4A, (uint16_t)count4);\r
557         forwardOf(OCR4B, OCR4A, PULSE_TIME);\r
558         /* Flag nextStepTimer4() that the last time is still running\r
559            so nextCount4 is queued */\r
560         count4 = FINAL;\r
561         /* Enable the OCA and OCB pin controls to clear (drive low) both pins;\r
562            see ATmega1280 "Table 17-3. Compare Output Mode, non-PWM" */\r
563         TCCR4A = (1 << COM4A1) | (1 << COM4B1) | TIMER_MODE_NORMAL;\r
564         /* Enable the timer OCB interrupt to reload at end of pulse */\r
565         TIMSK4 = 1 << OCIE4B;\r
566         return;\r
567     }\r
568     /* For all non-FINAL timing, only enable OCA interrupt; see ATmega1280\r
569        "17.11.36 TIMSK5 - Timer/Counter 5 Interrupt Mask Register" */\r
570     TIMSK4 = 1 << OCIE4A;\r
571     return;\r
572 }\r
573 /*..........................................................................*/\r
574 ISR(TIMER0_COMPA_vect)\r
575 {\r
576     QF_tick();\r
577 }\r
578 /*..........................................................................*/\r
579 QActive *ADC15Sense = (QActive *)0;\r
580 \r
581 void BSP_initAzimuthFault(QActive *me)\r
582 {\r
583         ADC15Sense = me;\r
584         /* Arduino ADC15 pin generates PCINT23 interrupts */\r
585         PCMSK2 |= 1 << PCINT23;\r
586         PCICR  |= 1 << PCIE2;   \r
587 }\r
588 \r
589 ISR(PCINT2_vect)\r
590 {\r
591         if (PINK & (1 << PINK7)) {\r
592                 QActive_postISR(ADC15Sense, STEPPER_FAULT_SIG, 0);\r
593         }\r
594 }\r
595 /*..........................................................................*/\r
596 QActive *PWM12Sense = (QActive *)0;\r
597 \r
598 void BSP_initElevationFault(QActive *me)\r
599 {\r
600         PWM12Sense = me;\r
601         /* Arduino PWM12 pin generates PCINT6 interrupts */\r
602         PCMSK0 |= 1 << PCINT6;\r
603         PCICR  |= 1 << PCIE0;   \r
604 }\r
605 \r
606 ISR(PCINT0_vect)\r
607 {\r
608         if (PINB & (1 << PINB6)) { \r
609                 QActive_postISR(PWM12Sense, STEPPER_FAULT_SIG, 0);\r
610         }\r
611 }\r
612 /*..........................................................................*/\r
613 void BSP_azimuthDirection(int8_t direction)\r
614 {\r
615         if (direction < 0) {\r
616                 PORTA &= ~(1 << AZ_DIR_PIN);\r
617                 return;         \r
618         }\r
619         PORTA |= 1 << AZ_DIR_PIN;\r
620 }\r
621 /*..........................................................................*/\r
622 void BSP_elevationDirection(int8_t direction)\r
623 {\r
624         if (direction < 0) {\r
625                 PORTA &= ~(1 << EL_DIR_PIN);\r
626                 return;\r
627         }\r
628         PORTA |= 1 << EL_DIR_PIN;\r
629 }\r
630 /*..........................................................................*/\r
631 /* Current reduction should be implemented automatically using FC jumper on\r
632    the GDM stepper driver board. See warning, GDM Manual, 6.5 Bridges \r
633    (Jumpers) function, page 11. The services and hardware are provided in\r
634    case explicit current control is necessary or for Current Off control. */\r
635 void BSP_azimuthCurrent(uint8_t full)\r
636 {\r
637         if (full) {\r
638                 PORTA &= ~(1 << AZ_CURR_PIN);\r
639                 return;\r
640         }\r
641         PORTA |= 1 << AZ_CURR_PIN;\r
642 }\r
643 /*..........................................................................*/\r
644 void BSP_elevationCurrent(uint8_t full)\r
645 {\r
646         if (full) {\r
647                 PORTA &= ~(1 << EL_CURR_PIN);\r
648                 return;\r
649         }\r
650         PORTA |= 1 << EL_CURR_PIN;\r
651 }\r
652 /*..........................................................................*/\r
653 void BSP_init(void)\r
654 {\r
655     /* set the output compare value */\r
656     OCR0A  = (uint8_t)((F_CPU / BSP_TICKS_PER_SEC / 1024) - 1);\r
657 \r
658         /* enable the shield activity LED port pin and drive LED on */ \r
659         DDRB  |= 1 << DDB7;\r
660         PORTB |= 1 << PB7;\r
661 \r
662         /* Drive output EL and AZ output pins and set inactive low */\r
663         PORTA &= ~(   (1 << EL_DIR_PIN)  | (1 << AZ_DIR_PIN)\r
664                          | (1 << EL_CURR_PIN) | (1 << AZ_CURR_PIN));\r
665         DDRA  |=   (1 << EL_DIR_PIN)  | (1 << AZ_DIR_PIN)\r
666                      | (1 << EL_CURR_PIN) | (1 << AZ_CURR_PIN);\r
667 }\r
668 /*..........................................................................*/\r
669 void QF_onStartup(void) {\r
670         cli();                    /* make sure that all interrupts are disabled */\r
671 \r
672     /* set Timer0 in CTC mode, 1/1024 prescaler, start the timer ticking */\r
673     TCCR0A = (1 << WGM01) | (0 << WGM00);\r
674     TCCR0B = (0 << WGM02) | (5 << CS00);\r
675     TIMSK0 = (1 << OCIE0A);              /* Enable TIMER0 compare Interrupt */\r
676 \r
677     // start the serial port\r
678     //serialReceiveStartup(USART0);\r
679     //serialTransmitStartup(USART0);\r
680 \r
681     sei();                     /* make sure that all interrupts are enabled */\r
682 }\r
683 /*..........................................................................*/\r
684 void QF_onIdle(void) {\r
685         PORTB &= ~(1 << PB7);\r
686         /* Power reduction on Timer1, SPI (both unused) */\r
687         // PRR = (1 << PRTWI) | (1 << PRTIM2) | (1 << PRTIM1) | (1 << PRSPI);\r
688         SMCR = (0 << SM0) | (1 << SE);                                        /* Power save */\r
689     __asm__ __volatile__ ("sei" "\n\t" :: );     /* Two instructions atomic */\r
690     __asm__ __volatile__ ("sleep" "\n\t" :: );\r
691     SMCR = 0;\r
692         PORTB |= 1 << PB7;\r
693 }\r
694 /*..........................................................................*/\r
695 void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) {\r
696     for (;;) {       /* NOTE: replace the loop with reset for final version */\r
697     }\r
698 }\r
699 /*..........................................................................*/\r

UCC git Repository :: git.ucc.asn.au