/* * 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) */ // ramp state-machine states enum ramp_state { ramp_idle = 0, ramp_up = 1, ramp_max = 2, ramp_down = 3, ramp_last = 4, }; enum ramp_state ramp_sts=ramp_idle; int16 motor_pos = 0; // absolute step number int16 pos_inc=0; // motor_pos increment uint16 phase=0; // ccpPhase[phase_ix] uint8 phase_ix=0; // index to ccpPhase[] uint8 phase_inc; // phase_ix increment uint8 run_flg; // true while motor is running // *************************** // 1. keep track of total delay count uint32 ccpr; // 24.8 fixed point delay count uint32 c; // 24.8 fixed point delay count increment uint16 step_no; // progress of move uint16 step_down; // start of down-ramp uint16 move; // total steps to move uint16 midpt; // midpoint of move int16 denom; // 4.n+1 in ramp algo // Config data to make CCP1&2 generate quadrature sequence on PHASE pins // Action on CCP match: 8=set+irq; 9=clear+irq uint16 const ccpPhase[] = {0x909, 0x908, 0x808, 0x809}; // 00,01,11,10 void current_on(){/* code as needed */} // motor drive current void current_off(){/* code as needed */} // reduce to holding value uint16 make16(uint8 l, uint8 r) { return (uint16) l << 8 + r; } // compiler-specific ISR declaration void __interrupt isr_motor_step(void) { // CCP1 match -> step pulse + IRQ ccpr += c; // next comparator value switch (ramp_sts) { case ramp_up: // accel if (step_no == midpt) { // midpoint: decel ramp_sts = ramp_down; // *************************** // 2. convert shift to multiplication // 3. avoid negative result from subtraction of unsigned values // denom = ((step_no - move) << 2) + 1; if(step_no > move) denom = ((step_no - move) * 4) + 1; else denom = ((move - step_no) * 4) - 1; if (!(move & 1)) { // even move: repeat last delay before decel denom += 4; break; } } // no break: share code for ramp algo case ramp_down: // decel if (step_no == move - 1) { // next irq is cleanup (no step) ramp_sts = ramp_last; break; } denom += 4; // calculate increment/decrement in delay count // *************************** // 3. avoid negative result from subtraction of unsigned values // c -= (c << 1) / denom; // ramp algorithm if(denom > 0) c -= (c << 1) / denom; else c += (c << 1) / -denom; if (c <= C_MIN) { // go to constant speed ramp_sts = ramp_max; step_down = move - step_no; c = C_MIN; break; } break; case ramp_max: // constant speed if (step_no == step_down) { // start decel ramp_sts = ramp_down; // *************************** // 2. convert shift to multiplication // 3. avoid negative result from subtraction of unsigned values // denom = ((step_no - move) << 2) + 1; denom = 5 - ((move - step_no) * 4); } break; default: // last step: cleanup ramp_sts = ramp_idle; current_off(); // reduce motor current to holding value CCP1IE = 0; // disable_interrupts(INT_CCP1); run_flg = false; // move complete break; } // switch (ramp_sts) if (ramp_sts != ramp_idle) { motor_pos += pos_inc; ++step_no; CCPR2H = CCPR1H = (ccpr >> 8); // timer value at next CCP match CCPR2L = CCPR1L = (ccpr & 0xff); if (ramp_sts != ramp_last) // else repeat last action: no step phase_ix = (phase_ix + phase_inc) & 3; phase = ccpPhase[phase_ix]; CCP1CON = phase & 0xff; // set CCP action on next match CCP2CON = phase >> 8; } // if (ramp_sts != ramp_idle) } // isr_motor_step() void motor_run(int16 pos_new) { // set up to drive motor to pos_new (absolute step#) if (pos_new < motor_pos) { // get direction & #steps move = motor_pos - pos_new; pos_inc = -1; phase_inc = 0xff; } else if (pos_new != motor_pos) { move = pos_new - motor_pos; pos_inc = 1; phase_inc = 1; } else return; // already there midpt = (move - 1) >> 1; c = C0; step_no = 0; // step counter denom = 1; // 4.n+1, n=0 ramp_sts = ramp_up; // start ramp state-machine run_flg = true; T1CONbits.TMR1ON = 0; // stop timer1; ccpr = make16(TMR1H, TMR1L); // 16bit value of Timer1 ccpr += 1000; // 1st step + irq 1ms after timer1 restart CCPR2H = CCPR1H = (ccpr >> 8); CCPR2L = CCPR1L = (ccpr & 0xff); phase_ix = (phase_ix + phase_inc) & 3; phase = ccpPhase[phase_ix]; CCP1CON = phase & 0xff; // sets action on match CCP2CON = phase >> 8; current_on(); // current in motor windings CCP1IE = 1; // enable_interrupts(INT_CCP1); T1CONbits.TMR1ON = 1; // restart timer1; } // motor_run() void initialize() { di(); // disable_interrupts(GLOBAL); CCP1IE = 0; // disable_interrupts(INT_CCP1); CCP2IE = 0; // disable_interrupts(INT_CCP2); PORTC = 0; // output_c(0); TRISC = 0; // set_tris_c(0); T3CON = 0; T1CON = 0x35; INTCONbits.PEIE = 1; INTCONbits.RBIF = 0; ei(); // enable_interrupts(GLOBAL); } // initialize()