/* CP0097 Rotating Plate Control Copyright Cyrob 2019 Arduino pro Micro Description Control a Stepper motor in two modes: - SpeedMode : The speed varie with the knob position from -max to max with a midle zero position Led is switched off if the motor is stopped Led is green if it moves Led is red is the maximum speed is reached - TrackMode : The position of the motor follows the knob one The increment of the position change with the speed of the knob to allow precision Led is blue Pushing the knob at any time stops the motor ans toggle mode. The motor windings are desactivated when motor is Off ===================================================================================== ========================== OPEN SOURCE LICENCE ================================== ===================================================================================== Copyright 2019 Philippe Demerliac Cyrob.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. ................................................................................................................ Release history ................................................................................................................ Version Date Author Comment 1.0d1 02/02/19 Phildem First blood 1.0 06/02/19 Phildem Version OK 1.1 06/02/19 Phildem Version OK, relative speed improvement 1.2 07/02/19 Phildem Version OK, Code refactoring and presentation */ //IO Abstraction ............................................................................................... #define kOutCpuLed 13 // Active High #define kOutRLed 12 // Active Low #define kOutGLed 11 // Active Low #define kOutBLed 10 // Active Low #define kOutDir 8 #define kOutStep 9 // Step on Low to High transition #define kOutEnable 5 // Active Low #define kInKnobCk 2 #define kInKnobDir 3 #define kInKnobPush 4 // Settings .................................................................................................... #define kMicroIdle 20 // Micro Ticker step duration in µs #define kStepPulseWidth_us 4 // Step pulse width in µS #define kMotorWakeUp_ms 10 // Delay in ms to let motor wake up when Enable set to low #define kMinSpeedDelay_uT 10000 // Slower speed in delay between Step pulse in Micro idle steps #define kMaxSpeedDelay_uT 100 // Higher speed in delay between Step pulse in Micro idle steps #define kMaxSpeed 100 // Max Value of gSpeed #define kSpeedInc 5 // Knob increment for Speed #define kPosInc 2 // Knob pos Increment in TrackMode // Global ..................................................................................................... static unsigned long gLastMicro =0; // Micro Ticker last Time ref static unsigned long gLastMilli =0; // Milli Ticker last Time ref static long gSpeed =0; // 0 Off else -kMaxSpeed to +kMaxSpeed Sign is dir static unsigned long gPulseDelay =0; // Delay counter in Micro Ticker between pulse static bool gMotEnable = false; // True if motor enable static bool gLastKnobCk; // Last knob Ck Input static unsigned long gLastKnobMilli =0; // Last milli of knob action static bool gModeTrack = false; // True if mode Track static unsigned long gTargetPos =0; // Track mode Target pos static unsigned long gActualPos =0; // Track mode Actual pos //------------------------------------------------------------------------------------------------------------ void setup(void) { //IO Init pinMode(kInKnobCk, INPUT_PULLUP); pinMode(kInKnobDir, INPUT_PULLUP); pinMode(kInKnobPush, INPUT_PULLUP); gLastKnobCk=digitalRead(kInKnobCk); pinMode(kOutCpuLed, OUTPUT); pinMode(kOutRLed, OUTPUT); pinMode(kOutGLed, OUTPUT); pinMode(kOutBLed, OUTPUT); pinMode(kOutDir, OUTPUT); pinMode(kOutStep, OUTPUT); pinMode(kOutEnable, OUTPUT); digitalWrite(kOutDir, LOW); digitalWrite(kOutStep, LOW); DisableMotor(); // Test Led Sequence digitalWrite(kOutCpuLed, HIGH); SetLedRed(); delay(500); SetLedGreen(); delay(500); SetLedBlue(); delay(500); for (int i=0;i<5;i++){ SetLedRed(); delay(20); SetLedOff(); delay(150); } digitalWrite(kOutCpuLed, LOW); SetModeSpeed(); } //============================================================================================================ // UI Led Control //____________________________________________________________________________________________________________ //------------------------------------------------------------------------------------------------------------ // Switch State Led to Red //------------------------------------------------------------------------------------------------------------ void SetLedRed(){ digitalWrite(kOutRLed, LOW); digitalWrite(kOutGLed, HIGH); digitalWrite(kOutBLed, HIGH); } //------------------------------------------------------------------------------------------------------------ // Switch State Led to Green //------------------------------------------------------------------------------------------------------------ void SetLedGreen(){ digitalWrite(kOutRLed, HIGH); digitalWrite(kOutGLed, LOW); digitalWrite(kOutBLed, HIGH); } //------------------------------------------------------------------------------------------------------------ // Switch State Led to Blue //------------------------------------------------------------------------------------------------------------ void SetLedBlue(){ digitalWrite(kOutRLed, HIGH); digitalWrite(kOutGLed, HIGH); digitalWrite(kOutBLed, LOW); } //------------------------------------------------------------------------------------------------------------ // Switch State Led to Off //------------------------------------------------------------------------------------------------------------ void SetLedOff(){ digitalWrite(kOutRLed, HIGH); digitalWrite(kOutGLed, HIGH); digitalWrite(kOutBLed, HIGH); } //------------------------------------------------------------------------------------------------------------ // Handle Led display must be call every ms //------------------------------------------------------------------------------------------------------------ void LedIdle() { if (gModeTrack) SetLedBlue(); else if (gSpeed==0) SetLedOff(); else if (abs(gSpeed)>=kMaxSpeed) SetLedRed(); else SetLedGreen(); } //============================================================================================================ // Knob Handling //____________________________________________________________________________________________________________ //------------------------------------------------------------------------------------------------------------ // Look if knob moved, return 0 if not, else -Step or +Step vs direction //------------------------------------------------------------------------------------------------------------ long ReadKnob() { bool Ck=digitalRead(kInKnobCk); if (Ck==gLastKnobCk) return 0; delayMicroseconds(400); gLastKnobCk=Ck; if (Ck==LOW) return 0; unsigned long Step; unsigned long Now=millis(); if (gModeTrack){ // Non linear in Track mode gLastKnobMilli=Now-gLastKnobMilli; Step=kPosInc; if (gLastKnobMilli<350) Step*=8; else if (gLastKnobMilli<500) Step*=4; else if (gLastKnobMilli<600) Step*=2; } else Step=kSpeedInc; gLastKnobMilli=Now; if (digitalRead(kInKnobDir)==LOW) // Set Sign vs direction return -Step; return Step; } //------------------------------------------------------------------------------------------------------------ // Handle Knob rotation, must be call every Micro Ticker //------------------------------------------------------------------------------------------------------------ void KnobRotIdle() { long k=ReadKnob(); if (k==0) return; if (gModeTrack) { SetRelativeTrackPosition(k); } else SetSpeed(gSpeed+k); } //------------------------------------------------------------------------------------------------------------ // Handle Knob Push must be call every ms //------------------------------------------------------------------------------------------------------------ void KnobPushIdle(){ if (digitalRead(kInKnobPush)==HIGH) return; // Wait for button released while (digitalRead(kInKnobPush)==LOW) delay(1); // Toggle mode if (gModeTrack){ // Return to speed mode SetModeSpeed(); } else { // Return to Track mode SetModeTrack(); } } //============================================================================================================ // Motor Control //____________________________________________________________________________________________________________ //------------------------------------------------------------------------------------------------------------ // Increment motor of one step //------------------------------------------------------------------------------------------------------------ void StepOne() { digitalWrite(kOutStep, HIGH); delayMicroseconds(kStepPulseWidth_us); digitalWrite(kOutStep, LOW); } //------------------------------------------------------------------------------------------------------------ // Enable motor power //------------------------------------------------------------------------------------------------------------ void EnableMotor(){ digitalWrite(kOutCpuLed, HIGH); digitalWrite(kOutEnable, LOW); if (!gMotEnable) // To let driver wake up delay(kMotorWakeUp_ms); gMotEnable=true; } //------------------------------------------------------------------------------------------------------------ // Disable motor power //------------------------------------------------------------------------------------------------------------ void DisableMotor(){ digitalWrite(kOutCpuLed, LOW); digitalWrite(kOutEnable, HIGH); gMotEnable=false; } //------------------------------------------------------------------------------------------------------------ // Set Global Speed, sign is direction, Speed will be clamp to max //------------------------------------------------------------------------------------------------------------ void SetSpeed(long Speed) { gSpeed = constrain(Speed, -kMaxSpeed, kMaxSpeed); } //------------------------------------------------------------------------------------------------------------ // Handle Motor, make it turn vs gSpeed. Must be called every Micro Ticker //------------------------------------------------------------------------------------------------------------ void MotorIdle() { if (gSpeed==0){ DisableMotor(); return; } if (gPulseDelay>0){ gPulseDelay--; return; } EnableMotor(); if (gSpeed>0){ digitalWrite(kOutDir, LOW); gActualPos++; } else { digitalWrite(kOutDir, HIGH); gActualPos--; } StepOne(); gPulseDelay=kMinSpeedDelay_uT/abs(gSpeed); } //============================================================================================================ // Mode Control //____________________________________________________________________________________________________________ //------------------------------------------------------------------------------------------------------------ // Indicate to switch to mode Speed //------------------------------------------------------------------------------------------------------------ void SetModeSpeed(){ gModeTrack=false; SetSpeed(0); } //------------------------------------------------------------------------------------------------------------ // Indicate to switch to mode Track //------------------------------------------------------------------------------------------------------------ void SetModeTrack(){ gModeTrack=true; gActualPos=0; gTargetPos=0; SetSpeed(0); } //------------------------------------------------------------------------------------------------------------ // Set the target position in Track Mode (Pos or neg vs Dir) //------------------------------------------------------------------------------------------------------------ void SetRelativeTrackPosition(long Move){ gTargetPos+=Move; } //------------------------------------------------------------------------------------------------------------ // Handle Mode must be call every ms //------------------------------------------------------------------------------------------------------------ void ModeIdle(){ if (!gModeTrack) return; if (gActualPos==gTargetPos){ gActualPos=0; gTargetPos=0; SetSpeed(0); } else SetSpeed(gTargetPos-gActualPos); } //============================================================================================================ // Cooperativ multi task supervisor //____________________________________________________________________________________________________________ //------------------------------------------------------------------------------------------------------------ // Main Loop //------------------------------------------------------------------------------------------------------------ void loop(void) { // µs Ticker ................................... unsigned long CurMicro=micros(); if (CurMicrokMicroIdle){ gLastMicro=CurMicro; // Fast Engine ------------- MotorIdle(); // Fast UI UI ------------- KnobRotIdle(); } // ms Ticker ................................ unsigned long CurMilli=millis(); if (CurMilli !=gLastMilli){ gLastMilli=CurMilli; // Slow Engine ------------- ModeIdle(); // Slow UI ------------- KnobPushIdle(); LedIdle(); } // of milli }