Contiki 2.5
stepper.c
1 #include <stepper.h>
2 #include <stepper-interrupt.h>
3 
4 #ifndef NUL
5 #define NULL 0
6 #endif
7 
8 static StepperAccSeq *free_seq = NULL;
9 
10 StepperAccSeq *
11 stepper_allocate_seq()
12 {
13  StepperAccSeq *seq;
14  if (!free_seq) return NULL;
15  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
16  seq = free_seq;
17  free_seq = seq->next;
18  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
19  return seq;
20 }
21 
22 
23 void
24 stepper_free_seq(StepperAccSeq *seq)
25 {
26  StepperAccSeq *s;
27  if (!seq) return;
28  s = seq;
29  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
30  while(s->next) s = s->next;
31  s->next = free_seq;
32  free_seq = seq;
33  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
34 }
35 
36 static void
37 stepper_state_init(StepperState *stepper)
38 {
39  stepper->step_count = 0;
40  stepper->io_mask = 0;
41  stepper->acc_steps = NULL;
42  stepper->run_steps = NULL;
43  stepper->hold_steps = NULL;
44  stepper->current_step = 0;
45  stepper->sequence_length = 0;
46 
47  stepper->velocity = 0;
48  stepper->acceleration = 0;
49  stepper->step_full = 0;
50  stepper->step_frac = 0;
51  stepper->n_steps = 0;
52 
53 #ifdef TIMING_ERRORS
54  stepper->err_min = TIMER_FREQ;
55  stepper->err_max = -TIMER_FREQ;
56 #endif
57 
58 }
59 
60 void
61 stepper_init(AT91PS_TC timer, unsigned int id)
62 {
63  unsigned int s;
64  stepper_context.flags = 0;
65  stepper_context.timer_channel = timer;
66  stepper_context.steps = NULL;
67  stepper_context.current_step = NULL;
68  stepper_context.period_count = 0;
69  stepper_context.user_callback = NULL;
70 
71  for (s = 0; s < NUM_STEPPERS; s++) {
72  stepper_state_init(&stepper_context.steppers[s]);
73  }
74  timer->TC_CMR = (AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO
75  | AT91C_TC_CLKS_TIMER_DIV3_CLOCK);
76  timer->TC_RC = TIMER_FREQ / PPS;
77  timer->TC_RA = 0xffff;
78  timer->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
79  *AT91C_PMC_PCER = (1 << id);
80 
81  AT91C_AIC_SMR[id] = AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE | 7;
82  AT91C_AIC_SVR[id] = (unsigned long)stepper_timer_interrupt;
83  *AT91C_AIC_IECR = (1 << id);
84  timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
85 }
86 
87 void
88 stepper_init_io(unsigned int stepper_index, uint32_t mask,
89  const uint32_t *acc, const uint32_t *run,
90  const uint32_t *hold, unsigned int nsteps)
91 {
92  StepperState *state;
93  if (stepper_index >= NUM_STEPPERS) return;
94  state = &stepper_context.steppers[stepper_index];
95 
96  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
97 
98  state->io_mask = mask;
99  state->acc_steps = acc;
100  state->run_steps = run;
101  state->hold_steps = hold;
102  state->current_step = 0;
103  state->sequence_length = nsteps;
104  *AT91C_PIOA_OWER = mask;
105  *AT91C_PIOA_MDDR = mask;
106 
107  *AT91C_PIOA_ODSR = ((*AT91C_PIOA_ODSR & ~mask)
108  | (state->hold_steps[0] & mask));
109  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
110  *AT91C_PIOA_OER = mask;
111 }
112 
113 /**
114  Append an acceleration sequence
115 
116  Truncates the current acceleration sequence at the insertion time
117  and appends the new sequence at that position.. The insertion time
118  is the time of the first element of the new sequence. The
119  truncation takes place after any elements with the acceleration set
120  to STEPPER_ACC_INVALID (user callbacks) that has the same time as
121  the insertion time. All other elements with the same time is
122  replaced.
123 
124  \param stepper_index Index of the stepper the sequence is intended for.
125  \param new_seq A linked list of sequence elements to append.
126  */
127 StepperResult
128 stepper_add_acc_seq(unsigned int stepper_index, StepperAccSeq *new_seq)
129 {
130  StepperResult res = STEPPER_ERR_TOO_LATE;
131  StepperAccSeq **seqp;
132  StepperState *state;
133  if (stepper_index >= NUM_STEPPERS) return STEPPER_ERR_INDEX;
134  state = &stepper_context.steppers[stepper_index];
135  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
136  seqp = &state->acceleration_sequence;
137  while(*seqp && ((*seqp)->period < new_seq->period || ((*seqp)->period == new_seq->period && (*seqp)->acceleration == STEPPER_ACC_INVALID))) {
138  seqp = &(*seqp)->next;
139  }
140  if (new_seq->period > stepper_context.period_count + 1) {
141  /* Replace tail of sequence */
142  if (*seqp) stepper_free_seq(*seqp);
143  *seqp = new_seq;
144  res = STEPPER_OK;
145  }
146  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
147  return res;
148 }
149 
150 /**
151  Insert a callback mark
152 
153  Inserts a callback mark at the indicated period. This will cause
154  the callback procedure to be called just before that period,
155  usually near the beginning of the previous period. Does not
156  truncate the current sequence.
157 
158  \param stepper_index Index of the stepper the callbak is intended for.
159  \param period When the callback should be invoked
160 
161  \sa stepper_set_callback_proc
162 */
163 
164 StepperResult
165 stepper_insert_callback(unsigned int stepper_index, unsigned int period)
166 {
167  StepperResult res = STEPPER_ERR_TOO_LATE;
168  StepperAccSeq **seqp;
169  StepperState *state;
170  if (stepper_index >= NUM_STEPPERS) return STEPPER_ERR_INDEX;
171  state = &stepper_context.steppers[stepper_index];
172  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
173  seqp = &state->acceleration_sequence;
174  while(*seqp && (*seqp)->period < period) {
175  seqp = &(*seqp)->next;
176  }
177  if (period > stepper_context.period_count + 1) {
178  StepperAccSeq *new_seq = stepper_allocate_seq();
179  if (!new_seq) {
180  res = STEPPER_ERR_MEM;
181  } else {
182  new_seq->next = *seqp;
183  *seqp = new_seq;
184  new_seq->period = period;
185  new_seq->acceleration = STEPPER_ACC_INVALID;
186  res = STEPPER_OK;
187  }
188  }
189  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
190  return res;
191 }
192 
193 StepperResult
194 stepper_add_acc(unsigned int stepper_index, unsigned int period, long acc)
195 {
196  StepperAccSeq *seq = stepper_allocate_seq();
197  /* printf("stepper_add_acc: %d %d %ld\n", stepper_index, period, acc); */
198  if (!seq) return STEPPER_ERR_MEM;
199  seq->next = NULL;
200  seq->period = period;
201  seq->acceleration = acc;
202  return stepper_add_acc_seq(stepper_index, seq);
203 }
204 
205 void
206 stepper_set_callback_proc(StepperUserCallback callback)
207 {
208  stepper_context.user_callback = callback;
209 }
210 
211 unsigned long
212 stepper_current_period()
213 {
214  return stepper_context.period_count;
215 }
216 
217 long
218 stepper_current_step(unsigned int stepper_index)
219 {
220  StepperState *state = &stepper_context.steppers[stepper_index];
221  return state->step_count;
222 }
223 
224 long long
225 stepper_step_frac(unsigned int stepper_index)
226 {
227  long long s;
228  StepperState *state = &stepper_context.steppers[stepper_index];
229  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
230  s = state->step_full * DIST_SCALE + state->step_frac;
231  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
232  return s;
233 }
234 
235 long
236 stepper_current_velocity(unsigned int stepper_index)
237 {
238  StepperState *state = &stepper_context.steppers[stepper_index];
239  return state->velocity;
240 }
241 
242 /* Calculate the speed at given current given the current acceleration
243  sequence. */
244 unsigned long
245 stepper_velocity(unsigned int stepper_index, unsigned long period)
246 {
247  long a;
248  long v;
249  unsigned long t;
250  StepperState *state;
251  StepperAccSeq *seq;
252  state = &stepper_context.steppers[stepper_index];
253 
254  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
255  seq = state->acceleration_sequence;
256  a = state->acceleration;
257  v = state->velocity;
258  t = stepper_context.period_count + 2;
259 
260  while(seq && seq->period < period) {
261  v += a * (seq->period - t);
262  t = seq->period;
263  a = seq->acceleration;
264  seq = seq->next;
265  }
266  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
267  v += a * (period - t);
268  return v;
269 }
270 
271 /**
272  Calculate the speed and position at specified period given the
273  current acceleration sequence.
274 
275  \param stepper_index Index of the stepper the callbak is intended for.
276  \param period The period to calculate for
277  \param Speed return
278  \param Position return. In fractional steps
279 
280 */
281 StepperResult
282 stepper_state_at(unsigned int stepper_index, unsigned long period,
283  long *velocity, long long *position)
284 {
285  long a;
286  long v;
287  long long s;
288  unsigned long t;
289  long dt;
290  StepperState *state;
291  StepperAccSeq *seq;
292  state = &stepper_context.steppers[stepper_index];
293 
294  stepper_context.timer_channel->TC_IDR = AT91C_TC_CPCS | AT91C_TC_CPAS;
295  if (period < stepper_context.period_count + 2) {
296  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
297  return STEPPER_ERR_TOO_LATE;
298  }
299  seq = state->acceleration_sequence;
300  a = state->acceleration;
301  v = state->velocity;
302  t = stepper_context.period_count + 2;
303  s = state->step_full * (long long)DIST_SCALE + state->step_frac;
304  while(seq && seq->period < period) {
305  dt = seq->period - t;
306  s += (a * (long long)dt + 2 * v) * dt;
307  v += a * (seq->period - t);
308  t = seq->period;
309  a = seq->acceleration;
310  seq = seq->next;
311  }
312  stepper_context.timer_channel->TC_IER = AT91C_TC_CPCS | AT91C_TC_CPAS;
313  dt = period - t;
314  *position = s + (a * (long long)dt + (DIST_SCALE/VEL_SCALE) * v) * dt;
315  *velocity = v + a * dt;
316 
317  return STEPPER_OK;
318 }
319 
320 
321 StepperResult
322 stepper_set_velocity(unsigned int stepper_index, unsigned long *periodp,
323  unsigned long max_acc, long final_speed)
324 {
325  long start_period = *periodp;
326  long v = stepper_velocity(stepper_index, start_period);
327  /* printf("%ld @ %ld\n", v, start_period); */
328  if (final_speed == v) {
329  return stepper_add_acc(stepper_index, start_period, 0);
330  } else {
331  StepperResult res;
332  long a = (final_speed > v) ? max_acc : -max_acc;
333  long t = ((long)(final_speed - v)) / a;
334  long diff = (final_speed - v) - t * a;
335  if (t > 0) {
336  res = stepper_add_acc(stepper_index, start_period, a);
337  if (res != STEPPER_OK) return res;
338  }
339  if (diff) {
340  res = stepper_add_acc(stepper_index, start_period+t, diff);
341  if (res != STEPPER_OK) return res;
342  t++;
343  }
344  *periodp = start_period+t;
345  return stepper_add_acc(stepper_index, start_period+t, 0);
346  }
347 }
348 
349 #ifdef TIMING_ERRORS
350 void
351 stepper_timing_errors(unsigned int stepper_index, long *min, long *max)
352 {
353  StepperState *state;
354  state = &stepper_context.steppers[stepper_index];
355  *min = state->err_min;
356  *max = state->err_max;
357  state->err_max = -TIMER_FREQ;
358  state->err_min = TIMER_FREQ;
359 }
360 #endif