4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30 * All Rights Reserved.
33 * \brief Stepper driver interface implementation.
35 * This module use the three timer on the at91 family, to generate a
36 * six periodic variable pwm waveform. The pulse width is fix, and could
37 * change by setting the STEPPER_DELAY_ON_COMPARE_C define, but you make
38 * an attention to do this, becouse the pulse width is not exactly
39 * STEPPER_DELAY_ON_COMPARE_C. The pulse width depend also to latency
40 * time of cpu to serve an interrupt, this generate an pwm waveform affect
41 * to noise. This noise not effect the period but only the pulse width,
42 * becouse the raising edge is generate by hardware comply with the our
45 * Note: is most important to set STEPPER_DELAY_ON_COMPARE_C value minor
46 * than a interrupt time service, becouse the falling edge must be happen
47 * inside to inerrupt service to guarantee a correct functionaly of pwm
53 * \author Daniele Basile <asterix@develer.com>
56 #include "stepper_at91.h"
58 #include <cfg/macros.h>
59 #include <cfg/debug.h>
61 #include <cpu/types.h>
66 #include "appconfig.h"
69 * Delay to set C compare to clear output
70 * on select TIO output
72 #define STEPPER_DELAY_ON_COMPARE_C 20
75 * Forward declaration for interrupt handler
77 static void stepper_tc0_irq(void);
78 static void stepper_tc1_irq(void);
79 static void stepper_tc2_irq(void);
81 ///< Static array of timer counter struct for stepper.
82 static struct TimerCounter stepper_timers[CONFIG_TC_STEPPER_MAX_NUM] =
84 { //Timer Counter settings for TIOA0 output pin
86 .blk_ctrl_set = TC_NONEXC0,
87 .chl_mode_reg = &TC0_CMR,
88 .chl_ctrl_reg = &TC0_CCR,
89 .comp_effect_mask = TC_ACPA_MASK,
90 .comp_effect_set = TC_ACPA_SET_OUTPUT,
91 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
92 .comp_effect_c_mask = TC_ACPC_MASK,
93 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
94 .ext_event_set = TC_EEVT_XC0,
96 .comp_c_reg = &TC0_RC,
97 .count_val_reg = &TC0_CV,
98 .irq_enable_reg = &TC0_IER,
99 .irq_disable_reg = &TC0_IDR,
100 .irq_set_mask = BV(TC_CPAS),
101 .irq_mask_reg = &TC0_IMR,
102 .isr = stepper_tc0_irq,
103 .status_reg = &TC0_SR,
108 { //Timer Counter settings for TIOB0 output pin
110 .blk_ctrl_set = TC_NONEXC0,
111 .chl_mode_reg = &TC0_CMR,
112 .chl_ctrl_reg = &TC0_CCR,
114 .comp_c_reg = &TC0_RC,
115 .count_val_reg = &TC0_CV,
116 .comp_effect_mask = TC_BCPB_MASK,
117 .comp_effect_set = TC_BCPB_SET_OUTPUT,
118 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
119 .comp_effect_c_mask = TC_BCPC_MASK,
120 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
121 .ext_event_set = TC_EEVT_XC0,
122 .irq_enable_reg = &TC0_IER,
123 .irq_disable_reg = &TC0_IDR,
124 .irq_set_mask = BV(TC_CPBS),
125 .irq_mask_reg = &TC0_IMR,
126 .isr = stepper_tc0_irq,
127 .status_reg = &TC0_SR,
132 { //Timer Counter settings for TIOA1 output pin
134 .blk_ctrl_set = TC_NONEXC1,
135 .chl_mode_reg = &TC1_CMR,
136 .chl_ctrl_reg = &TC1_CCR,
138 .comp_c_reg = &TC1_RC,
139 .count_val_reg = &TC1_CV,
140 .comp_effect_mask = TC_ACPA_MASK,
141 .comp_effect_set = TC_ACPA_SET_OUTPUT,
142 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
143 .comp_effect_c_mask = TC_ACPC_MASK,
144 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
145 .ext_event_set = TC_EEVT_XC1,
146 .irq_enable_reg = &TC1_IER,
147 .irq_disable_reg = &TC1_IDR,
148 .irq_set_mask = BV(TC_CPAS),
149 .irq_mask_reg = &TC1_IMR,
150 .isr = stepper_tc1_irq,
151 .status_reg = &TC1_SR,
156 { //Timer Counter settings for TIOB1 output pin
158 .blk_ctrl_set = TC_NONEXC1,
159 .chl_mode_reg = &TC1_CMR,
160 .chl_ctrl_reg = &TC1_CCR,
162 .comp_c_reg = &TC1_RC,
163 .count_val_reg = &TC1_CV,
164 .comp_effect_mask = TC_BCPB_MASK,
165 .comp_effect_set = TC_BCPB_SET_OUTPUT,
166 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
167 .comp_effect_c_mask = TC_BCPC_MASK,
168 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
169 .ext_event_set = TC_EEVT_XC1,
170 .irq_enable_reg = &TC1_IER,
171 .irq_disable_reg = &TC1_IDR,
172 .irq_set_mask = BV(TC_CPBS),
173 .irq_mask_reg = &TC1_IMR,
174 .isr = stepper_tc1_irq,
175 .status_reg = &TC1_SR,
180 { //Timer Counter settings for TIOA2 output pin
182 .blk_ctrl_set = TC_NONEXC2,
183 .chl_mode_reg = &TC2_CMR,
184 .chl_ctrl_reg = &TC2_CCR,
186 .comp_c_reg = &TC2_RC,
187 .count_val_reg = &TC2_CV,
188 .comp_effect_mask = TC_ACPA_MASK,
189 .comp_effect_set = TC_ACPA_SET_OUTPUT,
190 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
191 .comp_effect_c_mask = TC_ACPC_MASK,
192 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
193 .ext_event_set = TC_EEVT_XC2,
194 .irq_enable_reg = &TC2_IER,
195 .irq_disable_reg = &TC2_IDR,
196 .irq_set_mask = BV(TC_CPAS),
197 .irq_mask_reg = &TC2_IMR,
198 .isr = stepper_tc2_irq,
199 .status_reg = &TC2_SR,
204 { //Timer Counter settings for TIOB2 output pin
206 .blk_ctrl_set = TC_NONEXC2,
207 .chl_mode_reg = &TC2_CMR,
208 .chl_ctrl_reg = &TC2_CCR,
210 .comp_c_reg = &TC2_RC,
211 .count_val_reg = &TC2_CV,
212 .comp_effect_mask = TC_BCPB_MASK,
213 .comp_effect_set = TC_BCPB_SET_OUTPUT,
214 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
215 .comp_effect_c_mask = TC_BCPC_MASK,
216 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
217 .ext_event_set = TC_EEVT_XC2,
218 .irq_enable_reg = &TC2_IER,
219 .irq_disable_reg = &TC2_IDR,
220 .irq_set_mask = BV(TC_CPBS),
221 .irq_mask_reg = &TC2_IMR,
222 .isr = stepper_tc2_irq,
223 .status_reg = &TC2_SR,
231 * Generic TIO interrupt handler.
233 INLINE void stepper_tc_tio_irq(struct TimerCounter * t)
236 *t->chl_mode_reg &= ~t->comp_effect_c_mask;
237 *t->chl_mode_reg |= t->comp_effect_c_clear;
240 * Cleat TIO output on c register compare.
241 * This generate an pulse with variable lenght, this
242 * depend to delay that interrupt is realy service.
244 *t->comp_c_reg = *t->count_val_reg + STEPPER_DELAY_ON_COMPARE_C;
246 //Call the associate callback
247 t->callback(t->motor);
249 *t->chl_mode_reg &= ~t->comp_effect_c_mask;
254 * Interrupt handler for timer counter TCKL0
256 static void ISR_FUNC stepper_tc0_irq(void)
259 * Warning: when we read the status_reg register, we reset it.
260 * That mean if is occur an interrupt event we can read only
261 * the last that has been occur. To not miss an interrupt event
262 * we save the status_reg register and then we read it.
264 uint32_t status_reg = TC0_SR & TC0_IMR;
266 if ((status_reg & BV(TC_CPBS)) && (status_reg & BV(TC_CPAS)))
269 if (status_reg & BV(TC_CPAS))
270 stepper_tc_tio_irq(&stepper_timers[TC_TIOA0]);
272 if (status_reg & BV(TC_CPBS))
273 stepper_tc_tio_irq(&stepper_timers[TC_TIOB0]);
276 /* Inform hw that we have served the IRQ */
282 * Interrupt handler for timer counter TCKL1
284 static void ISR_FUNC stepper_tc1_irq(void)
288 * Warning: when we read the status_reg register, we reset it.
289 * That mean if is occur an interrupt event we can read only
290 * the last that has been occur. To not miss an interrupt event
291 * we save the status_reg register and then we read it.
293 uint32_t status_reg = TC1_SR & TC1_IMR;
295 if (status_reg & BV(TC_CPAS))
296 stepper_tc_tio_irq(&stepper_timers[TC_TIOA1]);
298 if (status_reg & BV(TC_CPBS))
299 stepper_tc_tio_irq(&stepper_timers[TC_TIOB1]);
302 /* Inform hw that we have served the IRQ */
304 STEPPER_STROBE_OFF_1;
309 * Interrupt handler for timer counter TCKL2
311 static void ISR_FUNC stepper_tc2_irq(void)
315 * Warning: when we read the status_reg register, we reset it.
316 * That mean if is occur an interrupt event we can read only
317 * the last that has been occur. To not miss an interrupt event
318 * we save the status_reg register and then we read it.
320 uint32_t status_reg = TC2_SR & TC2_IMR;
323 if (status_reg & BV(TC_CPAS))
324 stepper_tc_tio_irq(&stepper_timers[TC_TIOA2]);
326 if (status_reg & BV(TC_CPBS))
327 stepper_tc_tio_irq(&stepper_timers[TC_TIOB2]);
329 STEPPER_STROBE_OFF_2;
330 /* Inform hw that we have served the IRQ */
336 * Timer couter setup.
338 * This function apply to select timer couter all needed settings.
339 * Every settings are stored in stepper_timers[].
341 void stepper_tc_setup(int index, stepper_isr_t callback, struct Stepper *motor)
343 ASSERT(index < CONFIG_TC_STEPPER_MAX_NUM);
345 motor->timer = &stepper_timers[index];
347 //Disable PIO controller and enable TIO function
348 TIO_PIO_PDR = BV(motor->timer->tio_pin);
349 TIO_PIO_ABSR = BV(motor->timer->tio_pin);
352 * Sets timer counter in waveform mode.
354 * - Waveform mode 00 (see datasheet for more detail.)
355 * - Master clock prescaler to STEPPER_MCK_PRESCALER
356 * - Set none external event
357 * - Clear pin output on comp_reg
358 * - None effect on reg C compare
360 *motor->timer->chl_mode_reg = BV(TC_WAVE);
361 *motor->timer->chl_mode_reg |= motor->timer->ext_event_set;
362 *motor->timer->chl_mode_reg &= ~TC_WAVSEL_MASK;
363 *motor->timer->chl_mode_reg |= TC_WAVSEL_UP;
364 *motor->timer->chl_mode_reg |= STEPPER_MCK_PRESCALER;
365 *motor->timer->chl_mode_reg |= motor->timer->comp_effect_clear;
366 *motor->timer->chl_mode_reg &= ~motor->timer->comp_effect_c_mask;
368 //Reset comp_reg and C compare register
369 *motor->timer->comp_reg = 0;
370 *motor->timer->comp_c_reg = 0;
372 //Register interrupt vector
374 IRQ_SAVE_DISABLE(flags);
377 * Warning: To guarantee a correct management of interrupt event, we must
378 * trig the interrupt on level sensitive. This becouse, we have only a common
379 * line for interrupt request, and if we have at the same time two interrupt
380 * request could be that the is service normaly but the second will never
381 * been detected and interrupt will stay active but never serviced.
383 AIC_SVR(motor->timer->timer_id) = motor->timer->isr;
384 AIC_SMR(motor->timer->timer_id) = AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
385 AIC_IECR = BV(motor->timer->timer_id);
387 // Disable interrupt on select timer counter
388 stepper_tc_irq_disable(motor->timer);
393 motor->timer->callback = callback;
394 motor->timer->motor = motor;
398 * Timer counter init.
400 void stepper_tc_init(void)
404 ASSERT(CONFIG_NUM_STEPPER_MOTORS <= CONFIG_TC_STEPPER_MAX_NUM);
407 * Enable timer counter:
408 * - power on all timer counter
409 * - disable all interrupt
410 * - disable all external event/timer source
412 for (int i = 0; i < CONFIG_TC_STEPPER_MAX_NUM; i++)
414 PMC_PCER = BV(stepper_timers[i].timer_id);
415 *stepper_timers[i].irq_disable_reg = 0xFFFFFFFF;
416 TC_BMR = stepper_timers[i].blk_ctrl_set;
420 * Enable timer counter and start it.
422 for (int i = 0; i < CONFIG_TC_STEPPER_MAX_NUM; i++)
423 *stepper_timers[i].chl_ctrl_reg = (BV(TC_CLKEN) | BV(TC_SWTRG));