196 lines
6.1 KiB
C
196 lines
6.1 KiB
C
/*
|
|
* david austin
|
|
* http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
|
|
* DECEMBER 30, 2004
|
|
*
|
|
* Demo program for stepper motor control with linear ramps
|
|
* Hardware: PIC18F252, L6219
|
|
*
|
|
* Copyright (c) 2015 Robert Ramey
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0. (See
|
|
* accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
// ramp state-machine states
|
|
enum ramp_state {
|
|
ramp_idle = 0,
|
|
ramp_up = 1,
|
|
ramp_const = 2,
|
|
ramp_down = 3,
|
|
};
|
|
|
|
// ***************************
|
|
// 1. Define state variables using custom strong types
|
|
|
|
// initial setup
|
|
enum ramp_state ramp_sts;
|
|
position_t motor_position;
|
|
position_t m; // target position
|
|
position_t m2; // midpoint or point where acceleration changes
|
|
direction_t d; // direction of traval -1 or +1
|
|
|
|
// curent state along travel
|
|
step_t i; // step number
|
|
c_t c; // 24.8 fixed point delay count increment
|
|
ccpr_t ccpr; // 24.8 fixed point delay count
|
|
phase_ix_t phase_ix; // motor phase index
|
|
|
|
// ***************************
|
|
// 2. Surround all literal values with the "literal" keyword
|
|
|
|
// Config data to make CCP1&2 generate quadrature sequence on PHASE pins
|
|
// Action on CCP match: 8=set+irq; 9=clear+irq
|
|
phase_t const ccpPhase[] = {
|
|
literal(0x909),
|
|
literal(0x908),
|
|
literal(0x808),
|
|
literal(0x809)
|
|
}; // 00,01,11,10
|
|
|
|
void current_on(){/* code as needed */} // motor drive current
|
|
void current_off(){/* code as needed */} // reduce to holding value
|
|
|
|
// ***************************
|
|
// 3. Refactor code to make it easier to understand
|
|
// and relate to the documentation
|
|
|
|
bool busy(){
|
|
return ramp_idle != ramp_sts;
|
|
}
|
|
|
|
// set outputs to energize motor coils
|
|
void update(ccpr_t ccpr, phase_ix_t phase_ix){
|
|
// energize correct windings
|
|
const phase_t phase = ccpPhase[phase_ix];
|
|
CCP1CON = phase & literal(0xff); // set CCP action on next match
|
|
CCP2CON = phase >> literal(8);
|
|
// timer value at next CCP match
|
|
CCPR1H = literal(0xff) & (ccpr >> literal(8));
|
|
CCPR1L = literal(0xff) & ccpr;
|
|
}
|
|
|
|
// compiler-specific ISR declaration
|
|
// ***************************
|
|
// 4. Rewrite interrupt handler in a way which mirrors the orginal
|
|
// description of the algorithm and minimizes usage of state variable,
|
|
// accumulated values, etc.
|
|
void __interrupt isr_motor_step(void) { // CCP1 match -> step pulse + IRQ
|
|
// *** possible exception
|
|
// motor_position += d;
|
|
// use the following to avoid mixing exception policies which is an error
|
|
if(d < 0)
|
|
--motor_position;
|
|
else
|
|
++motor_position;
|
|
// *** possible exception
|
|
++i;
|
|
// calculate next difference in time
|
|
for(;;){
|
|
switch (ramp_sts) {
|
|
case ramp_up: // acceleration
|
|
if (i == m2) {
|
|
ramp_sts = ramp_down;
|
|
continue;
|
|
}
|
|
// equation 13
|
|
// *** possible negative overflow on update of c
|
|
c -= literal(2) * c / (literal(4) * i + literal(1));
|
|
if(c < C_MIN){
|
|
c = C_MIN;
|
|
ramp_sts = ramp_const;
|
|
// *** possible exception
|
|
m2 = m - i; // new inflection point
|
|
continue;
|
|
}
|
|
break;
|
|
case ramp_const: // constant speed
|
|
if(i > m2) {
|
|
ramp_sts = ramp_down;
|
|
continue;
|
|
}
|
|
break;
|
|
case ramp_down: // deceleration
|
|
if (i == m) {
|
|
ramp_sts = ramp_idle;
|
|
current_off(); // reduce motor current to holding value
|
|
CCP1IE = literal(0); // disable_interrupts(INT_CCP1);
|
|
return;
|
|
}
|
|
// equation 14
|
|
// *** possible positive overflow on update of c
|
|
// note: re-arrange expression to avoid negative result
|
|
// from difference of two unsigned values
|
|
c += literal(2) * c / (literal(4) * (m - i) - literal(1));
|
|
if(c > C0){
|
|
c = C0;
|
|
}
|
|
break;
|
|
default:
|
|
// should never arrive here!
|
|
assert(false);
|
|
} // switch (ramp_sts)
|
|
break;
|
|
}
|
|
assert(c <= C0 && c >= C_MIN);
|
|
// *** possible exception
|
|
ccpr = literal(0xffffff) & (ccpr + c);
|
|
phase_ix = (phase_ix + d) & literal(3);
|
|
update(ccpr, phase_ix);
|
|
} // isr_motor_step()
|
|
|
|
// set up to drive motor to pos_new (absolute step#)
|
|
void motor_run(position_t new_position) {
|
|
if(new_position > motor_position){
|
|
d = literal(1);
|
|
// *** possible exception
|
|
m = new_position - motor_position;
|
|
}
|
|
else
|
|
if(motor_position > new_position){
|
|
d = literal(-1);
|
|
// *** possible exception
|
|
m = motor_position - new_position;
|
|
}
|
|
else{
|
|
d = literal(0);
|
|
m = literal(0);
|
|
ramp_sts = ramp_idle; // start ramp state-machine
|
|
return;
|
|
}
|
|
|
|
i = literal(0);
|
|
m2 = m / literal(2);
|
|
|
|
ramp_sts = ramp_up; // start ramp state-machine
|
|
|
|
T1CONbits.TMR1ON = literal(0); // stop timer1;
|
|
|
|
current_on(); // current in motor windings
|
|
|
|
c = C0;
|
|
ccpr = (TMR1H << literal(8) | TMR1L) + C0 + literal(1000);
|
|
phase_ix = d & literal(3);
|
|
update(ccpr, phase_ix);
|
|
|
|
CCP1IE = literal(1); // enable_interrupts(INT_CCP1);
|
|
T1CONbits.TMR1ON = literal(1); // restart timer1;
|
|
} // motor_run()
|
|
|
|
void initialize() {
|
|
di(); // disable_interrupts(GLOBAL);
|
|
motor_position = literal(0);
|
|
CCP1IE = literal(0); // disable_interrupts(INT_CCP1);
|
|
CCP2IE = literal(0); // disable_interrupts(INT_CCP2);
|
|
PORTC = literal(0); // output_c(0);
|
|
TRISC = literal(0); // set_tris_c(0);
|
|
T3CON = literal(0);
|
|
T1CON = literal(0x35);
|
|
INTCONbits.PEIE = literal(1);
|
|
INTCONbits.RBIF = literal(0);
|
|
ei(); // enable_interrupts(GLOBAL);
|
|
} // initialize()
|