Modified phase table in stepper.cpp to enable stepping a bipolar motor using a twin H-bridge driver.

Fork of stepper by Kenji Arai

Corrected single-stepping, now walking up or down just one phase table. Compile-time options for driving bipolar motor in any of single-phase, two-phase, or half-stepping. Coils remain engaged at end of specifed movement command - de-energize coils by issuing a motor.move(0) while already stopped.

stepper.cpp

Committer:
gregeric
Date:
2014-12-31
Revision:
8:75fcbd49d49f
Parent:
7:9fc4b1be489c

File content as of revision 8:75fcbd49d49f:

/*
 * mbed library program
 *      Stepping Motor
 *
 * Copyright (c) 2014 Kenji Arai / JH1PJL
 *  http://www.page.sannet.ne.jp/kenjia/index.html
 *  http://mbed.org/users/kenjiArai/
 *      Created: August    20th, 2014
 *      Revised: August    23rd, 2014
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "mbed.h"
#include "stepper.h"

//uncomment one option
//in truth, both uni- & bi- are equivalent, and just represent a different wiring of mcu GPIOs to H-Bridge
//TODO: adopt one most-prevalent wiring scheme only, run-time selectable wiring & step pattern?

//#define BIPOLAR_STEPPER_1PH
//#define BIPOLAR_STEPPER_2PH
#define BIPOLAR_STEPPER_12S
//#define UNIPOLAR_STEPPER_1PH
//#define UNIPOLAR_STEPPER_2PH
//#define UNIPOLAR_STEPPER_12S
/*
 * Firing sequence for bi-polar (4 wires) stepper (2x H-Bridge driver required eg L298)
 */
#ifdef BIPOLAR_STEPPER_1PH
//single phase A+, B+, A-, B-
//mcu wiring to H-bridge:        A A'  B B'
const uint8_t pase_cw[4][4]  = {{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}};
#endif

#ifdef BIPOLAR_STEPPER_2PH
//double phase A+B+, A-B+, A-B-, A+B-
const uint8_t pase_cw[4][4]  = {{1, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 1}, {1, 0, 0, 1}};
#endif

#ifdef BIPOLAR_STEPPER_12S
//half step A+B+, A+, A+B-, B-, A-B-, A-, A-B+, B+
const uint8_t pase_cw[8][4] = {{1, 0, 1, 0}, {1, 0, 0, 0}, {1, 0, 0, 1}, {0, 0, 0, 1}, {0, 1, 0, 1}, {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}};
#endif

/*
 * Firing sequence for uni-polar (5+ wires) stepper (4x open-collector drivers required).
 */
#ifdef UNIPOLAR_STEPPER_1PH
//mcu wiring to Driver/FETs      A+ B+ A- B-
const uint8_t pase_cw[4][4]  = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
#endif

#ifdef UNIPOLAR_STEPPER_2PH
//mcu wiring to Driver/FETs      A+ B+ A- B-
const uint8_t pase_cw[4][4]  = {{1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 1}, {1, 0, 0, 1}};
#endif

#ifdef UNIPOLAR_STEPPER_12S
//mcu wiring to Driver/FETs      A+ B+ A- B-
const uint8_t pase_cw[8][4]  = {{1, 1, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}};
#endif

//declare as pointer, not array, so that we can swap to different acc/deceleration arrays on the fly.
extern uint8_t *pls_width;

STEPPER::STEPPER (PinName xp, PinName xn, PinName yp, PinName yn):
    _xp(xp), _xn(xn), _yp(yp), _yn(yn)
{
    stop();
}

void STEPPER::move (int32_t steps)
{
    if (steps < 0) {
        inf.direction = D_CCW;
        steps = -steps;
    }
    // test for +ve, don't flip direction for stop command steps==0
    else if (steps > 0) {
        inf.direction = D_CW;
    }
    inf.total_step = steps;
    setup_mtr_drv_dt(&inf, &cntl);
}

void STEPPER::set_max_speed (uint32_t time_base_us)
{
    if (time_base_us < (MT_PLS_WIDTH_MIN * 1000)) {
        time_base_us = (MT_PLS_WIDTH_MIN * 1000);
    }
    _smdrv.detach();
    _smdrv.attach_us(this, &STEPPER::millisec_inteval, time_base_us);
}

uint8_t STEPPER::status (void)
{
    return cntl.state;
}

void STEPPER::stop(void)
{
    busy_sm_drv = 0;
    _xp = 0;
    _xn = 0;
    _yp = 0;
    _yn = 0;
}

void STEPPER::set4ports (void)
{
    uint8_t i;
    cntl.motor_step+=inf.direction;
    i = (uint8_t)(cntl.motor_step % (sizeof(pase_cw)/4) );
    _xp = pase_cw[i][0];
    _xn = pase_cw[i][1];
    _yp = pase_cw[i][2];
    _yn = pase_cw[i][3];
}

void STEPPER::setup_mtr_drv_dt(Motor_Inf *mi, Motor_Control *mt)
{
    busy_sm_drv = 1;

    //0-steps command: if moving, bring to controlled stop & hold position
    if (mi->total_step == 0) {
        if (mt->state != M_STOP) {
            mt->state = M_CHANGE;
            mt->change_cnt = 5;
            mt->pls_width = 0;
            mt->ongoing = 0;
            mt->up_cnt = 0;
            mt->up_cnt_keep = 0;
            mt->down_cnt = 0;
            mt->continue_cnt = 0;
        } //else init(); // already stopped, interpret as: release HOLD currents, power down coils, motor freewheeling. init() now public, renamed stop()
        busy_sm_drv = 0;
        return;
    }

    //already travelling full speed, new step in same direction
    if ((mt->state == M_CONTINUE) && ( mt->direction == mi->direction)) {
        if (mi->total_step < MT_SLOP_STEP) { //step len shorter than ramp down ledn
            mt->up_cnt = 0;
            mt->up_cnt_keep = 0;
            mt->down_cnt = mi->total_step;
            mt->continue_cnt = 0;
            mt->state = M_DOWN;
            mt->ongoing = 0;
        } else {
            mt->up_cnt = 0;
            mt->up_cnt_keep = 0;
            mt->down_cnt = MT_SLOP_STEP -1;
            mt->continue_cnt = mi->total_step - (MT_SLOP_STEP - 1);
        }
    } else {
        //already moving, reverse direction required
        if ((mt->state == M_CONTINUE) && ( mt->direction != mi->direction)) {
            mt->state = M_CHANGE;
            mt->change_cnt = 5;
        } else {//motor was at rest?
            //mt->motor_step = 0; // don't destroy knowledge of current phase
            mt->state = M_UP;
        }
        mt->pls_width = 0;
        mt->ongoing = 0;
        mt->direction = mi->direction;
        if (mi->total_step < MT_MIN_STEP) {
            if (mi->total_step == MT_MIN_STEP - 1) {
                mt->up_cnt = MT_SLOP_STEP;
            } else {
                mt->up_cnt = mi->total_step / 2;
                if (mt->up_cnt==0) mt->up_cnt=1;
            }
            mt->up_cnt_keep = mt->up_cnt;
            mt->down_cnt = mi->total_step - mt->up_cnt;
            mt->continue_cnt = 0;
        } else {
            mt->up_cnt = MT_SLOP_STEP;
            mt->up_cnt_keep = mt->up_cnt;
            mt->down_cnt = MT_SLOP_STEP -1 ;
            mt->continue_cnt = mi->total_step - MT_SLOP_STEP - (MT_SLOP_STEP - 1);
        }
    }
    busy_sm_drv = 0;
}

void STEPPER::millisec_inteval()
{
    if (busy_sm_drv == 1) {
        return;
    }
    switch (cntl.state) {
        case M_STOP:
            /*
             * no move, but continue to energize coils: hold current position
            */
            break;
        case M_UP:
            if (cntl.ongoing) {
                if (--cntl.pls_width == 0) {
                    if (--cntl.up_cnt == 0) {
                        cntl.ongoing = 0;
                        if (cntl.continue_cnt == 0) {
                            cntl.state = M_DOWN;
                            if (cntl.down_cnt==0) cntl.state=M_STOP;
                        } else {
                            cntl.state = M_CONTINUE;
                        }
                    } else {
                        set4ports();
                        cntl.pls_width = pls_width[cntl.up_cnt_keep - cntl.up_cnt];
                    }
                } else {
                    break;
                }
            } else {    // 1st entry from M_STOP
                set4ports();
                cntl.pls_width = pls_width[cntl.up_cnt_keep - cntl.up_cnt];
                cntl.ongoing = 1;
            }
            break;
        case M_CONTINUE:
            set4ports();
            if (--cntl.continue_cnt == 0) {
                cntl.ongoing = 0;
                cntl.state = M_DOWN;
            }
            break;
        case M_DOWN:
            if (cntl.ongoing) {
                if (--cntl.pls_width == 0) {
                    if (--cntl.down_cnt == 0) {
                        cntl.ongoing = 0;
                        cntl.state = M_STOP;
                    } else {
                        set4ports();
                        cntl.pls_width = pls_width[cntl.down_cnt];
                    }
                } else {
                    break;
                }
            } else {    // 1st entry from M_UP or M_CONTINUE
                set4ports();
                cntl.pls_width = pls_width[cntl.down_cnt];
                cntl.ongoing = 1;
            }
            break;
        case M_CHANGE:
            if (cntl.ongoing) {
                if (--cntl.pls_width == 0) {
                    if (--cntl.change_cnt == 0) {
                        cntl.ongoing = 0;
                        if (cntl.up_cnt == 0) {
                            cntl.state = M_STOP;
                        } else {
                            cntl.state = M_UP;
                        }
                    } else {
                        set4ports();
                        cntl.pls_width = pls_width[cntl.change_cnt];
                    }
                } else {
                    break;
                }
            } else {    // 1st entry
                set4ports();
                cntl.pls_width = pls_width[cntl.change_cnt];
                cntl.ongoing = 1;
            }
            break;
        default :
            cntl.state = M_STOP;
    }
}