437 lines
15 KiB
C

/*
* geany_encoding=koi8-r
* motors.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "motors.h"
#include "uart.h"
#include "hardware.h"
//U8 irq_flag = 0; // |1 - 1st motor, |2 - second
// motor states
typedef enum{
MOTOR_RELAX, // do nothing
MOTOR_INFMOVE, // infinite moving
MOTOR_STOP, // stop
MOTOR_ZEROSTOP, // stop and set current position to zero
MOTOR_MOVENSTEPS, // move for N steps
MOTOR_OFFSWITCH, // try to pull off the positive switch (for turrets and to return from switch back)
} motor_state;
// direction of rotation
typedef enum{
DIR_CW, // clockwise
DIR_CCW, // counter-clockwise
DIR_STOP // stopped
} motor_direction;
static U16 Stepper_speed[2] = {DEFAULT_USTEP_PERIOD, DEFAULT_USTEP_PERIOD}; // length of one MICROstep in us
static volatile motor_direction Dir[2] = {DIR_STOP, DIR_STOP}; // direction of moving
static volatile motor_state state[2] = {MOTOR_RELAX, MOTOR_RELAX};
static long Steps_left[2] = {0,0}; // steppers left (when moving for given steps amount)
static long Current_pos[2] = {0,0}; // current position
static long Steps_left_at_esw[2] = {0,0}; // amount of `Steps_left` when MOTOR_OFFSWITCH activated
static U16 Acceleration[2] = {0, 0}; // current acceleration (to reach target speed by ACCEL_STEPS microsteps)
// microsteps profile
// microsteps: DCBA = 1000, 1010, 0010, 0110, 0100, 0101, 0001, 1001 - half-step
// 1010, 0110, 0101, 1001 - full step
static const U8 usteps[8] = {0b1000, 0b1010, 0b0010, 0b0110, 0b0100, 0b0101, 0b0001, 0b1001};
static U8 Ustep[2] = {0, 0}; // microstep counter
// init timers & GPIO for motors
void motors_init(){
// Configure timer 2 to generate signals for CLK of motor 0
TIM2_PSCR = 4; // 1MHz
TIM2_ARRH = DEFAULT_USTEP_PERIOD >> 8; // set speed
TIM2_ARRL = DEFAULT_USTEP_PERIOD & 0xff;
TIM2_IER = TIM_IER_UIE; // update interrupt enable
TIM2_CR1 |= TIM_CR1_APRE | TIM_CR1_URS; // auto reload + interrupt on overflow
// timer 3 for motor 1
TIM3_PSCR = 4;
TIM3_ARRH = DEFAULT_USTEP_PERIOD >> 8;
TIM3_ARRL = DEFAULT_USTEP_PERIOD & 0xff;
TIM3_IER = TIM_IER_UIE;
TIM3_CR1 |= TIM_CR1_APRE | TIM_CR1_URS;
}
void show_motors_help(){
// "start end"
uart_write("\tE - get end-switches\n");
uart_write("\tL - move CCW\n");
uart_write("\tM - get motor state\n");
uart_write("\tN - go for N st./get rest\n");
uart_write("\tO - pull off the switch\n");
uart_write("\tP - get current position\n");
uart_write("\tR - move CW\n");
uart_write("\tS - get/set speed\n");
uart_write("\tX - stop motor\n");
uart_write("\tZ - stop and zero position\n");
}
/**
* Check endswitches
* @return 0 if none pressed, 1 if "-", 2 if "+", 3 if both!
*/
static U8 check_endsw(U8 motor){
U8 ret = 0;
switch(motor){
case 0:
if(CHK_M0E1()) ++ret;
if(CHK_M0E2()) ret += 2;
break;
case 1:
if(CHK_M1E1()) ++ret;
if(CHK_M1E2()) ret += 2;
break;
default:
return 0;
}
return ret;
}
static void stop_motor(U8 motorNum){
// stop timers & turn off power
switch(motorNum){
case 0:
PORT(STP0_PORT, ODR) &= ~STP_PINS;
TIM2_CR1 &= ~TIM_CR1_CEN;
break;
case 1:
PORT(STP1_PORT, ODR) &= ~STP_PINS;
TIM3_CR1 &= ~TIM_CR1_CEN;
break;
default: return;
}
Steps_left[motorNum] = 0;
Dir[motorNum] = DIR_STOP;
if(state[motorNum] == MOTOR_ZEROSTOP) Current_pos[motorNum] = 0;
state[motorNum] = MOTOR_RELAX;
}
static void strtobuf(const char *str, char **buff){
while(*str) *((*buff)++) = *str++;
}
static void get_motor_state(U8 nmotor, char **buff){
char sig = '+';
if(nmotor > 1) return;
if(Dir[nmotor] == DIR_CCW) sig = '-';
switch(state[nmotor]){
case MOTOR_RELAX:
strtobuf("RELAX", buff);
break;
case MOTOR_INFMOVE:
strtobuf("INFMV", buff); // INVMV+ or INFMV-
*((*buff)++) = sig;
break;
break;
case MOTOR_STOP:
strtobuf("STOP", buff);
break;
case MOTOR_MOVENSTEPS:
strtobuf("MVSTP", buff); // MVSTP+ or MVSTP-
*((*buff)++) = sig;
break;
case MOTOR_OFFSWITCH:
strtobuf("OFFSW", buff); // OFFSW+ or OFFSW-
*((*buff)++) = sig;
break;
default:
strtobuf("UNDEF", buff);
}
}
// turn on motor's timer starting from the lowest speed
static void turnontimer(U8 motorNum){
U8 tmp;
switch(motorNum){
case 0:
// turn on power
tmp = PORT(STP0_PORT, ODR) & ~STP_PINS;
PORT(STP0_PORT, ODR) = tmp | usteps[Ustep[0]];
// start from the slowest speed
TIM2_ARRH = MAX_USTEP_PERIOD >> 8;
TIM2_ARRL = MAX_USTEP_PERIOD & 0xff;
// run timer
TIM2_CR1 |= TIM_CR1_CEN;
break;
case 1:
tmp = PORT(STP1_PORT, ODR) & ~STP_PINS;
PORT(STP1_PORT, ODR) = tmp | usteps[Ustep[1]];
TIM3_ARRH = MAX_USTEP_PERIOD >> 8;
TIM3_ARRL = MAX_USTEP_PERIOD & 0xff;
TIM3_CR1 |= TIM_CR1_CEN;
break;
default: return;
}
Acceleration[motorNum] = 1 + (MAX_USTEP_PERIOD - Stepper_speed[motorNum]) / ACCEL_USTEPS;
}
/**
* try to move motor motorNum for nsteps
* @return 1 if all OK; 0 if still moving or on end-switch
*/
static int moveNsteps(U8 motorNum, long nsteps){
U8 sw;
if(Dir[motorNum] != DIR_STOP) return 0;
sw = check_endsw(motorNum);
if(nsteps < 0){
if(sw) return 0; // on zero end-switch: no moving backward, on positive - no moving at all!
Dir[motorNum] = DIR_CCW;
nsteps = -nsteps;
state[motorNum] = MOTOR_MOVENSTEPS;
}else{
if(sw & 2) return 0; // for positive direction no moving to any side when on end-switch 2!
else if(sw & 1) state[motorNum] = MOTOR_OFFSWITCH;
else state[motorNum] = MOTOR_MOVENSTEPS;
Dir[motorNum] = DIR_CW;
}
Steps_left[motorNum] = nsteps;
// turn On timer
turnontimer(motorNum);
return 1;
}
/**
* try to move motor motorNum off the end-switch for nsteps
* @return 1 if all OK; 0 if still moving, motor is @ zero endswitch
*/
static int pullofftheswitch(U8 motorNum, long nsteps){
U8 sw;
if(Dir[motorNum] != DIR_STOP) return 0;
sw = check_endsw(motorNum);
if(sw == 0) return moveNsteps(motorNum, nsteps);
if(nsteps < 0){
if(sw & 1) return 0; // on zero end-switch: no moving backward
Dir[motorNum] = DIR_CCW;
nsteps = -nsteps;
}else{
Dir[motorNum] = DIR_CW;
}
Steps_left[motorNum] = nsteps;
Steps_left_at_esw[motorNum] = nsteps;
state[motorNum] = MOTOR_OFFSWITCH;
turnontimer(motorNum);
return 1;
}
void motor_command(const char *cmd, char **buff){
U8 motorNum = *cmd++ - '0';
U16 spd;
long l;
char c;
if(motorNum > 1) goto someerr;
*((*buff)++) = '0' + motorNum;
*((*buff)++) = ' ';
c = *cmd++;
*((*buff)++) = c;
switch(c){
case 'E': // check endswitches state
*((*buff)++) = ' ';
*((*buff)++) = '0' + check_endsw(motorNum);
break;
case 'L': // infinite move left
if(check_endsw(motorNum) & 1){
strtobuf(" E 1", buff);
}else{
state[motorNum] = MOTOR_INFMOVE;
Dir[motorNum] = DIR_CCW;
turnontimer(motorNum);
}
break;
case 'M': // get motor state
*((*buff)++) = ' ';
get_motor_state(motorNum, buff);
break;
case 'N': // go for N steps or get steps left
*((*buff)++) = ' ';
if(!readLong(cmd, &l)){ // get
long2buf(Steps_left[motorNum], buff);
}else{
if(!moveNsteps(motorNum, l)) goto someerr;
else long2buf(l, buff);
}
break;
case 'O': // pull off the switch (if no steps given, go for PULLOFFTHESW_STEPS)
*((*buff)++) = ' ';
if(!readLong(cmd, &l)){ // get
l = PULLOFFTHESW_STEPS;
}
if(!pullofftheswitch(motorNum, l)) goto someerr;
else long2buf(l, buff);
break;
case 'P': // get current position
*((*buff)++) = ' ';
long2buf(Current_pos[motorNum], buff);
break;
case 'R': // infinite move right
if(check_endsw(motorNum) & 2){
strtobuf(" E 2", buff);
}else{
state[motorNum] = MOTOR_INFMOVE;
Dir[motorNum] = DIR_CW;
turnontimer(motorNum);
}
break;
case 'S': // change speed
*((*buff)++) = ' ';
if(!readLong(cmd, &l) || l < MIN_USTEP_PERIOD || l > MAX_USTEP_PERIOD){ // get speed
if(motorNum == 0) spd = TIM2_ARRH << 8 | TIM2_ARRL;
else spd = TIM3_ARRH << 8 | TIM3_ARRL;
long2buf(spd, buff);
}else{
long2buf(l, buff);
spd = (U16)l;
Stepper_speed[motorNum] = spd;
Acceleration[motorNum] = 0;
if(motorNum == 0){
TIM2_ARRH = spd >> 8;
TIM2_ARRL = spd & 0xff;
}else{
TIM3_ARRH = spd >> 8;
TIM3_ARRL = spd & 0xff;
}
}
break;
case 'X': // stop
state[motorNum] = MOTOR_STOP;
break;
case 'Z':
state[motorNum] = MOTOR_ZEROSTOP;
break;
default: // return err
goto someerr;
}
return;
someerr:
*((*buff)++) = 'e';
*((*buff)++) = 'r';
*((*buff)++) = 'r';
}
/**
* this function calls from timer interrupt (TIM2 or TIM3 - motors 0/1)
*/
void stepper_interrupt(U8 motor_num){
U8 tmp;
U16 spd;
U8 sw = check_endsw(motor_num), st = state[motor_num];
U8 ccw = (Dir[motor_num] == DIR_CCW) ? 1 : 0;
//irq_flag ^= 1 << motor_num;
switch(motor_num){
case 0:
if(Acceleration[0]){
spd = (TIM2_ARRH << 8 | TIM2_ARRL) - Acceleration[0];
if(spd < Stepper_speed[0]){
spd = Stepper_speed[0];
Acceleration[0] = 0;
}
//printUint((U8*)&spd, 2);
//uart_write(" - speed0\n");
TIM2_ARRH = spd >> 8;
TIM2_ARRL = spd & 0xff;
}
tmp = PORT(STP0_PORT, ODR) & ~STP_PINS;
PORT(STP0_PORT, ODR) = tmp | usteps[Ustep[0]];
break;
case 1:
if(Acceleration[1]){
spd = (TIM3_ARRH << 8 | TIM3_ARRL) - Acceleration[1];
if(spd < Stepper_speed[1]){
spd = Stepper_speed[1];
Acceleration[1] = 0;
}
//printUint((U8*)&spd, 2);
//uart_write(" - speed1\n");
TIM3_ARRH = spd >> 8;
TIM3_ARRL = spd & 0xff;
}
tmp = PORT(STP1_PORT, ODR) & ~STP_PINS;
PORT(STP1_PORT, ODR) = tmp | usteps[Ustep[1]];
break;
default: return;
}
if(Ustep[motor_num] % 2 == 0){ // full amount of half-steps - increment step counters & check for stop
if(0 == --Steps_left[motor_num]) st = MOTOR_STOP;
if(ccw) --Current_pos[motor_num];
else ++Current_pos[motor_num];
if(st == MOTOR_STOP || st == MOTOR_ZEROSTOP){
stop_motor(motor_num);
return;
}
}
if(ccw){ // counter-clockwise
if(Ustep[motor_num] == 0) Ustep[motor_num] = 7;
else --Ustep[motor_num];
}else{ // clockwise
if(++Ustep[motor_num] > 7) Ustep[motor_num] = 0;
}
switch(st){
case MOTOR_OFFSWITCH: // don't care about endswitch for first PULLOFFTHESW_STEPS steps
if(Steps_left_at_esw[motor_num] - Steps_left[motor_num] >= PULLOFFTHESW_STEPS)
state[motor_num] = MOTOR_MOVENSTEPS;
break;
case MOTOR_MOVENSTEPS:
case MOTOR_INFMOVE: // set curpos to zero only in this state (after reaching ESW1)
if(sw){
if(ccw){
if(st == MOTOR_INFMOVE){
if(sw & 1) state[motor_num] = MOTOR_ZEROSTOP; // esw1 - stop @ zero when inf. left move
}else state[motor_num] = MOTOR_STOP; // just stop at any esw in steps move
}else{ // +switch when move CW
if(sw & 2) state[motor_num] = MOTOR_STOP; // stop in CW only on esw2 !!!
}
}
break;
default: break;
}
}
/**
* Main state-machine process
*
void process_stepper(U8 motor_num){
U8 sw = check_endsw(motor_num);
U8 ccw = (Dir[motor_num] == DIR_CCW) ? 1 : 0;
switch(state[motor_num]){
case MOTOR_OFFSWITCH: // don't care about endswitch for first PULLOFFTHESW_STEPS steps
if(Steps_left[motor_num] < 2) state[motor_num] = MOTOR_STOP;
else if(Steps_left_at_esw[motor_num] - Steps_left[motor_num] >= PULLOFFTHESW_STEPS)
state[motor_num] = MOTOR_MOVENSTEPS;
break;
case MOTOR_MOVENSTEPS:
if(Steps_left[motor_num] < 2) state[motor_num] = MOTOR_STOP; // stop @given position
case MOTOR_INFMOVE: // set curpos to zero only in this state (after reaching ESW1)
if(sw){
if(ccw){
if(state[motor_num] == MOTOR_INFMOVE){
if(sw & 1) state[motor_num] = MOTOR_ZEROSTOP; // esw1 - stop @ zero when inf. left move
}else state[motor_num] = MOTOR_STOP; // just stop at any esw in steps move
}else{ // +switch when move CW
if(sw & 2) state[motor_num] = MOTOR_STOP; // stop in CW only on esw2 !!!
}
}
break;
default: return; // MOTOR_RELAX
}
}*/