3 * http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
6 * Demo program for stepper motor control with linear ramps
7 * Hardware: PIC18F252, L6219
9 * Copyright (c) 2015 Robert Ramey
11 * Distributed under the Boost Software License, Version 1.0. (See
12 * accompanying file LICENSE_1_0.txt or copy at
13 * http://www.boost.org/LICENSE_1_0.txt)
18 // ramp state-machine states
26 // ***************************
27 // 1. Define state variables using custom strong types
30 enum ramp_state ramp_sts
;
31 position_t motor_position
;
32 position_t m
; // target position
33 position_t m2
; // midpoint or point where acceleration changes
34 direction_t d
; // direction of traval -1 or +1
36 // curent state along travel
37 step_t i
; // step number
38 c_t c
; // 24.8 fixed point delay count increment
39 ccpr_t ccpr
; // 24.8 fixed point delay count
40 phase_ix_t phase_ix
; // motor phase index
42 // ***************************
43 // 2. Surround all literal values with the "literal" keyword
45 // Config data to make CCP1&2 generate quadrature sequence on PHASE pins
46 // Action on CCP match: 8=set+irq; 9=clear+irq
47 phase_t
const ccpPhase
[] = {
54 void current_on(){/* code as needed */} // motor drive current
55 void current_off(){/* code as needed */} // reduce to holding value
57 // ***************************
58 // 3. Refactor code to make it easier to understand
59 // and relate to the documentation
62 return ramp_idle
!= ramp_sts
;
65 // set outputs to energize motor coils
66 void update(ccpr_t ccpr
, phase_ix_t phase_ix
){
67 // energize correct windings
68 const phase_t phase
= ccpPhase
[phase_ix
];
69 CCP1CON
= phase
& literal(0xff); // set CCP action on next match
70 CCP2CON
= phase
>> literal(8);
71 // timer value at next CCP match
72 CCPR1H
= literal(0xff) & (ccpr
>> literal(8));
73 CCPR1L
= literal(0xff) & ccpr
;
76 // compiler-specific ISR declaration
77 // ***************************
78 // 4. Rewrite interrupt handler in a way which mirrors the orginal
79 // description of the algorithm and minimizes usage of state variable,
80 // accumulated values, etc.
81 void __interrupt
isr_motor_step(void) { // CCP1 match -> step pulse + IRQ
82 // *** possible exception
83 // motor_position += d;
84 // use the following to avoid mixing exception policies which is an error
89 // *** possible exception
91 // calculate next difference in time
94 case ramp_up
: // acceleration
100 // *** possible negative overflow on update of c
101 c
-= literal(2) * c
/ (literal(4) * i
+ literal(1));
104 ramp_sts
= ramp_const
;
105 // *** possible exception
106 m2
= m
- i
; // new inflection point
110 case ramp_const
: // constant speed
112 ramp_sts
= ramp_down
;
116 case ramp_down
: // deceleration
118 ramp_sts
= ramp_idle
;
119 current_off(); // reduce motor current to holding value
120 CCP1IE
= literal(0); // disable_interrupts(INT_CCP1);
124 // *** possible positive overflow on update of c
125 // note: re-arrange expression to avoid negative result
126 // from difference of two unsigned values
127 c
+= literal(2) * c
/ (literal(4) * (m
- i
) - literal(1));
133 // should never arrive here!
135 } // switch (ramp_sts)
138 assert(c
<= C0
&& c
>= C_MIN
);
139 // *** possible exception
140 ccpr
= literal(0xffffff) & (ccpr
+ c
);
141 phase_ix
= (phase_ix
+ d
) & literal(3);
142 update(ccpr
, phase_ix
);
143 } // isr_motor_step()
145 // set up to drive motor to pos_new (absolute step#)
146 void motor_run(position_t new_position
) {
147 if(new_position
> motor_position
){
149 // *** possible exception
150 m
= new_position
- motor_position
;
153 if(motor_position
> new_position
){
155 // *** possible exception
156 m
= motor_position
- new_position
;
161 ramp_sts
= ramp_idle
; // start ramp state-machine
168 ramp_sts
= ramp_up
; // start ramp state-machine
170 T1CONbits
.TMR1ON
= literal(0); // stop timer1;
172 current_on(); // current in motor windings
175 ccpr
= (TMR1H
<< literal(8) | TMR1L
) + C0
+ literal(1000);
176 phase_ix
= d
& literal(3);
177 update(ccpr
, phase_ix
);
179 CCP1IE
= literal(1); // enable_interrupts(INT_CCP1);
180 T1CONbits
.TMR1ON
= literal(1); // restart timer1;
184 di(); // disable_interrupts(GLOBAL);
185 motor_position
= literal(0);
186 CCP1IE
= literal(0); // disable_interrupts(INT_CCP1);
187 CCP2IE
= literal(0); // disable_interrupts(INT_CCP2);
188 PORTC
= literal(0); // output_c(0);
189 TRISC
= literal(0); // set_tris_c(0);
191 T1CON
= literal(0x35);
192 INTCONbits
.PEIE
= literal(1);
193 INTCONbits
.RBIF
= literal(0);
194 ei(); // enable_interrupts(GLOBAL);