|
|
View previous topic :: View next topic |
Author |
Message |
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
Controlling position of stepper motor |
Posted: Wed Nov 07, 2018 12:09 am |
|
|
Hey guys I'm trying to control the position of stepper motor but couldn't get the hang of it. I have done the part where you have to slowly increase and decrease the frequency for smooth transitions. Now I'm stuck at controlling the position of the motor accurately. Right now whenever i run my code the motor always stops at a different position which shouldn't be the case. Can anyone please take a look at my code and point me in the right direction.
Code: |
#include <18f252.h>
#use delay(clock=20000000)
#fuses NOBROWNOUT, NOPROTECT, HS, NOLVP, NOWDT
int16 tmrval[] = {53035,55535,60535,64285,64702,64950,65085,65178,65230,65277,65310,65340};
int8 steps = 0;
int1 flag = FALSE, tmrInt = FALSE, atSpeed = FALSE, rDown = FALSE, rUp = TRUE;
unsigned int16 pulsesup = 4627, pulsesdown = 5415;
unsigned int32 set_pos = 20000, desired_pos = 0, position = 0;
#INT_TIMER3
void tmr3_isr()
{
tmrInt = TRUE;
}
#INT_TIMER1
void tmr1_isr()
{
if(flag)
{
set_timer1(tmrval[steps]);
}
output_toggle(PIN_C2);
}
#INT_EXT
void ext0_isr()
{
if(atSpeed && !rDown && rUp)
{
position++;
}
}
void main()
{
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
ext_int_edge(0, L_TO_H);
enable_interrupts(INT_EXT);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER3);
enable_interrupts(GLOBAL);
output_low(PIN_C4); // en
desired_pos = set_pos - (pulsesup + pulsesdown);
while(1)
{
output_high(PIN_C5); //direction signal
if(tmrInt)
{
if(atSpeed)
{
if(rUp)
{
output_high(PIN_A2);
output_low(PIN_A1);
output_low(PIN_A0);
}
if(position >= (desired_pos-1250))
{
if(!rDown && rUp)
{
position = 0;
rUp = FALSE;
rDown = TRUE;
atSpeed = FALSE;
}
}
if (desired_pos==0)
{
delay_ms(1000);
desired_pos = set_pos - (pulsesup + pulsesdown);
if(!rUp && rDown)
{
enable_interrupts(INT_TIMER1);
rUp = TRUE;
rDown = FALSE;
atSpeed = FALSE;
}
}
}
else if(rUp && !atSpeed)
{
steps++;
output_high(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A2);
flag = TRUE;
if(steps == 11)
{
steps = 11;
atSpeed = TRUE;
}
}
else if(rDown && !atSpeed)
{
output_high(PIN_A1);
output_low(PIN_A0);
output_low(PIN_A2);
if(steps >= 1)
steps--;
else steps = 0;
flag = TRUE;
if(steps == 0)
{
desired_pos = 0;
steps = 0;
atSpeed = TRUE;
disable_interrupts(INT_TIMER1);
}
}
tmrInt = FALSE;
}
}
} |
Last edited by bliztkrieg on Wed Nov 07, 2018 4:27 am; edited 1 time in total |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Nov 07, 2018 4:02 am |
|
|
I'm having some difficulty trying to decode what you're trying to do.
Yes, it's odd that you get different results each time you run the code.
I'd expect the same wrong result each time.
Please explain:-
What kind of stepper are you driving.
(Is it some sort of module with power drivers?)
How you think your system works.
The pin connections.
Mike
Can you remove unneeded code and add useful comments? |
|
|
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
|
Posted: Wed Nov 07, 2018 4:20 am |
|
|
Mike Walne wrote: | I'm having some difficulty trying to decode what you're trying to do.
Yes, it's odd that you get different results each time you run the code.
I'd expect the same wrong result each time.
Please explain:-
What kind of stepper are you driving.
(Is it some sort of module with power drivers?)
How you think your system works.
The pin connections.
Mike
Can you remove unneeded code and add useful comments? |
code without comments
Code: | #include <18f252.h>
#use delay(clock=20000000)
#fuses NOBROWNOUT, NOPROTECT, HS, NOLVP, NOWDT
int16 tmrval[] = {53035,55535,60535,64285,64702,64950,65085,65178,65230,65277,65310,65340};
int8 steps = 0;
int1 flag = FALSE, tmrInt = FALSE, atSpeed = FALSE, rDown = FALSE, rUp = TRUE;
unsigned int16 pulsesup = 4627, pulsesdown = 5415;
unsigned int32 set_pos = 20000, desired_pos = 0, position = 0;
#INT_TIMER3
void tmr3_isr()
{
tmrInt = TRUE;
}
#INT_TIMER1
void tmr1_isr()
{
if(flag)
{
set_timer1(tmrval[steps]);
}
output_toggle(PIN_C2);
}
#INT_EXT
void ext0_isr()
{
if(atSpeed && !rDown && rUp)
{
position++;
}
}
void main()
{
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
ext_int_edge(0, L_TO_H);
enable_interrupts(INT_EXT);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER3);
enable_interrupts(GLOBAL);
output_low(PIN_C4); // en
desired_pos = set_pos - (pulsesup + pulsesdown);
while(1)
{
output_high(PIN_C5); //direction signal
if(tmrInt)
{
if(atSpeed)
{
if(rUp)
{
output_high(PIN_A2);
output_low(PIN_A1);
output_low(PIN_A0);
}
if(position >= (desired_pos-1250))
{
if(!rDown && rUp)
{
position = 0;
rUp = FALSE;
rDown = TRUE;
atSpeed = FALSE;
}
}
if (desired_pos==0)
{
delay_ms(1000);
desired_pos = set_pos - (pulsesup + pulsesdown);
if(!rUp && rDown)
{
enable_interrupts(INT_TIMER1);
rUp = TRUE;
rDown = FALSE;
atSpeed = FALSE;
}
}
}
else if(rUp && !atSpeed)
{
steps++;
output_high(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A2);
flag = TRUE;
if(steps == 11)
{
steps = 11;
atSpeed = TRUE;
}
}
else if(rDown && !atSpeed)
{
output_high(PIN_A1);
output_low(PIN_A0);
output_low(PIN_A2);
if(steps >= 1)
steps--;
else steps = 0;
flag = TRUE;
if(steps == 0)
{
desired_pos = 0;
steps = 0;
atSpeed = TRUE;
disable_interrupts(INT_TIMER1);
}
}
tmrInt = FALSE;
}
}
} |
|
|
|
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
|
Posted: Wed Nov 07, 2018 4:22 am |
|
|
@Mike
I am counting the number of pulses to keep track how much steps i have taken and since i know how many pulses are there while deceleration im running that piece of code so the motor should stop on the same position every time. but it is not the case. Driver im using is MX-2H304D |
|
|
diode_blade
Joined: 18 Aug 2014 Posts: 55 Location: Sheffield, South Yorkshire
|
|
Posted: Wed Nov 07, 2018 5:11 am |
|
|
I made a sample polishing machine using 3 stepper motors, 1 for the polishing tape, 1 for turning the chuck and 1 for the linear actuator polishing Arm (from Oozenest), I used the threes ccp PWM ouputs to drive the stepper driver boards and fed the signals back into the the CCP compare modules, the way the program worked was to initiate the travel stepper and it's relavent interrupt, then the chuck stepper interrupt move the chuck stepper a few steps then the move the tape stepper with its interrupt enabled, then it re-enabled the travel arm itnterrupt to drive the travel arm stepper back the other way.
I stored various values in the Chips EEPROM so using a four button keypad and menu system i could select various polish sample lengths (all based on the amount of pulses delivered to the stepper from the PIC) which was setup using a setup sample subroutine.
Code: |
//============================== SAMPLE POLISHING MACHINE PROGRAM =====================================//
// CURRENT SOFTWARE VERSION V3
// THE MAIN HARDWARE
// C-Beam Linear Actuator Kit
// - Actuator Length: 250mm
// 3 - OFF NEMA23 Stepper Motors
// - Model: 2303HS280AW - 175oz ..
// DRV8825 stepper driver boards 3-off
// CONTROL BOARD MIKROE READY FOR PIC
// 40 PIN DEVICE BOARD
//=====================================================================================================//
//Processor include
//Fuses
//Timing settings
//pin definitions (pin select if applicable)
//I/O definitions (#use rs232, I2c etc.)
//Then include library files
//Then the code.
//LAST SOFTWARE MOD DATE 16-4-18
#nolist
#include "18F46K22.h"
#list
#device *=8
// DEVICE FUSE SETTINGS
#define device_clock 16M
#use delay(clock=device_clock)
//#device WRITE_EEPROM = NOINT
#fuses HSH,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOLVP,NOBROWNOUT,NOHFOFST
#fuses NOPUT,NOIESO,NOFCMEN,NOEBTR,NOEBTRB,NOCPB,NOPBADEN
#fuses NOWRTC,NOWRT,NOSTVREN,PLLEN,CCP3E0,CCP2C1,TIMER3B5
//--------------------------------------------------------------
// CAN'T USE THE FOLLOWING
// A4,A5,E0,E1,A2,A3 LCD PINS
// D1,D3,C1,C0,C2,B5 PWM O/P and CCP I/P PINS
// D4,D5,D6,D7 KEYPAD
// B0,B2,B3,C3,C4,C6 DRIVER BOARD CONTROL
// POLISH TRAVEL PIN DEFINES
// AVAILABLE PINS
// D0,D2,
// C5,C7
// B1,B4,B6,B7
//Driver board control PINS
//Actuator Travel Motor Pin defines.
#define rset_trav LATB4 //white/red
#define step_trav LATC1 //red/grn
#define direc_trav LATB2 //white/blk
//====================================//
// CHUCK MOTOR PIN DEFINES
#define rset_ch LATB3
#define step_ch LATD3
#define direc_ch LATC3
//==================================//
// TAPE MOTOR DEFINES
#define rset_tape LATC4
#define step_tape LATD1
#define direc_tape LATC6
//SAME COLOURS as Above
//==========================================//
// PINS USED SO FAR
// LCD DISPLAY
// DATA: A4,A5,E0,E1
// CONTROL: RS A2: EN A3
// PWM O/P'S D1 PWM 4: C1 PWM 2: D3 PWM3
// TIMER I/P's
// FOR CCP B5:T3 C0:T1 C2:T5
// ccp3 ccp1 ccp5
// tape travel chuck
//=========================================//
|
I hard wired the PWM pins back into the timer capture pins for each one to count and compare pulses used to drive each stepper.
Code: |
#use pwm(CCP2,timer=2,PWM_OFF,frequency=750Hz, duty=25,stream=travel ) /* working */
#use pwm(output=LATD1,timer=4,PWM_OFF,frequency=750Hz, duty=45,stream=tape)
#use pwm(output=LATD3,timer=6,PWM_OFF,frequency=150Hz, duty=25,stream=chuck) /* working */
#use fast_io(A)//using fast I/o so set each tris bit suit for inputs
#use fast_io(B)//keep all unsed ports pins as output and drive low
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
//I USED FAST I/O AND SET THE TRIS REGISTERS ACCORDINGLY
// DEFINE TIMER ON BITS
#bit t0_on =T0CON.7
#bit t2_on =T2CON.2
#bit t4_on =T4CON.2
#bit t6_on =T6CON.2
void setup_interrupts(void)
{
clear_interrupt(INT_CCP5);
clear_interrupt(INT_CCP3);
clear_interrupt(INT_CCP1);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL); // All interrupts ON
}
//=====================================================================
// SET THE CCP MODULES UP IN COMPARE MODE
// THESE WILL REACT TO THE AMOUNT SET TO THE PWM PULSES COMING IN
// AND GENERATE AN APPROPRIATE INTERRUPT IN TURN
// ALSO SETS UP THE TIMER MODULES THAT ARE USED
// WITH THE CCP MODULES
//=====================================================================
void CCP_Timer_setup(void)
{
setup_timer_0(T0_INTERNAL|T0_DIV_1|T0_OFF);
setup_timer_1(T1_EXTERNAL_SYNC|T1_DIV_BY_1);
setup_timer_3(T3_EXTERNAL_SYNC|T1_DIV_BY_1);
setup_timer_5(T5_EXTERNAL_SYNC|T1_DIV_BY_1);
setup_ccp1(CCP_COMPARE_INT|CCP_USE_TIMER1_AND_TIMER2|CCP_COMPARE_RESET_TIMER); // Interrupt on compare mode Tape count
setup_ccp3(CCP_COMPARE_INT|CCP_USE_TIMER3_AND_TIMER4|CCP_COMPARE_RESET_TIMER); // Interrupt on compare mode polish count
setup_ccp5(CCP_COMPARE_INT|CCP_USE_TIMER5_AND_TIMER6|CCP_COMPARE_RESET_TIMER); // Interrupt on compare mode chuck count
t2_on=false; //TURN ALL PWM TIMERS OFF
t4_on=false;
t6_on=false;
set_timer1(0); // ZERO ALL TIMERS
set_timer3(0);
set_timer5(0);
}
#int_ccp1
void rset_ccp1(void)
{
pwm_off(travel);//SWITCH OF THE TRAVEL
t2_on=false;
output_toggle(direc_trav);
disable_interrupts(INT_CCP1);
clear_interrupt(INT_CCP3);
enable_interrupts(INT_CCP3);
t4_on=true; //turn on tape motor pwm drive
#asm
nop
nop
#endasm
set_timer4(0);
pwm_on(tape);
//my code for setting up the tape travel would be below
//plus activating the ccp3 intterupt
}
//======================================================//
// WE GET HERE FROM THE ABOVE
//
#int_ccp3
void rset_ccp3(void)
{
pwm_off(tape); //switch tape move off
t4_on=false;
disable_interrupts(INT_CCP3);
t6_on=true; //TURN ON TIMER 4 FOR CCP5 COMPARE
#asm // NOW MOVE THE CHUCK
nop
nop
#endasm
clear_interrupt(INT_CCP5);
enable_interrupts(INT_CCP5);
set_timer6(0);
pwm_on(chuck); //turn on chuck pwm drive
}
//number of steps required for travel was selected from menu for the sample polish length
//=====================================================//
// WE GET HERE FROM THE ABOVE
//
#int_ccp5
void rset_ccp5(void)
{
pwm_off(chuck); // SWITCH THE CHUCK MOTOR OFF
disable_interrupts(INT_CCP5);
ccp_5=10;//reset number of steps reqired for next time for amount of //chuck movement
#asm
nop
nop
#endasm
set_timer5(0);
clear_interrupt(INT_CCP5);//clear the int flag
t6_on=false;
#asm
nop
nop
#endasm
t2_on=true; //turn on
#asm
nop
nop
#endasm // TURN TIMER 2 BACK ON FOR CCP1 COMPARE
//CHANGE THE DIRECTION OF TRAVEL
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
set_timer2(0);
pwm_on(travel); //turn on travel stepper drive
}
|
For obvious reasons I have not included all the code I wrote for this but I hope it can help you.
Regards
Dave |
|
|
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
|
Posted: Wed Nov 07, 2018 5:20 am |
|
|
@dave, i have done the same (almost) i mean my pulse signal is going to the external interrupt and i m counting the number for rising edges so that should be the total no. of pulses right ? The thing is i need to set the desired position and the motor should take steps accordingly. For example, distance from Point A to B is 1mm and the no. of steps required are 20000 so the motor should take 20000 steps to stop at exactly 1mm everytime |
|
|
diode_blade
Joined: 18 Aug 2014 Posts: 55 Location: Sheffield, South Yorkshire
|
|
Posted: Wed Nov 07, 2018 5:32 am |
|
|
I created a setup routine selected on a LCD menu system using a 4 button pushbutton keypad, when in that routine it did the following
One pushbuutn drove the actuator arm one way another the opposite
Quote: |
Select setup
|
|
MOVE Arm/Stepper to one end
|
|
Zero PULSE COUNT
|
|
Move Arm/Stepper Required amount
|
|
Store Count value
|
The just load that count value into the ccp for compare with the pulse count when you run in your main program
use your buttons if using a keypad to Move to a starting position
Zero the count
load your stored value
Run
Dave |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Nov 07, 2018 6:04 am |
|
|
bliztkrieg wrote: | @Mike
I am counting the number of pulses to keep track how much steps i have taken and since i know how many pulses are there while deceleration im running that piece of code so the motor should stop on the same position every time. but it is not the case. Driver im using is MX-2H304D |
How do you know your motor is not missing steps?
Mike |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1911
|
|
Posted: Wed Nov 07, 2018 8:19 am |
|
|
Mike Walne wrote: |
How do you know your motor is not missing steps?
|
I suspect the same as Mike. If you clock a stepper too fast, it stops stepping. Have you determined the maximum speed you can run your motor and backed off 10-20% from there? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9247 Location: Greensville,Ontario
|
|
Posted: Wed Nov 07, 2018 9:16 am |
|
|
I cringe when I see motor control with NO feedback ! Just counting pulses to the motor is not good enough, you really need feedback. Typically this is done using a quadrature encoder. They're cheap, reliable and easy to use.
Typically the motor will be driving another gear, a belt or swing an arm. Some kind of mechanical 'slop' between the motor and the 'end'. That has to be known and dealt with. Then there's the dreaded 'stepper hunting' or 'random dead space'. Every stepper will migrate to one of two random places at rest, the fewer the poles the worse the position.A lot of problems can be in the 'driver board'. The lack of power(current) and the stepper won't step. Over heat the driver and , yes, more missed steps. Change the load on the stepper, oops, a few more missed steps...hmm it's not where it's supposed to be.
Feedback...you really need feedback !
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Wed Nov 07, 2018 9:23 am |
|
|
There are several things in the code that may have 'issues'. For instance:
Code: |
if(position >= (desired_pos-1250))
|
What happens if 'desired_pos' is less than 1250?. Remember the variable is an unsigned integer, so will wrap...
Then there is the huge waste of tmrInt. An entire interrupt handler existing, and being called (with all the overhead this implies), just to set a flag bit.
Don't. Don't enable this interrupt, and get rid of the handler. Then instead of testing for tmrInt, test for 'interrupt_active(INT_TIMER3)'. This will be set to true whenever this timer wraps. Instead of setting tmrInt to FALSE, clear_interrupt(INT_TIMER3).
I too suspect missed steps. The actual interrupt speed at full speed is very fast. 65536-65340, is only 196 instruction times. At 20MHz, 25510Hz. If this is for a full pole step (as opposed to microstepping), the motor will be producing very low torque at such a speed.... |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Nov 07, 2018 1:07 pm |
|
|
The quoted driver does micro-stepping, but he's not telling how he's got it set up, what each driver pin is doing (apart from direction), or his motor's steps per revolution.
Driving @ 25kHz a full stepping 200 step/rev motor will be doing over 100 revs/sec, that's a rather speedy 6000rpm.
Lots more info please.
Mike |
|
|
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
|
Posted: Thu Nov 08, 2018 3:45 am |
|
|
Ttelmah wrote: | There are several things in the code that may have 'issues'. For instance:
Code: |
if(position >= (desired_pos-1250))
|
What happens if 'desired_pos' is less than 1250?. Remember the variable is an unsigned integer, so will wrap...
Then there is the huge waste of tmrInt. An entire interrupt handler existing, and being called (with all the overhead this implies), just to set a flag bit.
Don't. Don't enable this interrupt, and get rid of the handler. Then instead of testing for tmrInt, test for 'interrupt_active(INT_TIMER3)'. This will be set to true whenever this timer wraps. Instead of setting tmrInt to FALSE, clear_interrupt(INT_TIMER3).
I too suspect missed steps. The actual interrupt speed at full speed is very fast. 65536-65340, is only 196 instruction times. At 20MHz, 25510Hz. If this is for a full pole step (as opposed to microstepping), the motor will be producing very low torque at such a speed.... |
Desired pos is calculated in the beginning so it will not change and it is int32. OK that is a good approach to get rid of the timer3 interrupt. I'll try but everything seems so complicated. Can u suggest a different approach to control the position with ramp up and down scheme :( |
|
|
bliztkrieg
Joined: 23 Jul 2018 Posts: 20
|
|
Posted: Thu Nov 08, 2018 3:47 am |
|
|
Mike Walne wrote: | The quoted driver does micro-stepping, but he's not telling how he's got it set up, what each driver pin is doing (apart from direction), or his motor's steps per revolution.
Driving @ 25kHz a full stepping 200 step/rev motor will be doing over 100 revs/sec, that's a rather speedy 6000rpm.
Lots more info please.
Mike |
I'm running the motor at 10kHz freq. Driver I'm using is MX-2H304D with dip switches for stepping set to 00100 means 1600 step. There are 3 signals CP(pulses), DIR(direction), EN(enable). |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Thu Nov 08, 2018 4:15 am |
|
|
Quote: |
Im running the motor at 10kHz freq
|
Er.
Your code for timer1, has:
Code: |
#INT_TIMER1
void tmr1_isr()
{
if(flag)
{
set_timer1(tmrval[steps]);
}
output_toggle(PIN_C2);
}
|
At the maximum (steps==11), the timer is loaded with 65340. At the clock you show, this will result in the the timer toggling the output pin at about 11000Hz. Higher than your quoted speed (allowing 30 instructions latency).
However the thing most likely to be causing problems is the jump in the speed ramp. You normally accelerate the motor linearly.
Your ramp gives step rates of:
Quote: |
237.394359510018
249.227395075267
496.919101570264
1951.60031225605
2893.51851851852
4058.44155844156
5197.5051975052
6443.29896907217
7440.47619047619
8650.51903114187
9765.625
11061.9469026549
|
Now this has a three step very slow start, then a sudden jump in rate. Not linear.
As you have the code, with just one step at each speed, it is asking a lot for the motor to actually keep up.... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|