mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 18:55:13 +03:00
421 lines
12 KiB
C
421 lines
12 KiB
C
/*
|
|
* This file is part of the Stepper project.
|
|
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "flash.h"
|
|
#include "hardware.h"
|
|
#include "proto.h"
|
|
#include "spi.h"
|
|
#include "steppers.h"
|
|
|
|
static drv_type driver = DRV_NONE;
|
|
|
|
// maximum number of microsteps per driver
|
|
static const uint16_t maxusteps[] = {
|
|
[DRV_NONE] = 0,
|
|
[DRV_NOTINIT] = 0,
|
|
[DRV_MAILF] = 0,
|
|
[DRV_8825] = 32,
|
|
[DRV_4988] = 16,
|
|
[DRV_2130] = 256
|
|
};
|
|
|
|
int32_t mot_position = -1; // current position of motor (from zero endswitch, -1 means inactive)
|
|
uint32_t steps_left = 0; // amount of steps left
|
|
stp_state state = STP_SLEEP;// current state of motor
|
|
// ARR register values: low (max speed), high (min speed = 10% from max), step (1/50(hi-lo))
|
|
static uint16_t stplowarr, stphigharr, stpsteparr;
|
|
static int8_t dir = 0; // moving direction: -1 (negative) or 1 (positive)
|
|
|
|
#if 0
|
|
/**
|
|
* @brief checkDrv - test if driver connected
|
|
*/
|
|
static void checkDrv(){
|
|
uint8_t oldstate;
|
|
// turn power on and wait a little
|
|
SLEEP_ON(); // sleep -> 0
|
|
DRV_DISABLE();
|
|
VIO_ON(); sleep(15);
|
|
// check Vdd
|
|
if(FAULT_STATE()){
|
|
MSG("No power @ Vin, mailfunction\n");
|
|
driver = DRV_MAILF;
|
|
VIO_OFF();
|
|
goto ret;
|
|
}
|
|
SLP_CFG_IN();
|
|
sleep(2);
|
|
// Check is ~SLEEP is in air
|
|
oldstate = SLP_STATE();
|
|
#ifdef EBUG
|
|
if(oldstate) MSG("SLP=1\n"); else MSG("SLP=0\n");
|
|
#endif
|
|
SLEEP_OFF(); SLP_CFG_OUT(); // sleep -> 1
|
|
sleep(2);
|
|
SLP_CFG_IN();
|
|
sleep(2);
|
|
#ifdef EBUG
|
|
if(SLP_STATE()) MSG("SLP=1\n"); else MSG("SLP=0\n");
|
|
#endif
|
|
if(SLP_STATE() != oldstate){
|
|
MSG("~SLP is in air\n");
|
|
if(driver != DRV_2130){
|
|
driver = DRV_NONE;
|
|
VIO_OFF();
|
|
}
|
|
goto ret;
|
|
}
|
|
SLEEP_ON(); SLP_CFG_OUT();
|
|
// check if ~EN is in air
|
|
DRV_ENABLE(); // EN->0
|
|
sleep(2);
|
|
EN_CFG_IN();
|
|
sleep(2);
|
|
oldstate = EN_STATE();
|
|
#ifdef EBUG
|
|
if(oldstate) MSG("EN=1\n"); else MSG("EN=0\n");
|
|
#endif
|
|
DRV_DISABLE(); // EN->1
|
|
EN_CFG_OUT();
|
|
sleep(2);
|
|
EN_CFG_IN();
|
|
sleep(2);
|
|
#ifdef EBUG
|
|
if(EN_STATE()) MSG("EN=1\n"); else MSG("EN=0\n");
|
|
#endif
|
|
if(oldstate != EN_STATE()){
|
|
MSG("~EN is in air\n");
|
|
driver = DRV_NONE;
|
|
VIO_OFF();
|
|
goto ret;
|
|
}
|
|
ret:
|
|
DRV_DISABLE();
|
|
EN_CFG_OUT(); // return OUT conf
|
|
SLEEP_ON();
|
|
SLP_CFG_OUT();
|
|
#ifdef EBUG
|
|
sendbuf();
|
|
#endif
|
|
}
|
|
#endif
|
|
static drv_type ini2130(){ // init 2130: SPI etc.
|
|
if(driver != DRV_2130) return DRV_MAILF;
|
|
VIO_ON();
|
|
spi_setup();
|
|
CS_ACTIVE();
|
|
return DRV_2130;
|
|
}
|
|
|
|
static drv_type ini4988_8825(){ // init 4988 or 8825
|
|
if(driver != DRV_4988 && driver != DRV_8825){
|
|
MSG("Wrong drv\n");
|
|
return DRV_MAILF;
|
|
}
|
|
if(the_conf.microsteps > maxusteps[driver]){
|
|
SEND("Wrong microstepping settings\n");
|
|
return DRV_MAILF;
|
|
}
|
|
if(the_conf.microsteps == 0){
|
|
SEND("Configure microstepping first\n");
|
|
return DRV_MAILF;
|
|
}
|
|
if(driver == DRV_4988) VIO_ON();
|
|
// init microstepping pins and set config
|
|
DRV_RESET_ON(); // reset counters
|
|
UST01_CFG_PP();
|
|
uint8_t PINS = 0;
|
|
if(the_conf.microsteps == 16 && driver == DRV_4988) PINS = 7; // microstepping settings for 4988 in 1/16 differs from 8825
|
|
else PINS = (uint8_t)__builtin_ctz(the_conf.microsteps);
|
|
#ifdef EBUG
|
|
SEND("Microstep PINS=");
|
|
printu(PINS);
|
|
newline(); sendbuf();
|
|
#endif
|
|
// now PINS is M0..M2 settings
|
|
if(PINS & 1) SET_UST0(); else RESET_UST0();
|
|
if(PINS & 2) SET_UST1(); else RESET_UST1();
|
|
if(PINS & 4) SET_UST2(); else RESET_UST2();
|
|
// turn on timer
|
|
timer_setup();
|
|
// recalculate defaults
|
|
stp_chspd();
|
|
DRV_RESET_OFF(); // normal mode
|
|
SLEEP_OFF();
|
|
SEND("Init OK\n");
|
|
return driver;
|
|
}
|
|
|
|
/**
|
|
* @brief initDriver - try to init driver
|
|
* @return driver type
|
|
*/
|
|
drv_type initDriver(){
|
|
stp_stop();
|
|
DRV_DISABLE();
|
|
VIO_OFF();
|
|
SLEEP_ON();
|
|
if(driver != DRV_NOTINIT){ // reset all settings
|
|
MSG("clear GPIO & other setup\n");
|
|
STEP_TIMER_OFF();
|
|
// TODO: turn off SPI & timer
|
|
gpio_setup(); // reset pins control
|
|
}
|
|
driver = the_conf.driver_type;
|
|
//checkDrv();
|
|
state = STP_SLEEP;
|
|
if(driver > DRV_MAX-1) return (driver = DRV_NONE);
|
|
MSG("init pins\n");
|
|
switch(driver){
|
|
case DRV_2130:
|
|
return ini2130();
|
|
break;
|
|
case DRV_4988:
|
|
case DRV_8825:
|
|
return ini4988_8825();
|
|
break;
|
|
default:
|
|
SEND("Set driver type in config\n");
|
|
return driver; // bad driver type
|
|
}
|
|
return driver;
|
|
}
|
|
|
|
uint16_t getMaxUsteps(){
|
|
if(driver > DRV_MAX-1) return 0;
|
|
return maxusteps[driver];
|
|
}
|
|
|
|
static const char *stpstates[] = {
|
|
[STP_SLEEP] = "Relax"
|
|
,[STP_ACCEL] = "Accelerated"
|
|
,[STP_MOVE] = "Moving"
|
|
,[STP_MVSLOW] = "Slowmoving"
|
|
,[STP_DECEL] = "Decelerated"
|
|
,[STP_STOP] = "Stopping"
|
|
,[STP_STOPZERO] = "Stop0"
|
|
,[STP_MOVE0] = "Moveto0"
|
|
,[STP_MOVE1] = "Moveto1"
|
|
};
|
|
const char *stp_getstate(){
|
|
return stpstates[state];
|
|
}
|
|
|
|
static const char *drvtypes[] = {
|
|
[DRV_NONE] = "Absent"
|
|
,[DRV_NOTINIT] = "NotInit"
|
|
,[DRV_MAILF] = "Mailfunction"
|
|
,[DRV_8825] = "DRV8825"
|
|
,[DRV_4988] = "A4988"
|
|
,[DRV_2130] = "TMC2130"
|
|
};
|
|
const char *stp_getdrvtype(){
|
|
return drvtypes[driver];
|
|
}
|
|
|
|
void stp_chspd(){
|
|
uint32_t arrval = DEFTICKSPERSEC / the_conf.motspd;
|
|
arrval /= the_conf.microsteps;
|
|
if(arrval > 0xffff){
|
|
SEND("The speed is too little for this microstepping settings, set min available\n");
|
|
arrval = 0xffff;
|
|
}else if(arrval < TIM15ARRMIN){
|
|
SEND("The speed is too big for this microstepping settings, set max available\n");
|
|
arrval = TIM15ARRMIN;
|
|
}
|
|
stplowarr = (uint16_t)arrval;
|
|
arrval *= LOW_SPEED_DIVISOR;
|
|
if(arrval > 0xffff) arrval = 0xffff;
|
|
stphigharr = (uint16_t)arrval;
|
|
stpsteparr = (stphigharr - stplowarr) / ((uint16_t)the_conf.accdecsteps);
|
|
if(stpsteparr < 1) stpsteparr = 1;
|
|
#ifdef EBUG
|
|
SEND("stplowarr="); printu(stplowarr);
|
|
SEND("\nstphigharr="); printu(stphigharr);
|
|
SEND("\nstpsteparr="); printu(stpsteparr);
|
|
newline();
|
|
#endif
|
|
}
|
|
|
|
// check end-switches for stepper motors
|
|
void stp_process(){
|
|
// check end-switches; ESW0&ESW3 stops motor
|
|
uint8_t esw = ESW_STATE();
|
|
switch(state){
|
|
case STP_MOVE0: // move towards ESW0
|
|
state = STP_SLEEP;
|
|
stp_move(-the_conf.maxsteps); // won't move if the_conf.maxsteps == 0
|
|
break;
|
|
case STP_MOVE1: // move towards ESW3
|
|
state = STP_SLEEP;
|
|
stp_move(the_conf.maxsteps);
|
|
break;
|
|
case STP_ACCEL: // @ any move check esw
|
|
case STP_DECEL:
|
|
case STP_MOVE:
|
|
case STP_MVSLOW:
|
|
if((esw&1) && dir == -1){ // move through ESW0
|
|
state = STP_STOPZERO; // stop @ end-switch
|
|
MSG("ESW0 active\n");
|
|
}else if((esw&8) && dir == 1){ // move through ESW3
|
|
state = STP_STOP; // stop @ ESW3
|
|
MSG("ESW3 active\n");
|
|
}
|
|
break;
|
|
default: // stopping states - do nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
// move motor to `steps` steps, @return 0 if all OK
|
|
stp_status stp_move(int32_t steps){
|
|
if(state != STP_SLEEP && state != STP_MOVE0 && state != STP_MOVE1) return STPS_ACTIVE;
|
|
if(steps == 0){
|
|
MSG("Zero move\n");
|
|
return STPS_ZEROMOVE;
|
|
}
|
|
if(the_conf.maxsteps && steps > (int32_t)the_conf.maxsteps){
|
|
MSG("Too much steps\n");
|
|
return STPS_TOOBIG;
|
|
}
|
|
int8_t d;
|
|
if(steps < 0){
|
|
MSG("Negative direction\n");
|
|
d = -1;
|
|
steps = -steps;
|
|
}else d = 1; // positive direction
|
|
// check end-switches
|
|
uint8_t esw = ESW_STATE();
|
|
if(((esw&1) && d == -1) || ((esw&8) && d == 1)){
|
|
MSG("Stay @esw\n");
|
|
return STPS_ONESW; // can't move through esw
|
|
}
|
|
dir = d;
|
|
// change value of DIR pin
|
|
if(the_conf.defflags.reverse){
|
|
if(d>0)
|
|
SET_DIR();
|
|
else
|
|
CLEAR_DIR();
|
|
}else{
|
|
if(d>0)
|
|
CLEAR_DIR();
|
|
else
|
|
SET_DIR();
|
|
}
|
|
// turn on driver, EN=0
|
|
DRV_ENABLE();
|
|
steps_left = (uint32_t)steps;
|
|
// setup timer & start it
|
|
TIMx->ARR = stphigharr;
|
|
TIMx->CCMR1 = TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1; // PWM mode 1: active->inacive, preload enable
|
|
TIMx->CR1 |= TIM_CR1_CEN;
|
|
if(steps < the_conf.accdecsteps*2) state = STP_MVSLOW; // move without acceleration
|
|
else state = STP_ACCEL; // move with acceleration
|
|
MSG("Start moving\n");
|
|
return STPS_ALLOK;
|
|
}
|
|
|
|
// change ARR value
|
|
void stp_chARR(uint32_t val){
|
|
if(val < TIM15ARRMIN) val = TIM15ARRMIN;
|
|
else if(val > 0xffff) val = 0xffff;
|
|
TIMx->ARR = (uint32_t)val;
|
|
}
|
|
|
|
void stp_stop(){ // stop motor by demand or @ end-switch
|
|
switch(state){
|
|
case STP_SLEEP:
|
|
return;
|
|
break;
|
|
case STP_MOVE0:
|
|
case STP_MOVE1:
|
|
state = STP_SLEEP;
|
|
break;
|
|
default:
|
|
state = STP_STOP;
|
|
}
|
|
}
|
|
|
|
void timer_isr(){
|
|
static uint16_t ustep = 0;
|
|
uint16_t tmp, arrval;
|
|
TIMx->SR = 0;
|
|
if(the_conf.microsteps == ++ustep){ // prevent stop @ not full step
|
|
ustep = 0;
|
|
if(state == STP_STOPZERO){
|
|
MSG("Stop @zero\n");
|
|
mot_position = 0;
|
|
}else{
|
|
if(0 == --steps_left){
|
|
MSG("End moving\n");
|
|
state = STP_STOP;
|
|
}
|
|
mot_position += dir;
|
|
}
|
|
//SEND("Left ");printu(steps_left); newline(); sendbuf();
|
|
}else return;
|
|
//SEND("state="); printu(state); newline(); sendbuf();
|
|
switch(state){
|
|
case STP_ACCEL: // acceleration phase
|
|
//SEND("ACC"); newline(); sendbuf();
|
|
arrval = (uint16_t)TIMx->ARR - stpsteparr;
|
|
tmp = stplowarr;
|
|
if(arrval <= tmp || arrval > stphigharr){
|
|
arrval = tmp;
|
|
state = STP_MOVE; // end of acceleration phase
|
|
MSG("Accel end\n");
|
|
}
|
|
TIMx->ARR = arrval;
|
|
break;
|
|
case STP_DECEL: // deceleration phase
|
|
//SEND("DEC"); newline(); sendbuf();
|
|
arrval = (uint16_t)TIMx->ARR + stpsteparr;
|
|
tmp = stphigharr;
|
|
if(arrval >= tmp || arrval < stplowarr){
|
|
arrval = tmp;
|
|
state = STP_MVSLOW; // end of deceleration phase, move @ lowest speed
|
|
MSG("Decel end\n");
|
|
}
|
|
TIMx->ARR = arrval;
|
|
break;
|
|
case STP_MOVE: // moving with constant speed phases
|
|
//SEND("MOVE"); newline(); sendbuf();
|
|
if(steps_left <= the_conf.accdecsteps){
|
|
MSG("Decel start\n");
|
|
state = STP_DECEL; // change moving status to decelerate
|
|
}
|
|
break;
|
|
case STP_MVSLOW:
|
|
//SEND("MVSLOW"); newline(); sendbuf();
|
|
// nothing to do here: all done before switch()
|
|
break;
|
|
default: // STP_STOP, STP_STOPZERO
|
|
//SEND("DEFAULT"); newline(); sendbuf();
|
|
ustep = 0;
|
|
TIMx->CCMR1 = TIM_CCMR1_OC2M_2; // Force inactive
|
|
TIMx->CR1 &= ~TIM_CR1_CEN; // stop timer
|
|
DRV_DISABLE();
|
|
CLEAR_DIR();
|
|
dir = 0;
|
|
steps_left = 0;
|
|
state = STP_SLEEP;
|
|
MSG("Stop motor\n");
|
|
break;
|
|
}
|
|
}
|