-
-
Save speters/6524227 to your computer and use it in GitHub Desktop.
See "Linear motion control without the math" by Pramod Ranade in http://www.nxtbook.com/nxtbooks/cmp/esd0409/index.php?startid=23 (Embedded System Design Vol 22, #4, April 2009)
and
http://picprog.strongedge.net/step_prof/step-profile.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
step.c | |
Program to verify new algorithm for linear acceleration. | |
Author: Pramod Ranade <[email protected]> | |
*/ | |
#include <stdio.h> | |
#if defined(_WIN32) || defined(_WIN64) | |
#include <io.h> | |
#endif | |
#include <stdlib.h> | |
#define SCAN_TIME_IN_MICRO_SEC 2 | |
#define COUNT_FOR_COMPARISON (1000000000UL / SCAN_TIME_IN_MICRO_SEC) | |
#define SPEED_LIMIT 9500 | |
#define ACC_LIMIT 900000 | |
#define START_SPEED 1500 | |
#define DES_POS 117 | |
#define AVAILABLE_TIME_IN_MILI_SEC 23 | |
int OutputPinStatus = 1 ; | |
int g_nRisingEdgeCount = 0 ; | |
FILE *fptr ; | |
#define MakeOutputHigh() OutputPinStatus=1 | |
#define MakeOutputLow() {PrintDebugInfo(uiDoneSteps, uiCurTimeInMicroSec, uiCurSpeedX1K);OutputPinStatus=0;} | |
void PrintDebugInfo (unsigned int uiDoneSteps, unsigned int uiCurTimeInMicroSec, unsigned int uiCurSpeedX1K) | |
{ | |
fprintf(fptr,"%u\t%u\t%u\n", uiDoneSteps, uiCurTimeInMicroSec, uiCurSpeedX1K) ; | |
} | |
int FpgaRunMotor (unsigned int uiDesPos, unsigned int uiStartSpeedX1K, unsigned int uiDeltaSpeedX1K, unsigned int uiTimeForAccInMicroSec, unsigned int uiTimeToStartDeccInMicroSec, unsigned int uiPeakSpeed) | |
{ | |
/* | |
This function performs the actions which must be done by FPGA. | |
So, finally this should be converted to VHDL or Verilog code for the FPGA. | |
*/ | |
unsigned int uiDoneSteps ; | |
unsigned int uiCurSpeedX1K ; | |
unsigned int uiSpeedTimeProduct ; | |
unsigned int uiCurTimeInMicroSec ; | |
MakeOutputHigh() ; | |
fprintf(fptr,"Inside FpgaRunMotor:\n") ; | |
fprintf(fptr,"uiDesPos = %u\n", uiDesPos) ; | |
fprintf(fptr,"uiStartSpeedX1K = %u\n", uiStartSpeedX1K) ; | |
fprintf(fptr,"uiDeltaSpeedX1K = %u\n", uiDeltaSpeedX1K) ; | |
fprintf(fptr,"uiTimeForAccInMicroSec = %u\n", uiTimeForAccInMicroSec) ; | |
fprintf(fptr,"uiTimeToStartDeccInMicroSec = %u\n", uiTimeToStartDeccInMicroSec) ; | |
uiDoneSteps = 0 ; | |
uiCurSpeedX1K = uiStartSpeedX1K ; | |
uiDoneSteps ++ ; | |
uiSpeedTimeProduct = uiCurSpeedX1K ; | |
uiCurTimeInMicroSec = 0 ; | |
MakeOutputLow() ; // first pulse output | |
while(uiDoneSteps < uiDesPos) | |
{ | |
// execute this loop after every N micro-seconds, where N = SCAN_TIME_IN_MICRO_SEC | |
uiCurTimeInMicroSec += SCAN_TIME_IN_MICRO_SEC ; | |
uiSpeedTimeProduct += uiCurSpeedX1K ; | |
if (uiCurTimeInMicroSec >= uiTimeToStartDeccInMicroSec) | |
{ | |
// it means deceleration is going on | |
if (uiCurSpeedX1K > uiDeltaSpeedX1K) | |
{ | |
uiCurSpeedX1K -= uiDeltaSpeedX1K ; | |
} | |
else | |
{ | |
uiSpeedTimeProduct = COUNT_FOR_COMPARISON ; | |
printf("\t\terror!\n") ; | |
} | |
} | |
else if (uiCurTimeInMicroSec < uiTimeForAccInMicroSec) | |
{ | |
// it means acceleration is going on | |
uiCurSpeedX1K += uiDeltaSpeedX1K ; | |
} | |
else | |
{ | |
// it means steady speed (plateau) | |
uiCurSpeedX1K = uiPeakSpeed ; | |
} | |
if (uiSpeedTimeProduct >= COUNT_FOR_COMPARISON) | |
{ | |
uiDoneSteps ++ ; | |
MakeOutputLow() ; // next pulse output | |
printf("falling edge at step %d!\n", uiDoneSteps) ; | |
uiSpeedTimeProduct -= COUNT_FOR_COMPARISON ; | |
} | |
else if (uiSpeedTimeProduct >= (COUNT_FOR_COMPARISON / 2)) | |
{ | |
if (!OutputPinStatus) | |
{ | |
printf("g_nRisingEdgeCount = %d\n", g_nRisingEdgeCount) ; | |
} | |
MakeOutputHigh() ; | |
g_nRisingEdgeCount ++ ; | |
} | |
} | |
MakeOutputHigh() ; | |
return 0 ; | |
} | |
int RunMotor (unsigned int uiStartSpeed, unsigned int uiAvailableTimeInMicroSec, int nDesPos, unsigned int uiSpeedLimit, unsigned int uiAccLimit) | |
{ | |
/* | |
This function performs the calculations which must be done by microprocessor. | |
Finally, it calls another function FpgaRunMotor() | |
i.e. it passes some parameters to FPGA and tells it to start motion. | |
*/ | |
unsigned int uiAverageSpeed ; | |
unsigned int uiTimeForAccInMicroSec ; | |
unsigned int uiTimeToStartDeccInMicroSec, uiRequiredAcc ; | |
unsigned int uiPeakSpeed, uiDeltaSpeedX1K ; | |
uiRequiredAcc = 0 ; | |
if (uiAvailableTimeInMicroSec) | |
{ | |
uiAverageSpeed = abs(nDesPos) * 1000000UL / uiAvailableTimeInMicroSec ; // steps/sec | |
if (uiAverageSpeed <= uiStartSpeed) | |
{ | |
// no acceleration/deccelration required | |
uiStartSpeed = uiAverageSpeed ; | |
} | |
else | |
{ | |
uiRequiredAcc = 1 ; | |
} | |
} | |
else | |
{ | |
// no acceleration/deccelration required | |
} | |
if (uiRequiredAcc) | |
{ | |
if (uiAverageSpeed <= ((uiSpeedLimit + uiStartSpeed) / 2)) | |
{ | |
// tri-angular profile | |
uiTimeForAccInMicroSec = uiAvailableTimeInMicroSec / 2 ; | |
uiPeakSpeed = ((uiAverageSpeed - uiStartSpeed) * 2) + uiStartSpeed ; | |
uiRequiredAcc = (((uiPeakSpeed - uiStartSpeed) * 40000UL) / (uiAvailableTimeInMicroSec)) * 50 ; | |
// please do not try to reduce above stmnt | |
// becuase it results in overflow of intermediate result | |
} | |
else | |
{ | |
// trapezoidal profile | |
/* | |
number of steps covered during acc and deacc = (Ta * (uiSpeedLimit + uiStartSpeed)) | |
number of steps covered during steady speed = nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed)) | |
Ts = steady time can be computed from: | |
uiSpeedLimit = (nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed))) / Ts ; | |
i.e. | |
Ts = (nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed))) / uiSpeedLimit ; | |
(Ts + 2*Ta) * 1000000 = uiAvailableTimeInMicroSec ; | |
Here, Ts and Ta are in seconds. | |
(((nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed))) / uiSpeedLimit) + (2*Ta)) * 1000000 = uiAvailableTimeInMicroSec ; | |
((nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed))) / uiSpeedLimit) + (2*Ta) = uiAvailableTimeInMicroSec / 1000000 ; | |
multiply boths sides by uiSpeedLimit | |
(nDesPos - (Ta * (uiSpeedLimit + uiStartSpeed))) + (2*Ta*uiSpeedLimit) = uiAvailableTimeInMicroSec*uiSpeedLimit / 1000000 ; | |
nDesPos - Ta*uiSpeedLimit - Ta*uiStartSpeed + 2*Ta*uiSpeedLimit = uiAvailableTimeInMicroSec*uiSpeedLimit / 1000000 ; | |
nDesPos - Ta*(uiStartSpeed - uiSpeedLimit) = uiAvailableTimeInMicroSec*uiSpeedLimit / 1000000 ; | |
nDesPos - (uiAvailableTimeInMicroSec*uiSpeedLimit / 1000000) = Ta*(uiStartSpeed - uiSpeedLimit) ; | |
Ta = (nDesPos - (uiAvailableTimeInMicroSec*uiSpeedLimit / 1000000)) / (uiStartSpeed - uiSpeedLimit) ; | |
TaInUSec = (nDesPos*1000000 - uiAvailableTimeInMicroSec*uiSpeedLimit) / (uiStartSpeed - uiSpeedLimit) ; | |
TaInUSec = (uiAvailableTimeInMicroSec*uiSpeedLimit - nDesPos*1000000) / (uiSpeedLimit - uiStartSpeed) ; | |
*/ | |
uiPeakSpeed = uiSpeedLimit ; | |
uiTimeForAccInMicroSec = (uiAvailableTimeInMicroSec*uiSpeedLimit - nDesPos*1000000) / (uiSpeedLimit - uiStartSpeed) ; | |
uiRequiredAcc = (((uiSpeedLimit - uiStartSpeed) * 40000U) / uiTimeForAccInMicroSec) * 25 ; | |
// please do not try to reduce above stmnt | |
// becuase it results in overflow of intermediate result | |
if (uiRequiredAcc > uiAccLimit) | |
{ | |
// handle this error condition | |
// TBD | |
} | |
} | |
uiTimeToStartDeccInMicroSec = uiAvailableTimeInMicroSec - uiTimeForAccInMicroSec ; | |
uiDeltaSpeedX1K = SCAN_TIME_IN_MICRO_SEC * uiRequiredAcc / 1000 ; | |
if (!uiDeltaSpeedX1K) | |
{ | |
uiDeltaSpeedX1K = 1 ; | |
} | |
// do something to set/clear direction pin here | |
FpgaRunMotor(abs(nDesPos), uiStartSpeed * 1000, uiDeltaSpeedX1K, uiTimeForAccInMicroSec, uiTimeToStartDeccInMicroSec, uiPeakSpeed * 1000) ; | |
} | |
else | |
{ | |
// run steady at uiStartSpeed for N steps and then stop | |
// do something to set/clear direction pin here | |
FpgaRunMotor(abs(nDesPos), uiStartSpeed * 1000, 0, 0, uiAvailableTimeInMicroSec, uiStartSpeed * 1000) ; | |
} | |
fprintf(fptr,"Required acceleration = %u Steps/sec/sec\n", uiRequiredAcc) ; | |
return uiRequiredAcc ; | |
} | |
int main (int argc, char* argv[]) | |
{ | |
unsigned int uiStartSpeed = START_SPEED ; | |
unsigned int uiAvailableTimeInMiliSec = AVAILABLE_TIME_IN_MILI_SEC ; | |
int nDesPos = DES_POS ; | |
unsigned int uiSpeedLimit = SPEED_LIMIT ; | |
unsigned int uiAccLimit = ACC_LIMIT ; | |
int k ; | |
if ((argc < 2) || (argc > 7)) | |
{ | |
printf("Syntax: step outputfilename [AvailableTime] [DesPos] [StrtSpeed] [SpeedLimit] [AccLimit]\n") ; | |
return 1 ; | |
} | |
fptr = fopen(argv[1], "wt") ; | |
if (fptr == NULL) | |
{ | |
printf("Could not open file %s\n", argv[1]) ; | |
return 2 ; | |
} | |
for(k = 2 ; k < argc ; k ++) | |
{ | |
switch(k) | |
{ | |
case 2 : | |
uiAvailableTimeInMiliSec = atoi(argv[k]) ; | |
break ; | |
case 3 : | |
nDesPos = atoi(argv[k]) ; | |
break ; | |
case 4 : | |
uiStartSpeed = atoi(argv[k]) ; | |
break ; | |
case 5 : | |
uiSpeedLimit = atoi(argv[k]) ; | |
break ; | |
case 6 : | |
uiAccLimit = atoi(argv[k]) ; | |
break ; | |
} | |
} | |
fprintf(fptr,"\n\t\tProfile Generation Algorithm Test Program\n") ; | |
fprintf(fptr,"\nSpeed Limit in Steps/Sec is:%u",uiSpeedLimit) ; | |
fprintf(fptr,"\nAcceleration Limit in Steps/Sec/Sec is:%u",uiAccLimit) ; | |
fprintf(fptr,"\nStart Speed in Steps/Sec is:%u",uiStartSpeed ) ; | |
fprintf(fptr,"\nDesired Position (Number of Steps) is:%u",nDesPos ) ; | |
fprintf(fptr,"\nAvailable time in mili seconds is:%u\n\n",uiAvailableTimeInMiliSec ) ; | |
RunMotor(uiStartSpeed,(uiAvailableTimeInMiliSec * 1000),nDesPos,uiSpeedLimit,uiAccLimit) ; | |
fclose(fptr) ; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment