View previous topic :: View next topic |
Author |
Message |
erhane
Joined: 01 Jul 2014 Posts: 41
|
Stepper Motor Start and Stop Pulse Profile[SOLVED] |
Posted: Wed Jul 09, 2014 10:57 am |
|
|
Hello,
I am trying to generate pulses for stepper motor drive application with dspic and pic16f familiy.
I have problems with start and stop pulse profiles. I want to start and stop smoothly. That is why tried to produce pulses like ramp but could not succed.
I tried it like
Code: |
int x=2000;
While(x>100)
{
output_high(pin);
delay_us(x);
output_low(pin);
x-=1;
}
|
this loop not working like linear. It is like exponential and fastening immediately.
What is the reason and can you suggest any solutions.
I thought about producing an array and get delay times from that array but speed of stepper motor changes randomly according to user and every speed has its own start and stop profile.
Thank You.
Last edited by erhane on Thu Jul 10, 2014 1:34 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Wed Jul 09, 2014 11:22 am |
|
|
Normally what you do is have a 'tick' interrupt that just pulses the controller.
This is called perhaps 50000 times per second.
This has a counter, and only generates a pulse when the counter reaches/exceeds the value in a global variable. The counter is reset at this point.
So if you ramp this variable, the pulse rate will change.
You start with a value in this, corresponding to the speed that the controller can be stepped from a cold start, without losing a step. The pull in speed.
You then ramp this linearly down to the value needed to give the speed you want. Leaving it at this value, then gives a constant speed. When you want to slow, you ramp it back linearly up to the pull in value, then stop. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Wed Jul 09, 2014 1:48 pm |
|
|
hmm..
int x=2000;
Integers in CCS are by default 8 bits......
also
without a delay after the output_low(....)..
you will not get a nice square wave.
jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Wed Jul 09, 2014 2:25 pm |
|
|
He shouldn't need a square wave.
Since he is not outputting a bit pattern, he is using a step/direction controller board. All he needs is a tiny pulse (1uSec typically), at a varying frequency.
However the basic approach is wrong. He needs to be running a state machine, and moving through stopped, accelerate, at speed, decelerate, stopped phases. These should be controlled by the outer code loop, and then the timings generated from a tick as outlined.
Have done it many hundreds of times, for different applications, ranging from milling machines, to fans. |
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Wed Jul 09, 2014 11:51 pm |
|
|
Ttelmah, thank you for your reply.
Are you talking about timer interrupt? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 10, 2014 1:26 am |
|
|
Yes, a 'tick', is an interrupt that occurs a a regular interval (just like the tick of a clock).
This is partially trimmed code from a working system, so may well have things typed wrong, or bits missing (the system controls four motors).
What you do, is something like:
Code: |
#define PULL_IN 2000
int16 motor=0;
int16 mramp=PULL_IN; //This will depend on the pull in step rate
//of your controller/motor combination, and on the tick rate used.
int1 at_speed=FALSE;
#define STEP_PIN pin_xx //Pin that steps the controller
#define MIN //minimum number of clock cycles the step pin needs
#INT_TIMERx //ideally at a high rate, like 50K/sec.
void tick(void)
static int16 motor_tick=0;
if (motor_tick)
--motor_tick; //do nothing this time
else
{
if (motor!=0) //there is a speed required
{
output_low(STEP_PIN);
if (motor!=mramp) //changing speed
{
if (mramp>motor) //speeding
{
--mramp; //step faster
motor_tick=mramp;
}
else
{ //slowing
++mramp; //step slower
if (mramp>=MIN)
motor=0; //stop next cycle
motor_tick=mramp;
}
}
else
{
motor_tick=motor; //holding speed
at_speed=TRUE; //signal the main code that speed is reached.
}
delay_cycles(MIN);
output_high(STEP_PIN);
}
}
}
//The main then sets up the timer to tick.
//Writing a tick count to motor, makes it start accelerating _to_ this count
//From the speed defined as 'MRAMP'.
//So with 50000* per sec, with the 2000 given, it'll start stepping at 25Hz.
//If you want 5KHz as the running speed,
//then motors=9, will give this. The speed will ramp to this rate, then
//at_speed will be set true.
//So:
motor=9; //select for the speed you want
while (!at_speed)
{
//do what you want while waiting for the motor
}
//now the motor is being stepped steadily at the requested speed
//do what you want here
//Then to stop:
motor=MIN;
while(motor!=0)
{
//do what you want while it decelerates
}
//Now motor has stopped.
|
Now the controller will have a minimum pulse width needed on the step pin. If this is (say) 1uSec, and your CPU is running at 60MHz, then this is 30 instruction cycles (DsPIC). The test for motor==0 will take perhaps four cycles, and the loading of the register takes another four cycles, so the pulse must be kept low for another 22 cycles to guarantee to meet this time. This is the value 'MIN'.
This requires a delay in the ISR, and this must be short. Delay_cycles avoids
interrupts being disabled in delays in the main code, and is ideal for the short delay required.
The exact value will depend on the code (I haven't checked the instruction count as posted, my code on which this is based, is fractionally different), on the CPU speed, and on the controller being used. |
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Thu Jul 10, 2014 1:35 am |
|
|
Thank you! i will work on it. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 10, 2014 2:04 am |
|
|
I wouldn't have added the 'solved' tag, till you have it working!....
|
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Thu Jul 10, 2014 2:27 am |
|
|
Hahahaha i know it will not that easy :D
But i got the point "what i have to work on". That is why i tag it as solved. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 10, 2014 2:33 am |
|
|
One thing I missed, is that you have to set 'at_speed' to FALSE, after loading the speed you want it to accelerate to, and before testing.... |
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Thu Jul 17, 2014 1:25 am |
|
|
Which interrupt should i use do you think?
I mean i get assumed pulse periods with both timer1 and timer2 interrupts. Is there any differences? I saw some topics which says timer2 interrupt is used for PWM projects.
Thank You |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 17, 2014 1:39 am |
|
|
The key point about timer2, compared to any other normal timer on the PIC16/18, is that it is self resetting. So you can program timer2, to give an interrupt 'at' a specific interval, while the other timers can't do this. On the other timers, you have to 'fiddle' by adding a value to the count register on each interrupt. The self resetting ability, is there, so this timer can be used as the source for PWM, but it also makes it the most useful timer for a constant frequency high speed 'tick'.
Some of the later PIC18's have a second timer with this ability (the chips that have timer4).
Timer2(or 4 if it exists), is the best timer for this job. Even if using PWM, you can program this timer to interrupt at a number of counts of the PWM. |
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Thu Jul 17, 2014 8:02 am |
|
|
I know this is a simple question :D
Is this enough to enable interrupt?
setup_timer_2(T2_DIV_BY_16,255,16);
and use
#INT_TIMER2
I could not interrupt.
This is my fuses and main code. My aim is just produce square wave at output with timer interrupt.
Code: |
#FUSES INTRC_IO // Internal RC clock (OSC1 and OSC2 pins are normal I/O)
#FUSES NOWDT // Watch Dog Timer disabled
#FUSES PUT // Power Up Timer enabled
#FUSES NOMCLR // Master Clear pin is used for I/O
#FUSES PROTECT // Code protected from reads
#FUSES CPD // Data EEPROM code protected
#FUSES BROWNOUT // Brownout Reset enabled
#FUSES BORV25 // Brownout Reset at 2.5V
#FUSES NOCLKOUT // Disable clock output on OSC2
#FUSES NOIESO // Internal External Switch Over Mode disabled
#FUSES NOFCMEN // Fail-safe clock monitor disabled
#FUSES WRT // Program memory write protected
#FUSES NOLVP // Low Voltage Programming disabled
#INT_TIMER2
void interrupt()
{
output_toggle(DRV_STEP);
}
void main()
{
// Set I/O states of the ports
// 76543210
set_tris_a(0b00101000);
set_tris_b(0b11001111);
// Set alternative pin functions
set_pins();
// Set SPI parameters
set_SPI();
// Set variables to default values
set_variables();
// Set driver variables to default values
set_driver();
motion_cycle();
fprintf(RS232,"calisiyor\n\r");
//homing_cycle();
//enable_interrupts(global);
//enable_interrupts(INT_RDA);
setup_timer_2(T2_DIV_BY_16,255,16);
set_timer2(0);
while(true)
{
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 17, 2014 2:50 pm |
|
|
To 'enable' the interrupt, you have to enable the interrupt....
You are _generating_ a 'potential' interrupt source, but not telling the chip to respond to it.
enable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER2);
The chip will then respond to the interrupt.
However assuming you are using a stepper that supports a reasonably high step rate at full speed, you need a much higher interrupt rate.
You don't tell us your CPU clock rate, but assuming (say) 32MHz, then the settings you have would only give about 130Hz. I use 20 to 50kHz for this type of control.... |
|
|
erhane
Joined: 01 Jul 2014 Posts: 41
|
|
Posted: Fri Jul 18, 2014 4:34 am |
|
|
I thing we are doing something wrong. I apply our code but ramp is not linear :/The problem is about Floating point probably :/ i assumed i can remove floating problem with timer interrupt but couldnt :D
You can watch it in here:
http://www.youtube.com/watch?v=ES9yUw-Z0zk&feature=youtu.be
And here is code:
Code: |
#INT_TIMER2
void interrupt()
{
if (motor_tick)
--motor_tick; //do nothing this time
else
{
if (motor!=0) //there is a speed required
{
output_low(STEP_PIN);
if (motor!=mramp) //changing speed
{
if (mramp>motor) //speeding
{
--mramp; //step faster
motor_tick=mramp;
}
else
{ //slowing
++mramp; //step slower
if (mramp>=MIN)
motor=0; //stop next cycle
motor_tick=mramp;
}
}
else
{
motor_tick=motor; //holding speed
at_speed=TRUE; //signal the main code that speed is reached.
}
delay_cycles(MIN);
output_high(STEP_PIN);
integer--;
}
}
}
void main()
{
// Set I/O states of the ports
// 76543210
set_tris_a(0b00101000);
set_tris_b(0b11001111);
// Set alternative pin functions
set_pins();
// Set SPI parameters
set_SPI();
// Set variables to default values
set_variables();
// Set driver variables to default values
set_driver();
motion_cycle();
fprintf(RS232,"calisiyor\n\r");
//homing_cycle();
enable_interrupts(global);
enable_interrupts(INT_RDA);
enable_interrupts(INT_TIMER2);
setup_timer_2(T2_DIV_BY_16,5,4);
set_timer2(0);
///////////////////////////////////////////////////////////////////
motor=9; //select for the speed you want
fprintf(RS232,"rampada\n\r"); //do what you want while waiting for the motor
while (!at_speed)
{
while(integer!=0);
}
//now the motor is being stepped steadily at the requested speed
//do what you want here
fprintf(RS232,"ulasti\n\r"); //do what you want while waiting for the motor
//Then to stop:
motor=MIN;
while(motor!=0)
{
fprintf(RS232,"yavasliyor\n\r"); //do what you want while waiting for the motor//do what you want while it decelerates
}
///////////////////////////////////////////////////////////////////////////////
}
|
|
|
|
|