Trikarus/firmware_smartstepper_trikarus/stepper_nano_zero/stepper_controller.cpp

1952 lines
44 KiB
C++
Raw Normal View History

2021-08-08 14:43:38 +02:00
/**********************************************************************
* Author: tstern
*
Copyright (C) 2018 MisfitTech, All rights reserved.
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.
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 <https://www.gnu.org/licenses/>.
Written by Trampas Stern for MisfitTech.
Misfit Tech invests time and resources providing this open source code,
please support MisfitTech and open-source hardware by purchasing
products from MisfitTech, www.misifittech.net!
*********************************************************************/
#include "stepper_controller.h"
#include "nonvolatile.h" //for programmable parameters
#include <Wire.h>
#include <inttypes.h>
#include "steppin.h"
#pragma GCC push_options
#pragma GCC optimize ("-Ofast")
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
volatile bool TC5_ISR_Enabled=false;
void setupTCInterrupts() {
// Enable GCLK for TC4 and TC5 (timer counter input clock)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
while (GCLK->STATUS.bit.SYNCBUSY);
TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; // Disable TCx
WAIT_TC16_REGS_SYNC(TC5) // wait for sync
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
WAIT_TC16_REGS_SYNC(TC5)
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TC as normal Normal Frq
WAIT_TC16_REGS_SYNC(TC5)
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1; // Set perscaler
WAIT_TC16_REGS_SYNC(TC5)
TC5->COUNT16.CC[0].reg = F_CPU/NZS_CONTROL_LOOP_HZ;
WAIT_TC16_REGS_SYNC(TC5)
TC5->COUNT16.INTENSET.reg = 0; // disable all interrupts
TC5->COUNT16.INTENSET.bit.OVF = 1; // enable overfollow
// TC5->COUNT16.INTENSET.bit.MC0 = 1; // enable compare match to CC0
NVIC_SetPriority(TC5_IRQn, 2);
// Enable InterruptVector
NVIC_EnableIRQ(TC5_IRQn);
// Enable TC
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TC5)
}
static void enableTCInterrupts() {
TC5_ISR_Enabled=true;
NVIC_EnableIRQ(TC5_IRQn);
TC5->COUNT16.INTENSET.bit.OVF = 1;
// TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //Enable TC5
// WAIT_TC16_REGS_SYNC(TC5) //wait for sync
}
static void disableTCInterrupts() {
TC5_ISR_Enabled=false;
//NVIC_DisableIRQ(TC5_IRQn);
TC5->COUNT16.INTENCLR.bit.OVF = 1;
}
static bool enterCriticalSection()
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
return state;
}
static void exitCriticalSection(bool prevState)
{
if (prevState)
{
enableTCInterrupts();
} //else do nothing
}
void StepperCtrl::updateParamsFromNVM(void)
{
bool state=enterCriticalSection();
pPID.Kd=NVM->pPID.Kd*CTRL_PID_SCALING;
pPID.Ki=NVM->pPID.Ki*CTRL_PID_SCALING;
pPID.Kp=NVM->pPID.Kp*CTRL_PID_SCALING;
vPID.Kd=NVM->vPID.Kd*CTRL_PID_SCALING;
vPID.Ki=NVM->vPID.Ki*CTRL_PID_SCALING;
vPID.Kp=NVM->vPID.Kp*CTRL_PID_SCALING;
sPID.Kd=NVM->sPID.Kd*CTRL_PID_SCALING;
sPID.Ki=NVM->sPID.Ki*CTRL_PID_SCALING;
sPID.Kp=NVM->sPID.Kp*CTRL_PID_SCALING;
if (NVM->SystemParams.parametersVaild)
{
memcpy((void *)&systemParams, (void *)&NVM->SystemParams, sizeof(systemParams));
LOG("Home pin %d",systemParams.homePin);
}else
{
ERROR("This should never happen but just in case");
systemParams.microsteps=16;
#if defined(CTRL_POS_PID_AS_DEFAULT)
systemParams.controllerMode=CTRL_POS_PID;
#else
systemParams.controllerMode=CTRL_SIMPLE;
#endif
systemParams.dirPinRotation=CW_ROTATION; //default to clockwise rotation when dir is high
systemParams.errorLimit=(int32_t)ANGLE_FROM_DEGREES(1.8);
systemParams.errorPinMode=ERROR_PIN_MODE_ENABLE; //default to enable pin
systemParams.errorLogic=false;
systemParams.homeAngleDelay=ANGLE_FROM_DEGREES(10);
systemParams.homePin=-1; //no homing pin configured
}
//default the error pin to input, if it is an error pin the
// handler for this will change the pin to be an output.
// for bidirection error it has to handle input/output it's self as well.
// This is not the cleanest way to handle this...
// TODO implement this cleaner?
pinMode(PIN_ERROR, INPUT_PULLUP); //we have input pin
if (NVM->motorParams.parametersVaild)
{
memcpy((void *)&motorParams, (void *)&NVM->motorParams, sizeof(motorParams));
} else
{
//MotorParams_t Params;
motorParams.fullStepsPerRotation=200;
motorParams.currentHoldMa=500;
motorParams.currentMa=800;
motorParams.homeHoldMa=200;
motorParams.homeMa=800;
motorParams.motorWiring=true;
//memcpy((void *)&Params, (void *)&motorParams, sizeof(motorParams));
//nvmWriteMotorParms(Params);
}
stepperDriver.setRotationDirection(motorParams.motorWiring);
exitCriticalSection(state);
}
void StepperCtrl::motorReset(void)
{
//when we reset the motor we want to also sync the motor
// phase. Therefore we move forward a few full steps then back
// to sync motor phasing, leaving the motor at "phase 0"
bool state=enterCriticalSection();
// stepperDriver.move(0,motorParams.currentMa);
// delay(100);
// stepperDriver.move(A4954_NUM_MICROSTEPS,motorParams.currentMa);
// delay(100);
// stepperDriver.move(A4954_NUM_MICROSTEPS*2,motorParams.currentMa);
// delay(100);
// stepperDriver.move(A4954_NUM_MICROSTEPS*3,motorParams.currentMa);
// delay(100);
// stepperDriver.move(A4954_NUM_MICROSTEPS*2,motorParams.currentMa);
// delay(100);
// stepperDriver.move(A4954_NUM_MICROSTEPS,motorParams.currentMa);
// delay(100);
stepperDriver.move(0,motorParams.currentMa);
delay(1000);
setLocationFromEncoder(); //measure new starting point
exitCriticalSection(state);
}
void StepperCtrl::setLocationFromEncoder(void)
{
numSteps=0;
currentLocation=0;
if (calTable.calValid())
{
int32_t n,x;
int32_t calIndex;
Angle a;
//set our angles based on previous cal data
x=sampleMeanEncoder(200);
a=calTable.fastReverseLookup(x);
//our cal table starts at angle zero, so lets set starting based on this and stepsize
LOG("start angle %d, encoder %d", (uint16_t)a,x);
// we were rounding to nearest full step, but this should not be needed TBS 10/12/2017
// //TODO we need to handle 0.9 degree motor
// if (CALIBRATION_TABLE_SIZE == motorParams.fullStepsPerRotation)
// {
// n=(int32_t)ANGLE_STEPS/CALIBRATION_TABLE_SIZE;
//
// calIndex=((int32_t)((uint16_t)a+n/2)*CALIBRATION_TABLE_SIZE)/ANGLE_STEPS; //find calibration index
// if (calIndex>CALIBRATION_TABLE_SIZE)
// {
// calIndex-=CALIBRATION_TABLE_SIZE;
// }
// a=(uint16_t)((calIndex*ANGLE_STEPS)/CALIBRATION_TABLE_SIZE);
// }
x=(int32_t)((((float)(uint16_t)a)*360.0/(float)ANGLE_STEPS)*1000);
LOG("start angle after rounding %d %d.%03d", (uint16_t)a,x/1000,x%1000);
//we need to set our numSteps
numSteps=DIVIDE_WITH_ROUND( ((int32_t)a *motorParams.fullStepsPerRotation*systemParams.microsteps),ANGLE_STEPS);
currentLocation=(uint16_t)a;
}
zeroAngleOffset=getCurrentLocation(); //zero the angle shown on LCD
}
void StepperCtrl::acceptPositionAndStealthSwitchMode(feedbackCtrl_t m)
{
bool state=enterCriticalSection();
currentLocationIsDesiredLocation();
systemParams.controllerMode=m;
exitCriticalSection(state);
}
void StepperCtrl::stealthSwitchMode(feedbackCtrl_t m)
{
bool state=enterCriticalSection();
systemParams.controllerMode=m;
exitCriticalSection(state);
}
int64_t StepperCtrl::getZeroAngleOffset(void)
{
int64_t x;
bool state=enterCriticalSection();
x=zeroAngleOffset;
exitCriticalSection(state);
return x;
}
void StepperCtrl::setAngle(int64_t angle)
{
bool state=enterCriticalSection();
zeroAngleOffset=getCurrentLocation()-angle;
exitCriticalSection(state);
}
void StepperCtrl::setZero(void)
{
//we want to set the starting angle to zero.
bool state=enterCriticalSection();
zeroAngleOffset=getCurrentLocation();
exitCriticalSection(state);
}
void StepperCtrl::encoderDiagnostics(char *ptrStr)
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
encoder.diagnostics(ptrStr);
if (state) enableTCInterrupts();
}
//TODO This function does two things, set rotation direction
// and measures step size, it should be two functions.
//return is anlge in degreesx100 ie 360.0 is returned as 36000
float StepperCtrl::measureStepSize(void)
{
int32_t angle1,angle2,x,i;
bool feedback=enableFeedback;
int32_t microsteps=systemParams.microsteps;
systemParams.microsteps=1;
enableFeedback=false;
motorParams.motorWiring=true; //assume we are forward wiring to start with
stepperDriver.setRotationDirection(motorParams.motorWiring);
/////////////////////////////////////////
//// Measure the full step size /////
/// Note we assume machine can take one step without issue///
LOG("reset motor");
motorReset(); //this puts stepper motor at stepAngle of zero
LOG("sample encoder");
angle1=sampleMeanEncoder(200);
LOG("move");
stepperDriver.move(A4954_NUM_MICROSTEPS/2,motorParams.currentMa); //move one half step 'forward'
delay(100);
stepperDriver.move(A4954_NUM_MICROSTEPS,motorParams.currentMa); //move one half step 'forward'
delay(500);
LOG("sample encoder");
angle2=sampleMeanEncoder(200);
LOG("Angles %d %d",angle1,angle2);
if ((abs(angle2-angle1))>(ANGLE_STEPS/2))
{
//we crossed the wrap around
if (angle1>angle2)
{
angle1=angle1+(int32_t)ANGLE_STEPS;
}else
{
angle2=angle2+(int32_t)ANGLE_STEPS;
}
}
LOG("Angles %d %d",angle1,angle2);
//when we are increase the steps in the stepperDriver.move() command
// we want the encoder increasing. This ensures motor is moving clock wise
// when encoder is increasing.
// if (angle2>angle1)
// {
// motorParams.motorWiring=true;
// stepperDriver.setRotationDirection(true);
// LOG("Forward rotating");
// }else
// {
// //the motor is wired backwards so correct in stepperDriver
// motorParams.motorWiring=false;
// stepperDriver.setRotationDirection(false);
// LOG("Reverse rotating");
// }
x=((int64_t)(angle2-angle1)*36000)/(int32_t)ANGLE_STEPS;
// if x is ~180 we have a 1.8 degree step motor, if it is ~90 we have 0.9 degree step
LOG("%angle delta %d %d (%d %d)",x,abs(angle2-angle1),angle1,angle2 );
//move back
stepperDriver.move(-A4954_NUM_MICROSTEPS/2,motorParams.currentMa); //move one half step 'forward'
delay(100);
stepperDriver.move(-A4954_NUM_MICROSTEPS,motorParams.currentMa); //move one half step 'forward'
systemParams.microsteps=microsteps;
enableFeedback=feedback;
return ((float)x)/100.0;
}
int32_t StepperCtrl::measureError(void)
{
//LOG("current %d desired %d %d",(int32_t) currentLocation, (int32_t)getDesiredLocation(), numSteps);
return ((int32_t)currentLocation-(int32_t)getDesiredLocation());
}
/*
bool StepperCtrl::changeMicrostep(uint16_t microSteps)
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
systemParams.microsteps=microSteps;
motorReset();
if (state) enableTCInterrupts();
return true;
}
*/
Angle StepperCtrl::maxCalibrationError(void)
{
//Angle startingAngle;
bool done=false;
int32_t mean;
int32_t maxError=0, j;
int16_t dist;
uint16_t angle=0;
bool feedback=enableFeedback;
uint16_t microSteps=systemParams.microsteps;
int32_t steps;
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
if (false == calTable.calValid())
{
return ANGLE_MAX;
}
enableFeedback=false;
j=0;
LOG("Running calibration test");
systemParams.microsteps=1;
motorReset();
steps=0;
while(!done)
{
Angle cal;
Angle act, desiredAngle;
//todo we should measure mean and wait until stable.
delay(200);
mean=sampleMeanEncoder(200);
desiredAngle=(uint16_t)(getDesiredLocation() & 0x0FFFFLL);
cal=calTable.getCal(desiredAngle);
dist=Angle(mean)-cal;
act=calTable.fastReverseLookup(cal);
LOG("actual %d, cal %d",mean,(uint16_t)cal);
LOG("desired %d",(uint16_t)desiredAngle);
LOG("numSteps %d", numSteps);
LOG("cal error for step %d is %d",j,dist);
LOG("mean %d, cal %d",mean, (uint16_t)cal);
updateStep(0,1);
// move one half step at a time, a full step move could cause a move backwards depending on how current ramps down
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
delay(50);
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
if (400==motorParams.fullStepsPerRotation)
{
delay(100);
updateStep(0,1);
// move one half step at a time, a full step move could cause a move backwards depending on how current ramps down
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
delay(100);
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
}
//delay(400);
if (abs(dist)>maxError)
{
maxError=abs(dist);
}
j++;
if (j>=(1*CALIBRATION_TABLE_SIZE+3))
{
done=true;
}
}
systemParams.microsteps=microSteps;
motorReset();
enableFeedback=feedback;
if (state) enableTCInterrupts();
LOG("max error is %d cnts", maxError);
return Angle((uint16_t)maxError);
}
//The encoder needs to be calibrated to the motor.
// we will assume full step detents are correct,
// ex 1.8 degree motor will have 200 steps for 360 degrees.
// We also need to calibrate the phasing of the motor
// to the A4954. This requires that the A4954 "step angle" of
// zero is the first entry in the calibration table.
bool StepperCtrl::calibrateEncoder(void)
{
int32_t x,i,j;
uint32_t angle=0;
int32_t steps;
bool done=false;
int32_t mean;
uint16_t microSteps=systemParams.microsteps;
bool feedback=enableFeedback;
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
enableFeedback=false;
systemParams.microsteps=1;
LOG("reset motor");
motorReset();
LOG("Starting calibration");
delay(200);
steps=0;
j=0;
while(!done)
{
int ii,N;
Angle cal,desiredAngle;
desiredAngle=(uint16_t)(getDesiredLocation() & 0x0FFFFLL);
cal=calTable.getCal(desiredAngle);
delay(200);
mean=sampleMeanEncoder(200);
LOG("Previous cal distance %d, %d, mean %d, cal %d",j, cal-Angle((uint16_t)mean), mean, (uint16_t)cal);
calTable.updateTableValue(j,mean);
updateStep(0,1);
N=2;
// move one half step at a time, a full step move could cause a move backwards depending on how current ramps down
for (ii=0; ii<N; ii++)
{
steps+=A4954_NUM_MICROSTEPS/N;
stepperDriver.move(steps,motorParams.currentMa);
delay(50);
}
//steps+=A4954_NUM_MICROSTEPS/2;
//stepperDriver.move(steps,motorParams.currentMa);
if (400==motorParams.fullStepsPerRotation)
{
delay(100);
updateStep(0,1);
// move one half step at a time, a full step move could cause a move backwards depending on how current ramps down
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
delay(100);
steps+=A4954_NUM_MICROSTEPS/2;
stepperDriver.move(steps,motorParams.currentMa);
}
j++;
if (j>=CALIBRATION_TABLE_SIZE)
{
done=true;
}
}
//calTable.printCalTable();
//calTable.smoothTable();
//calTable.printCalTable();
calTable.saveToFlash(); //saves the calibration to flash
calTable.printCalTable();
systemParams.microsteps=microSteps;
motorReset();
enableFeedback=feedback;
if (state) enableTCInterrupts();
return done;
}
stepCtrlError_t StepperCtrl::begin(void)
{
int i;
float x;
enableFeedback=false;
velocity=0;
currentLocation=0;
numSteps=0;
//we have to update from NVM before moving motor
updateParamsFromNVM(); //update the local cache from the NVM
LOG("start up encoder");
if (false == encoder.begin(PIN_AS5047D_CS))
{
return STEPCTRL_NO_ENCODER;
}
LOG("cal table init");
calTable.init();
startUpEncoder=(uint16_t)getEncoderAngle();
WARNING("start up encoder %d",startUpEncoder);
LOG("start stepper driver");
stepperDriver.begin();
#ifdef NEMA17_SMART_STEPPER_3_21_2017
if (NVM->motorParams.parametersVaild)
{
//lets read the motor voltage
if (GetMotorVoltage()<5)
{
//if we have less than 5 volts the motor is not powered
uint32_t x;
x=(uint32_t)(GetMotorVoltage()*1000.0);
ERROR("Motor voltage is %" PRId32 "mV",x); //default printf does not support floating point numbers
ERROR("Motor may not have power");
return STEPCTRL_NO_POWER;
}
bool state=enterCriticalSection();
setLocationFromEncoder(); //measure new starting point
exitCriticalSection(state);
}else
{
LOG("measuring step size");
x=measureStepSize();
if (abs(x)<0.5)
{
ERROR("Motor may not have power");
return STEPCTRL_NO_POWER;
}
}
#else
LOG("measuring step size");
x=measureStepSize();
if (abs(x)<0.5)
{
ERROR("Motor may not have power");
return STEPCTRL_NO_POWER;
}
#endif
LOG("Checking the motor parameters");
//todo we might want to move this up a level to the NZS
// especially since it has default values
if (false == NVM->motorParams.parametersVaild)
{
MotorParams_t params;
WARNING("NVM motor parameters are not set, we will update");
//power could have just been applied and step size read wrong
// if we are more than 200 steps/rotation which is most common
// lets read again just to be sure.
if (abs(x)<1.5)
{
//run step test a second time to be sure
x=measureStepSize();
}
if (x>0)
{
motorParams.motorWiring=true;
} else
{
motorParams.motorWiring=false;
}
if (abs(x)<=1.2)
{
motorParams.fullStepsPerRotation=400;
}else
{
motorParams.fullStepsPerRotation=200;
}
memcpy((void *)&params, (void *)&motorParams,sizeof(motorParams));
nvmWriteMotorParms(params);
}
LOG("Motor params are now good");
LOG("fullSteps %d", motorParams.fullStepsPerRotation);
LOG("motorWiring %d", motorParams.motorWiring);
LOG("currentMa %d", motorParams.currentMa);
LOG("holdCurrentMa %d", motorParams.currentHoldMa);
LOG("homeMa %d", motorParams.homeMa);
LOG("homeHoldMa %d", motorParams.homeHoldMa);
updateParamsFromNVM(); //update the local cache from the NVM
if (false == calTable.calValid())
{
return STEPCTRL_NO_CAL;
}
enableFeedback=true;
setupTCInterrupts();
enableTCInterrupts();
return STEPCTRL_NO_ERROR;
}
Angle StepperCtrl::sampleAngle(void)
{
uint16_t angle;
int32_t x,y;
#ifdef NZS_AS5047_PIPELINE
//read encoder twice such that we get the latest sample as the pipeline is always once sample behind
y=encoder.readEncoderAnglePipeLineRead(); //convert the 14 bit encoder value to a 16 bit number
x=encoder.readEncoderAnglePipeLineRead();
angle=((uint32_t)(x)*4); //convert the 14 bit encoder value to a 16 bit number
#else
angle=((uint32_t)encoder.readEncoderAngle())<<2; //convert the 14 bit encoder value to a 16 bit number
#endif
return Angle(angle);
}
//when sampling the mean of encoder if we are on roll over
// edge we can have an issue so we have this function
// to do the mean correctly
Angle StepperCtrl::sampleMeanEncoder(int32_t numSamples)
{
int32_t i,last,x=0;
int64_t mean=0;
int32_t min,max;
mean=0;
for (i=0; i<(numSamples); i++)
{
int32_t d;
last=x;
x=(((int32_t)encoder.readEncoderAngle())*4);
if (encoder.getError())
{
#ifndef MECHADUINO_HARDWARE
SerialUSB.println("AS5047 Error");
#else
Serial5.print("AS5047 Error");
#endif
delay(1000);
return 0;
}
if(i==0)
{
last=x;
min=x;
max=x;
}
//LOG("i %d,min %d, max %d, last %d, x %d", i, min, max, last, x);
if (abs(last-x)>65536/2)
{
if (last>x)
{
x=x+65536;
} else
{
x=x-65536;
}
}
if (x>max)
{
max=x;
}
if (x<min)
{
min=x;
}
mean=mean+(x);
}
LOG("min %d, max %d, mean %d", min, max, (int32_t)(mean/numSamples));
return Angle(mean/numSamples);
}
void StepperCtrl::feedback(bool enable)
{
disableTCInterrupts();
motorReset();
enableFeedback=enable;
if (enable == true)
{
enableTCInterrupts();
}
}
void StepperCtrl::updateSteps(int64_t steps)
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
numSteps+=steps;
if (state) enableTCInterrupts();
}
void StepperCtrl::updateStep(int dir, uint16_t steps)
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
if (dir)
{
numSteps-=steps;
}else
{
numSteps+=steps;
}
if (state) enableTCInterrupts();
}
void StepperCtrl::requestStep(int dir, uint16_t steps)
{
bool state;
state=TC5_ISR_Enabled;
disableTCInterrupts();
if (dir)
{
numSteps-=steps;
}else
{
numSteps+=steps;
}
if (false == enableFeedback)
{
moveToAngle(getDesiredLocation(),motorParams.currentMa);
}
if (state) enableTCInterrupts();
}
void StepperCtrl::move(int dir, uint16_t steps)
{
int64_t ret;
int32_t n;
updateStep(dir,steps);
if (false == enableFeedback)
{
n=systemParams.microsteps;
ret=((int64_t)numSteps * A4954_NUM_MICROSTEPS+(n/2))/n;
n=A4954_NUM_MICROSTEPS*motorParams.fullStepsPerRotation;
while(ret>n)
{
ret-=n;
}
while(ret<-n)
{
ret+=n;
}
n=(int32_t)(ret);
LOG("s is %d %d",n,steps);
stepperDriver.move(n,motorParams.currentMa);
}
}
int64_t StepperCtrl::getDesiredLocation(void)
{
int64_t ret;
int32_t n;
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
n=motorParams.fullStepsPerRotation * systemParams.microsteps;
ret=((int64_t)numSteps * (int64_t)ANGLE_STEPS+(n/2))/n ;
if (state) enableTCInterrupts();
return ret;
}
//int32_t StepperCtrl::getSteps(void)
//{
// int32_t ret;
// bool state=enterCriticalSection();
// ret=numSteps;
// exitCriticalSection(state);
// return ret;
//}
void StepperCtrl::moveToAbsAngle(int32_t a)
{
int64_t ret;
int32_t n;
n=motorParams.fullStepsPerRotation * systemParams.microsteps;
ret=(((int64_t)a+zeroAngleOffset)*n+ANGLE_STEPS/2)/(int32_t)ANGLE_STEPS;
bool state=enterCriticalSection();
numSteps=ret;
exitCriticalSection(state);
}
void StepperCtrl::moveToAngle(int32_t a, uint32_t ma)
{
//we need to convert 'Angle' to A4954 steps
a=a % ANGLE_STEPS; //we only interested in the current angle
a=DIVIDE_WITH_ROUND( (a*motorParams.fullStepsPerRotation*A4954_NUM_MICROSTEPS), ANGLE_STEPS);
//LOG("move %d %d",a,ma);
stepperDriver.move(a,ma);
}
Angle StepperCtrl::getEncoderAngle(void)
{
Angle a;
bool state=enterCriticalSection();
a=calTable.fastReverseLookup(sampleAngle());
exitCriticalSection(state);
return a;
}
int64_t StepperCtrl::getCurrentLocation(void)
{
Angle a;
int32_t x;
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
a=calTable.fastReverseLookup(sampleAngle());
x=(int32_t)a - (int32_t)((currentLocation) & ANGLE_MAX);
if (x>((int32_t)ANGLE_STEPS/2))
{
currentLocation -= ANGLE_STEPS;
}
if (x<-((int32_t)ANGLE_STEPS/2))
{
currentLocation += ANGLE_STEPS;
}
currentLocation=(currentLocation & 0xFFFFFFFFFFFF0000UL) | (uint16_t)a;
if (state) enableTCInterrupts();
return currentLocation;
}
int64_t StepperCtrl::getCurrentAngle(void)
{
int64_t x;
x=getCurrentLocation()-zeroAngleOffset;
return x;
}
int64_t StepperCtrl::getDesiredAngle(void)
{
int64_t x;
x=getDesiredLocation()-zeroAngleOffset;
return x;
}
void StepperCtrl::setVelocity(int64_t vel)
{
bool state=enterCriticalSection();
velocity=vel;
exitCriticalSection(state);
}
void StepperCtrl::setTorque(int8_t tor)
{
bool state=enterCriticalSection();
torque=tor;
exitCriticalSection(state);
}
int8_t StepperCtrl::getTorque(void)
{
int8_t tor;
bool state=enterCriticalSection();
tor=torque;
exitCriticalSection(state);
return tor;
}
int64_t StepperCtrl::getVelocity(void)
{
int64_t vel;
bool state=enterCriticalSection();
vel=velocity;
exitCriticalSection(state);
return vel;
}
bool StepperCtrl::torqueLoop(int64_t currentLoc, Control_t *ptrCtrl)
{
int32_t U, ma;
int64_t u, y;
int32_t fullStep=ANGLE_STEPS/motorParams.fullStepsPerRotation;
y=currentLoc;
ma=motorParams.currentMa;
u=torque*ma/128; // Make ma the maximum torque at 128
// If dir pin changes meaning then torque also changes direction
if (CW_ROTATION == systemParams.dirPinRotation)
{
u=-u;
}
U=abs(u);
if (U>ma)
U=ma;
if (u>0)
y=y+fullStep;
else
y=y-fullStep;
ptrCtrl->ma=U;
ptrCtrl->angle=(int32_t)y;
moveToAngle(y,U);
}
void StepperCtrl::PrintData(void)
{
char s[128];
bool state=enterCriticalSection();
sprintf(s, "%u,%u,%u", (uint32_t)numSteps,(uint32_t)getDesiredAngle(),(uint32_t)getCurrentAngle());
#ifndef MECHADUINO_HARDWARE
SerialUSB.println(s);
#endif
exitCriticalSection(state);
}
//this is the velocity PID feedback loop
bool StepperCtrl::vpidFeedback(int64_t desiredLoc, int64_t currentLoc, Control_t *ptrCtrl)
{
int32_t fullStep=ANGLE_STEPS/motorParams.fullStepsPerRotation;
static int64_t lastY=getCurrentLocation();
static int32_t lastError=0;
static int64_t Iterm=0;
int64_t y,z;
int64_t v,dy;
int64_t u;
//get the current location
y =currentLoc;
v=y-lastY;
//add in phase prediction
y=y+calculatePhasePrediction(currentLoc);
z=y;
lastY=y;
v=v*NZS_CONTROL_LOOP_HZ;
if (enableFeedback) //if ((micros()-lastCall)>(updateRate/10))
{
int64_t error,U;
error = velocity-v;
Iterm += (vPID.Ki * error);
if (Iterm>(16*4096*CTRL_PID_SCALING *(int64_t)motorParams.currentMa))
{
Iterm=(16*4096*CTRL_PID_SCALING *(int64_t)motorParams.currentMa);
}
if (Iterm<-(16*4096*CTRL_PID_SCALING *(int64_t)motorParams.currentMa))
{
Iterm=-(16*4096*CTRL_PID_SCALING*(int64_t)motorParams.currentMa);
}
u=((vPID.Kp * error) + Iterm - (vPID.Kd *(lastError-error)));
U=abs(u)/CTRL_PID_SCALING/1024; //scale the error to make PID params close to 1.0;//scale the error to make PID params close to 1.0 by dividing by 1024
if (U>motorParams.currentMa)
{
U=motorParams.currentMa;
}
//when error is positive we need to move reverse direction
if (u>0)
{
z=z+(fullStep);
}else
{
z=z-(fullStep);
}
ptrCtrl->ma=U;
ptrCtrl->angle=(int32_t)z;
moveToAngle(z,U);
loopError=error;
lastError=error;
} else
{
lastError=0;
Iterm=0;
}
if (abs(lastError)>(systemParams.errorLimit))
{
return 1;
}
return 0;
}
//Since we are doing fixed point math our
// threshold needs to be large.
// We need a large threshold when we have fast update
// rate as well. But for most part it is random
bool StepperCtrl::pidFeedback(int64_t desiredLoc, int64_t currentLoc, Control_t *ptrCtrl)
{
static int count=0;
static int32_t maxError=0;
static int32_t lastError=0;
static int32_t Iterm=0;
int32_t ma;
int64_t y;
int32_t fullStep=ANGLE_STEPS/motorParams.fullStepsPerRotation;
int32_t dy;
y=currentLoc;
//add in phase prediction
y=y+calculatePhasePrediction(currentLoc);
if (enableFeedback) //if ((micros()-lastCall)>(updateRate/10))
{
int64_t error,u;
int32_t U,x;
//error is in units of degrees when 360 degrees == 65536
error=(desiredLoc-y); //error is currentPos-desiredPos
Iterm+=(pPID.Ki * error);
if (systemParams.homePin>0 && digitalRead(systemParams.homePin)==0)
{
ma=motorParams.homeMa;
} else
{
ma=motorParams.currentMa;
}
//Over the long term we do not want error
// to be much more than our threshold
if (Iterm> (ma*CTRL_PID_SCALING) )
{
Iterm=(ma*CTRL_PID_SCALING) ;
}
if (Iterm<-(ma*CTRL_PID_SCALING) )
{
Iterm=-(ma*CTRL_PID_SCALING) ;
}
//u=((pPID.Kp * error) + Iterm - (pPID.Kd *(lastError-error)));
if(abs(error) > 90304) // 540 degrees
{
u=(int64_t)((float)ma*(float)CTRL_PID_SCALING*90304.0/(float)error);
}
else
{
u=((pPID.Kp * error) + Iterm - (pPID.Kd *(lastError-error)));
}
U=abs(u)/CTRL_PID_SCALING;
if (U>ma)
{
U=ma;
}
//when error is positive we need to move reverse direction
if (u>0)
{
y=y+fullStep;
}else
{
y=y-fullStep;
}
ptrCtrl->ma=U;
ptrCtrl->angle=(int32_t)y;
moveToAngle(y,U);
loopError=error;
lastError=error;
}else
{
lastError=0;
Iterm=0;
}
if (abs(lastError)>(systemParams.errorLimit))
{
return 1;
}
return 0;
}
// the phase prediction tries to predict error from sensor based
// on current location and previous location.
// TODO our error can help in the phase prediction.
// if the error
int64_t StepperCtrl::calculatePhasePrediction(int64_t currentLoc)
{
static int64_t lastLoc=0;
static int32_t mean=0;
int32_t dx,x;
#ifndef ENABLE_PHASE_PREDICTION
return 0;
#endif
//what was our change in the location
dx=currentLoc-lastLoc; //max value is typically less than 327(1.8 degrees) or 163(0.9 degree)
//if the motor direction changes, zero phase prediction
if (SIGN(dx) != SIGN(mean))
{
//last thing we want is phase prediction during direction change.
mean=0;
} else
{
if (abs(dx)>abs(mean))
{
//increase mean really slowly, 2048 ~ 1/3 second with 6khz processing loop
// in fixed point since the dx is so small we need to scale it up to average
// dx has be be greater than 8 to change the mean...
// this limits the acceleration of motor above max processing speed (6k*1.8)=1800RPM
// however I doubt the motor can accelerate that fast with any load...
// The average helps prevent external impulse error from causing prediction to cause issues.
mean=DIVIDE_WITH_ROUND(2047*mean + dx*128, 2048);
}else
{
//decrease fast
//do not add more phase prediction than the difference in last two samples.
mean=dx*128;
}
}
lastLoc=currentLoc;
x= mean/128; //scale back to normal
return x;
}
bool StepperCtrl::determineError(int64_t currentLoc,int64_t error)
{
static int64_t lastLocation=0;
static int64_t lastError=0;
static int64_t lastVelocity=0;
int64_t velocity;
//since this is called on periodic timer the velocity
// is propotional to the change in location
// one rotation per second is velocity of 10, assumming 6khz update rate
// one rotation per minute is 10/60 velocity units
// since this is less than 1 we will scale the velo
velocity=(currentLoc-lastLocation);
if (velocity>0 && lastVelocity>0)
{
}
lastVelocity=velocity;
lastError=error;
lastLocation=currentLoc;
}
//this was written to do the PID loop not modeling a DC servo
// but rather using features of stepper motor.
bool StepperCtrl::simpleFeedback(int64_t desiredLoc, int64_t currentLoc, Control_t *ptrCtrl)
{
static uint32_t t0=0;
static uint32_t calls=0;
bool ret=false;
static int32_t maxError=0;
static int32_t lastError=0;
static int32_t i=0;
static int32_t iTerm=0;
//static int64_t lastY=getCurrentLocation();
static int32_t velocity=0;
static int32_t errorCount=0;
static bool lastProbeState=false;
static int64_t probeStartAngle=0;
static int32_t maxMa=0;
static int64_t filteredError=0;
static int32_t probeCount=0;
int32_t fullStep=ANGLE_STEPS/motorParams.fullStepsPerRotation;
int32_t ma=0;
int64_t y;
//estimate our current location based on the encoder
y=currentLoc;
//add in phase prediction
y=y+calculatePhasePrediction(currentLoc);
//we can limit the velocity by controlling the amount we move per call to this function
// this only works for velocity greater than 100rpm
/* if (velocity!=0)
{
fullStep=velocity/NZS_CONTROL_LOOP_HZ;
}
if (fullStep==0)
{
fullStep=1; //this RPM of (1*NZS_CONTROL_LOOP_HZ)/60 ie at 6Khz it is 100RPM
}
*/
if (enableFeedback)
{
int64_t error;
int32_t u;
int32_t x;
int32_t kp;
//error is in units of degrees when 360 degrees == 65536
error=(desiredLoc-y);//measureError(); //error is currentPos-desiredPos
//data[i]=(int16_t)error;
//i++;
if (i>=N_DATA)
{
i=0;
}
kp=sPID.Kp;
if (1)//(abs(error)<(fullStep))
{
iTerm+=(sPID.Ki*error);
x=iTerm/CTRL_PID_SCALING;
}else
{
kp=(CTRL_PID_SCALING*9)/10;
x=0;
iTerm=0;
}
if (x>fullStep)
{
x=fullStep;
iTerm=fullStep;
}
if (x<-fullStep)
{
x=-fullStep;
iTerm=-fullStep;
}
//u=(kp * error)/CTRL_PID_SCALING+x+(sPID.Kd *(error-lastError))/CTRL_PID_SCALING;
// Lower effort if we're very far off
if(abs(error) > 90304) // 540 degrees
{
u=(int64_t)((float)fullStep*90304.0/(float)error);
}
else
{
u=(kp * error)/CTRL_PID_SCALING+x+(sPID.Kd *(error-lastError))/CTRL_PID_SCALING;
}
//limit error to full step
if (u>fullStep)
{
u=fullStep;
}
if (u<-fullStep)
{
u=-fullStep;
}
ma=(abs(u)*(motorParams.currentMa-motorParams.currentHoldMa))/ fullStep + motorParams.currentHoldMa;
if (ma>motorParams.currentMa)
{
ma=motorParams.currentMa;
}
//maxMa=motorParams.currentMa;
if (systemParams.homePin>=0)
{
if (digitalRead(systemParams.homePin)==0)
{
if (lastProbeState==false)
{
//record our current angle for homing
probeStartAngle=desiredLoc;
probeCount=0;
maxMa=0;
}
lastProbeState=true;
probeCount++;
//we will lower current after whe have moved some amount
if (probeCount > NZS_CONTROL_LOOP_HZ && probeCount <(2* NZS_CONTROL_LOOP_HZ))
{
maxMa+=ma;
if (abs(error)>maxError)
{
maxError=abs(error);
}
}
if (probeCount>(2*NZS_CONTROL_LOOP_HZ))
{
// ma=(abs(u)*(maxMa))/ fullStep;// + motorParams.homeHoldMa;
// if (ma>motorParams.homeMa)
// {
// ma=motorParams.homeMa;
// }
//if (ma>maxMa/NZS_CONTROL_LOOP_HZ)
{
ma=((maxMa/NZS_CONTROL_LOOP_HZ)*9)/10;
}
}
} else
{
lastProbeState=false;
}
}else
{
maxError=0;
probeCount=0;
//maxMa=0;
}
y=y+u;
ptrCtrl->ma=ma;
ptrCtrl->angle=(int32_t)y;
moveToAngle(y,ma); //35us
lastError=error;
loopError=error;
//stepperDriver.limitCurrent(99);
}
//filteredError=(filteredError*15+lastError)/16;
if (probeCount>(2*NZS_CONTROL_LOOP_HZ))
{
if (abs(lastError) > maxError )
{
errorCount++;
if (errorCount>(10))
{
return 1;
}
return 0;
}
}
else
{
if (abs(lastError) > systemParams.errorLimit)
{
errorCount++;
if (errorCount>(NZS_CONTROL_LOOP_HZ/128)) // error needs to exist for some time period
{
return 1;
}
return 0;
}
}
if (errorCount>0)
{
errorCount--;
}
//errorCount=0;
stepperDriver.limitCurrent(99); //reduce noise on low error
return 0;
}
void StepperCtrl::currentLocationIsDesiredLocation(){
bool state=enterCriticalSection();
moveToAbsAngle(getCurrentAngle());
exitCriticalSection(state);
}
void StepperCtrl::enable(bool enable)
{
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
bool feedback=enableFeedback;
stepperDriver.enable(enable); //enable or disable the stepper driver as needed
if (enabled==true && enable==false)
{
feedback = false;
}
if (enabled==false && enable==true) //if we are enabling previous disabled motor
{
feedback = true;
setLocationFromEncoder();
}
enabled=enable;
enableFeedback=feedback;
if (state) enableTCInterrupts();
}
/*
void StepperCtrl::testRinging(void)
{
uint16_t c;
int32_t steps;
int32_t microSteps=systemParams.microsteps;
bool feedback=enableFeedback;
enableFeedback=false;
systemParams.microsteps=1;
motorReset();
for (c=2000; c>10; c=c-10)
{
SerialUSB.print("Current ");
SerialUSB.println(c);
steps+=A4954_NUM_MICROSTEPS;
stepperDriver.move(steps,NVM->SystemParams.currentMa);
currentLimit=false;
measure();
}
systemParams.microsteps=microSteps;
motorReset();
enableFeedback=feedback;
}
*/
//returns -1 if no data, else returns number of data points remaining.
int32_t StepperCtrl::getLocation(Location_t *ptrLoc)
{
bool state=enterCriticalSection();
int32_t n;
//check for empty
if (locReadIndx==locWriteIndx)
{
//empty data
exitCriticalSection(state);
return -1;
}
//else read data
memcpy(ptrLoc,(void *)&locs[locReadIndx], sizeof(Location_t));
//update the read index
locReadIndx=(locReadIndx+1)%MAX_NUM_LOCATIONS;
//calculate number of locations left
n=((locWriteIndx+MAX_NUM_LOCATIONS)-locReadIndx)%MAX_NUM_LOCATIONS;
exitCriticalSection(state);
return n;
}
void StepperCtrl::updateLocTable(int64_t desiredLoc, int64_t currentLoc, Control_t *ptrCtrl)
{
bool state=enterCriticalSection();
int32_t next;
// set the next write location
next=(locWriteIndx+1)%MAX_NUM_LOCATIONS;
if (next==locReadIndx)
{
//we are full, exit
exitCriticalSection(state);
//RED_LED(true); //turn Red LED on to indciate buffer full
return;
}
//use ticks for the moment so we can tell if we miss data on the print.
locs[locWriteIndx].microSecs=(int32_t)micros();
locs[locWriteIndx].desiredLoc=(int32_t)(desiredLoc-zeroAngleOffset);
locs[locWriteIndx].actualLoc=(int32_t)(currentLoc-zeroAngleOffset);
locs[locWriteIndx].angle=(ptrCtrl->angle-zeroAngleOffset);
locs[locWriteIndx].ma=ptrCtrl->ma;
locWriteIndx=next;
exitCriticalSection(state);
}
bool StepperCtrl::processFeedback(void)
{
bool ret;
int32_t us,j;
Control_t ctrl;
int64_t desiredLoc;
int64_t currentLoc;
int32_t steps;
static int64_t mean=0;;
us=micros();
#ifdef USE_TC_STEP
static int64_t lastSteps;
int64_t x;
x=getSteps()-lastSteps;
updateSteps(x);
lastSteps+=x;
#endif
// steps=getSteps();
// if (steps>0)
// {
// requestStep(1, (uint16_t)steps);
// }else
// {
// requestStep(0, (uint16_t)(-steps));
// }
desiredLoc=getDesiredLocation();
currentLoc=getCurrentLocation();
mean=(31*mean+currentLoc+16)/32;
#ifdef A5995_DRIVER //the A5995 is has more driver noise
if (abs(currentLoc-mean)<ANGLE_FROM_DEGREES(0.9))
#else
if (abs(currentLoc-mean)<ANGLE_FROM_DEGREES(0.3))
#endif
{
currentLoc=mean;
}
switch (systemParams.controllerMode)
{
#if defined(CTRL_POS_PID_AS_DEFAULT)
default:
#endif
case CTRL_POS_PID:
{
ret=pidFeedback(desiredLoc, currentLoc, &ctrl);
break;
}
#if !defined(CTRL_POS_PID_AS_DEFAULT)
default:
#endif
case CTRL_SIMPLE:
{
ret=simpleFeedback(desiredLoc, currentLoc,&ctrl);
break;
}
case CTRL_TORQUE:
{
ret=torqueLoop(currentLoc, &ctrl);
break;
}
case CTRL_POS_VELOCITY_PID:
{
ret=vpidFeedback(desiredLoc, currentLoc,&ctrl);
break;
}
//TODO if disable feedback and someone switches mode
// they will have to turn feedback back on.
case CTRL_OFF:
{
enableFeedback=false;
break;
}
case CTRL_OPEN:
{
enableFeedback=false;
break;
}
}
ticks++;
updateLocTable(desiredLoc, currentLoc,&ctrl);
loopTimeus=micros()-us;
return ret;
}
//auto tuning of PID parameters based on documentation here:
// http://brettbeauregard.com/blog/2012/01/arduino-pid-autotune-library
// http://www.controleng.com/search/search-single-display/relay-method-automates-pid-loop-tuning/4a5774decc.html
void StepperCtrl::PID_Autotune(void)
{
int32_t noiseMin, noiseMax, error;
int32_t eMin, eMax;
int64_t mean;
int32_t startAngle, thres;
int32_t i,j;
int32_t t0,t1;
int32_t times[100];
int32_t angle;
//save previous state;
uint16_t microSteps=systemParams.microsteps;
bool feedback=enableFeedback;
feedbackCtrl_t prevCtrl=systemParams.controllerMode;
//disable interrupts and feedback controller
bool state=TC5_ISR_Enabled;
disableTCInterrupts();
systemParams.controllerMode=CTRL_POS_PID;
enableFeedback=false;
motorReset();
//nvmWritePID(1,0,0,2);
//set the number of microsteps to 1
//systemParams.microsteps=1;
for (i=0; i<10; i++)
{
angle=getCurrentLocation();
}
// pKp=NVM->PIDparams.Kp;
// pKi=NVM->PIDparams.Ki;
// pKd=NVM->PIDparams.Kd;
// threshold=NVM->PIDparams.Threshold;
//enableTCInterrupts();
moveToAngle(angle,motorParams.currentMa);
//moveToAngle(angle,NVM->SystemParams.currentMa);
/*
//next lets measure our noise on the encoder
noiseMin=(int32_t)ANGLE_MAX;
noiseMax=-(int32_t)ANGLE_MAX;
mean=0;
j=1000000UL/NZS_CONTROL_LOOP_HZ;
prevAngle=sampleAngle();
for (i=0; i<(NZS_CONTROL_LOOP_HZ/2); i++)
{
Angle a;
a=sampleAngle();
error=(int32_t)(prevAngle-a);
if (error<noiseMin)
{
noiseMin=error;
}
if (error>noiseMax)
{
noiseMax=error;
}
mean=mean+(int32_t)a;
delayMicroseconds(j);
}
mean=mean/i;
while (mean>ANGLE_MAX)
{
mean=mean-ANGLE_STEPS;
}
while (mean<0)
{
mean=mean+ANGLE_STEPS;
}
//mean is the average of the encoder.
*/
stepperDriver.move(0,motorParams.currentMa);
delay(1000);
//now we need to do the relay control
for (i=0; i<10; i++)
{
startAngle=getCurrentLocation();
LOG("Start %d", (int32_t)startAngle);
}
thres=startAngle + (int32_t)((ANGLE_STEPS/motorParams.fullStepsPerRotation)*10/9);
LOG("Thres %d, start %d",(int32_t)thres,(int32_t)startAngle);
eMin=(int32_t)ANGLE_MAX;
eMax=-(int32_t)ANGLE_MAX;
int32_t reset=0;
int32_t force=(motorParams.currentMa);
for (i=0; i<100; i++)
{
int32_t error;
if (reset)
{
motorReset();
stepperDriver.move(0,motorParams.currentMa);
delay(1000);
startAngle=getCurrentLocation();
LOG("Start %d", (int32_t)startAngle);
force=force-100;
eMin=(int32_t)ANGLE_MAX;
eMax=-(int32_t)ANGLE_MAX;
if (force<100)
{
i=100;
break;
}
LOG("force set to %d",force);
i=0;
}
reset=0;
stepperDriver.move(A4954_NUM_MICROSTEPS,force);
//moveToAngle(startAngle+(ANGLE_STEPS/motorParams.fullStepsPerRotation),force);
//stepperDriver.move(A4954_NUM_MICROSTEPS,NVM->SystemParams.currentMa);
t0=micros();
error=0;
while(error<=((ANGLE_STEPS/motorParams.fullStepsPerRotation)/2+40))
{
int32_t y;
y=getCurrentLocation();
error=y-startAngle;
//LOG("Error1 %d",error);
if (error<eMin)
{
eMin=error;
}
if (error>eMax)
{
eMax=error;
}
if (abs(error)>ANGLE_STEPS/motorParams.fullStepsPerRotation*2)
{
LOG("large Error1 %d, %d, %d",error, y, startAngle);
reset=1;
break;
}
}
stepperDriver.move(0,force);
//stepperDriver.move(0,NVM->SystemParams.currentMa);
t1=micros();
error=(ANGLE_STEPS/motorParams.fullStepsPerRotation);
while(error>=((ANGLE_STEPS/motorParams.fullStepsPerRotation)/2-40))
{
error=getCurrentLocation()-startAngle;
//LOG("Error2 %d",error);
if (error<eMin)
{
eMin=error;
}
if (error>eMax)
{
eMax=error;
}
if (abs(error)>ANGLE_STEPS/motorParams.fullStepsPerRotation*2)
{
LOG("large Error2 %d",error);
reset=1;
break;
}
}
times[i]=t1-t0;
}
for (i=0; i<100; i++)
{
LOG("Time %d %d",i,times[i]);
}
LOG("errorMin=%d",eMin);
LOG("errorMax=%d",eMax);
motorReset();
systemParams.controllerMode=prevCtrl;
systemParams.microsteps=microSteps;
enableFeedback=feedback;
if (state) enableTCInterrupts();
}
//void StepperCtrl::printData(void)
//{
// bool state=TC5_ISR_Enabled;
// disableTCInterrupts();
// int32_t i;
// for(i=0; i<N_DATA; i++)
// {
// LOG ("%d\n",data[i]);
// }
//
// if (state) enableTCInterrupts();
//
//}
#pragma GCC pop_options