2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @file stepper.cpp
|
|
|
|
*
|
|
|
|
* http://rusefi.com/wiki/index.php?title=Hardware:Stepper_motor
|
|
|
|
*
|
|
|
|
* @date Dec 24, 2014
|
2020-01-13 18:57:43 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
|
|
|
|
2021-07-25 22:05:17 -07:00
|
|
|
#include "pch.h"
|
2019-01-03 20:51:29 -08:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
#include "stepper.h"
|
|
|
|
|
|
|
|
float StepperMotorBase::getTargetPosition() const {
|
|
|
|
return m_targetPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StepperMotorBase::setTargetPosition(float targetPositionSteps) {
|
|
|
|
// When the IAC position value change is insignificant (lower than this threshold), leave the poor valve alone
|
|
|
|
// When we get a larger change, actually update the target stepper position
|
|
|
|
if (absF(m_targetPosition - targetPositionSteps) >= 1) {
|
|
|
|
m_targetPosition = targetPositionSteps;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StepperMotorBase::initialize(StepperHw *hardware, int totalSteps) {
|
|
|
|
m_totalSteps = maxI(3, totalSteps);
|
|
|
|
|
|
|
|
m_hw = hardware;
|
|
|
|
}
|
|
|
|
|
2019-01-03 20:51:29 -08:00
|
|
|
// todo: EFI_STEPPER macro
|
|
|
|
#if EFI_PROD_CODE || EFI_SIMULATOR
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
void StepperMotorBase::saveStepperPos(int pos) {
|
2017-08-07 04:28:21 -07:00
|
|
|
// use backup-power RTC registers to store the data
|
2019-04-12 17:52:51 -07:00
|
|
|
#if EFI_PROD_CODE
|
2017-12-22 05:49:03 -08:00
|
|
|
backupRamSave(BACKUP_STEPPER_POS, pos + 1);
|
2018-11-02 10:18:47 -07:00
|
|
|
#endif
|
2020-09-07 11:41:04 -07:00
|
|
|
postCurrentPosition();
|
2017-08-07 04:28:21 -07:00
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
int StepperMotorBase::loadStepperPos() {
|
2019-04-12 17:52:51 -07:00
|
|
|
#if EFI_PROD_CODE
|
2017-12-22 05:49:03 -08:00
|
|
|
return (int)backupRamLoad(BACKUP_STEPPER_POS) - 1;
|
2018-11-02 10:18:47 -07:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2017-08-07 04:28:21 -07:00
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
void StepperMotorBase::changeCurrentPosition(bool positive) {
|
2020-09-07 11:41:04 -07:00
|
|
|
if (positive) {
|
|
|
|
m_currentPosition++;
|
|
|
|
} else {
|
|
|
|
m_currentPosition--;
|
|
|
|
}
|
|
|
|
postCurrentPosition();
|
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
void StepperMotorBase::postCurrentPosition(void) {
|
2022-07-07 19:58:25 -07:00
|
|
|
if (engineConfiguration->debugMode == DBG_STEPPER_IDLE_CONTROL) {
|
2020-09-07 11:41:04 -07:00
|
|
|
#if EFI_TUNER_STUDIO
|
2021-12-07 17:18:47 -08:00
|
|
|
engine->outputChannels.debugIntField5 = m_currentPosition;
|
2020-09-07 11:41:04 -07:00
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
2020-01-14 23:18:15 -08:00
|
|
|
}
|
2020-09-07 11:41:04 -07:00
|
|
|
}
|
2020-01-14 23:18:15 -08:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
void StepperMotorBase::setInitialPosition(void) {
|
2017-08-07 04:28:21 -07:00
|
|
|
// try to get saved stepper position (-1 for no data)
|
2020-01-08 22:03:23 -08:00
|
|
|
m_currentPosition = loadStepperPos();
|
2017-08-07 04:28:21 -07:00
|
|
|
|
2019-04-12 17:52:51 -07:00
|
|
|
#if HAL_USE_ADC
|
2017-08-07 04:28:21 -07:00
|
|
|
// first wait until at least 1 slowADC sampling is complete
|
|
|
|
waitForSlowAdc();
|
2018-11-01 12:57:50 -07:00
|
|
|
#endif
|
2020-09-07 11:41:04 -07:00
|
|
|
|
2019-04-12 17:52:51 -07:00
|
|
|
#if EFI_SHAFT_POSITION_INPUT
|
2020-09-05 15:49:42 -07:00
|
|
|
bool isRunning = engine->rpmCalculator.isRunning();
|
2019-03-30 14:41:46 -07:00
|
|
|
#else
|
|
|
|
bool isRunning = false;
|
|
|
|
#endif /* EFI_SHAFT_POSITION_INPUT */
|
2017-08-07 04:28:21 -07:00
|
|
|
// now check if stepper motor re-initialization is requested - if the throttle pedal is pressed at startup
|
2021-10-05 16:59:07 -07:00
|
|
|
auto tpsPos = Sensor::getOrZero(SensorType::DriverThrottleIntent);
|
2020-04-01 17:21:03 -07:00
|
|
|
bool forceStepperParking = !isRunning && tpsPos > STEPPER_PARKING_TPS;
|
2021-11-17 00:54:21 -08:00
|
|
|
if (engineConfiguration->stepperForceParkingEveryRestart)
|
2018-01-29 15:27:39 -08:00
|
|
|
forceStepperParking = true;
|
2021-04-20 11:09:41 -07:00
|
|
|
efiPrintf("Stepper: savedStepperPos=%d forceStepperParking=%d (tps=%.2f)", m_currentPosition, (forceStepperParking ? 1 : 0), tpsPos);
|
2017-08-07 04:28:21 -07:00
|
|
|
|
2020-01-08 22:03:23 -08:00
|
|
|
if (m_currentPosition < 0 || forceStepperParking) {
|
2021-04-20 11:09:41 -07:00
|
|
|
efiPrintf("Stepper: starting parking...");
|
2017-08-07 04:28:21 -07:00
|
|
|
// reset saved value
|
|
|
|
saveStepperPos(-1);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* let's park the motor in a known position to begin with
|
|
|
|
*
|
|
|
|
* I believe it's safer to retract the valve for parking - at least on a bench I've seen valves
|
|
|
|
* disassembling themselves while pushing too far out.
|
2018-01-28 11:44:01 -08:00
|
|
|
*
|
|
|
|
* Add extra steps to compensate step skipping by some old motors.
|
2017-08-07 04:28:21 -07:00
|
|
|
*/
|
2021-11-17 00:54:21 -08:00
|
|
|
int numParkingSteps = (int)efiRound((1.0f + (float)engineConfiguration->stepperParkingExtraSteps / PERCENT_MULT) * m_totalSteps, 1.0f);
|
2018-01-28 11:44:01 -08:00
|
|
|
for (int i = 0; i < numParkingSteps; i++) {
|
2020-09-07 11:41:04 -07:00
|
|
|
if (!m_hw->step(false)) {
|
|
|
|
initialPositionSet = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
changeCurrentPosition(false);
|
2017-08-07 04:28:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// set & save zero stepper position after the parking completion
|
2020-01-08 22:03:23 -08:00
|
|
|
m_currentPosition = 0;
|
|
|
|
saveStepperPos(m_currentPosition);
|
2021-04-20 11:09:41 -07:00
|
|
|
efiPrintf("Stepper: parking finished!");
|
2018-01-28 11:44:01 -08:00
|
|
|
} else {
|
|
|
|
// The initial target position should correspond to the saved stepper position.
|
|
|
|
// Idle thread starts later and sets a new target position.
|
2020-01-08 22:03:23 -08:00
|
|
|
setTargetPosition(m_currentPosition);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2020-09-07 11:41:04 -07:00
|
|
|
initialPositionSet = true;
|
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
void StepperMotorBase::doIteration() {
|
|
|
|
int targetPosition = efiRound(getTargetPosition(), 1);
|
|
|
|
int currentPosition = m_currentPosition;
|
|
|
|
|
|
|
|
// the stepper does not work if the main relay is turned off (it requires +12V)
|
|
|
|
if (!engine->isMainRelayEnabled()) {
|
|
|
|
m_hw->pause();
|
2020-09-07 11:41:04 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
if (!initialPositionSet) {
|
|
|
|
setInitialPosition();
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 11:41:04 -07:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
if (targetPosition == currentPosition) {
|
2021-10-06 10:57:04 -07:00
|
|
|
m_hw->sleep();
|
2021-08-02 04:03:57 -07:00
|
|
|
m_isBusy = false;
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 11:41:04 -07:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
m_isBusy = true;
|
2020-09-07 11:41:04 -07:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
bool isIncrementing = targetPosition > currentPosition;
|
2020-01-14 23:18:15 -08:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
if (m_hw->step(isIncrementing)) {
|
|
|
|
changeCurrentPosition(isIncrementing);
|
|
|
|
}
|
2020-01-08 22:03:23 -08:00
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
// save position to backup RTC register
|
2019-04-12 17:52:51 -07:00
|
|
|
#if EFI_PROD_CODE
|
2021-08-02 04:03:57 -07:00
|
|
|
saveStepperPos(m_currentPosition);
|
2018-11-02 10:18:47 -07:00
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2021-08-02 04:03:57 -07:00
|
|
|
bool StepperMotorBase::isBusy() const {
|
|
|
|
return m_isBusy;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2020-01-14 23:18:15 -08:00
|
|
|
void StepDirectionStepper::setDirection(bool isIncrementing) {
|
2020-01-08 22:03:23 -08:00
|
|
|
if (isIncrementing != m_currentDirection) {
|
2018-01-28 11:44:01 -08:00
|
|
|
// compensate stepper motor inertia
|
2020-01-14 23:18:15 -08:00
|
|
|
pause();
|
2020-01-08 22:03:23 -08:00
|
|
|
m_currentDirection = isIncrementing;
|
2018-01-28 11:44:01 -08:00
|
|
|
}
|
2020-01-14 23:18:15 -08:00
|
|
|
|
2018-01-28 11:44:01 -08:00
|
|
|
directionPin.setValue(isIncrementing);
|
|
|
|
}
|
|
|
|
|
2020-09-07 11:41:04 -07:00
|
|
|
bool StepDirectionStepper::pulse() {
|
|
|
|
// we move the motor only of it is powered from the main relay
|
|
|
|
if (!engine->isMainRelayEnabled())
|
|
|
|
return false;
|
|
|
|
|
2019-11-24 21:02:53 -08:00
|
|
|
enablePin.setValue(false); // enable stepper
|
|
|
|
|
|
|
|
stepPin.setValue(true);
|
2020-01-14 23:18:15 -08:00
|
|
|
pause();
|
2019-07-13 06:38:37 -07:00
|
|
|
|
2019-11-24 21:02:53 -08:00
|
|
|
stepPin.setValue(false);
|
2020-01-14 23:18:15 -08:00
|
|
|
pause();
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-11-24 21:02:53 -08:00
|
|
|
enablePin.setValue(true); // disable stepper
|
2020-09-07 11:41:04 -07:00
|
|
|
|
|
|
|
return true;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2021-10-30 17:31:09 -07:00
|
|
|
void StepperHw::sleep() {
|
2021-10-06 10:57:04 -07:00
|
|
|
pause();
|
|
|
|
}
|
|
|
|
|
|
|
|
void StepperHw::pause(int divisor) const {
|
|
|
|
// currently we can't sleep less than 1ms (see #3214)
|
|
|
|
chThdSleepMicroseconds(maxI(MS2US(1), (int)(MS2US(m_reactionTime)) / divisor));
|
2020-01-14 23:18:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void StepperHw::setReactionTime(float ms) {
|
|
|
|
m_reactionTime = maxF(1, ms);
|
|
|
|
}
|
|
|
|
|
2020-09-07 11:41:04 -07:00
|
|
|
bool StepDirectionStepper::step(bool positive) {
|
2020-01-14 23:18:15 -08:00
|
|
|
setDirection(positive);
|
2020-09-07 11:41:04 -07:00
|
|
|
return pulse();
|
2020-01-14 23:18:15 -08:00
|
|
|
}
|
|
|
|
|
2021-04-20 11:09:41 -07:00
|
|
|
void StepperMotor::initialize(StepperHw *hardware, int totalSteps) {
|
2021-08-02 04:03:57 -07:00
|
|
|
StepperMotorBase::initialize(hardware, totalSteps);
|
2020-01-14 23:18:15 -08:00
|
|
|
|
2022-07-21 12:17:32 -07:00
|
|
|
start();
|
2020-01-14 23:18:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void StepDirectionStepper::initialize(brain_pin_e stepPin, brain_pin_e directionPin, pin_output_mode_e directionPinMode, float reactionTime, brain_pin_e enablePin, pin_output_mode_e enablePinMode) {
|
2021-01-08 17:01:26 -08:00
|
|
|
if (!isBrainPinValid(stepPin) || !isBrainPinValid(directionPin)) {
|
2015-07-10 06:01:56 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-14 23:18:15 -08:00
|
|
|
setReactionTime(reactionTime);
|
|
|
|
|
2017-06-13 06:28:05 -07:00
|
|
|
this->directionPinMode = directionPinMode;
|
2020-03-29 16:07:07 -07:00
|
|
|
this->directionPin.initPin("Stepper DIR", directionPin, &this->directionPinMode);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-11-24 21:02:53 -08:00
|
|
|
this->stepPinMode = OM_DEFAULT; // todo: do we need configurable stepPinMode?
|
2020-03-29 16:07:07 -07:00
|
|
|
this->stepPin.initPin("Stepper step", stepPin, &this->stepPinMode);
|
2019-04-12 14:28:19 -07:00
|
|
|
|
2019-11-24 21:02:53 -08:00
|
|
|
this->enablePinMode = enablePinMode;
|
2020-03-29 16:07:07 -07:00
|
|
|
this->enablePin.initPin("Stepper EN", enablePin, &this->enablePinMode);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2018-01-22 14:48:12 -08:00
|
|
|
// All pins must be 0 for correct hardware startup (e.g. stepper auto-disabling circuit etc.).
|
2019-11-24 21:02:53 -08:00
|
|
|
this->enablePin.setValue(true); // disable stepper
|
|
|
|
this->stepPin.setValue(false);
|
2018-01-22 14:48:12 -08:00
|
|
|
this->directionPin.setValue(false);
|
2020-01-08 22:03:23 -08:00
|
|
|
m_currentDirection = false;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-01-03 20:51:29 -08:00
|
|
|
#endif
|
2021-10-30 17:31:09 -07:00
|
|
|
|
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
void StepperHw::sleep() { }
|
|
|
|
#endif // EFI_UNIT_TEST
|