PDA

View Full Version : Linear acceleration ramp



pxidr84
- 5th May 2012, 18:37
Hi everyone,

I would like to make a simple, linear acceleration ramp.
I think that low priority DT interrupts can serve as a timebase.

The thing I want is like that: increase linearly the value "x" to value "y" in "n" seconds.

For example, ramp from 10 (x) to 560 (y) in 42 seconds (n).

Time (n) is between 0 and 255 seconds.
Values to increment (x and y) are between 10 and 1200.

Thanks

Archangel
- 6th May 2012, 09:00
I don't know if it will execute quickly enough but you could use a lookup table in a for next loop where the counter VAR is the lookup VAR.
example:
for i = 0 to 10
lookup i,[10,25,50, . . . . ] pulsevar
next i

Charlie
- 6th May 2012, 11:33
Although you said linear, I assume you really can live with a staircase.
If that's the case, and I understand the question correctly, then it's just simple math.

Your example: ramp from 10 (x) to 560 (y) in 42 seconds (n).
Suppose you want 1 step per second. In that case:
stepsize = (y-x)/n
output = x
FOR QQ = 1 to n
PAUSE 1000
output = x + stepsize
next QQ
Replace the pause with a 1 second interrupt if you need to do other things.
"output" feeds yout DAC

pxidr84
- 7th May 2012, 09:41
Although you said linear, I assume you really can live with a staircase.
If that's the case, and I understand the question correctly, then it's just simple math.

Your example: ramp from 10 (x) to 560 (y) in 42 seconds (n).
Suppose you want 1 step per second. In that case:
stepsize = (y-x)/n
output = x
FOR QQ = 1 to n
PAUSE 1000
output = x + stepsize
next QQ
Replace the pause with a 1 second interrupt if you need to do other things.
"output" feeds yout DAC

I will try that, thanks!

SteveB
- 7th May 2012, 18:41
On problem is the rounding error you will get with (y-x)/n

In your example: (560-10)/42 = 13.09524
But, PBP will only see 13. So, at the end of your routine, you will end up with 10 + (42*13) = 556

You will need to put in one line at after Next QQ:

Output = y

SteveB
- 7th May 2012, 20:23
Here is another alternative: Vary the period you increment your speed.
This examply uses PAUSE.

Incr_Time_ms = (1000 * Duration) / (End_Val - Start_Val)
Current_Val = Start_Val
Do Until Current_Val = End_Val
PAUSE Incr_Time_ms
Current_Val = Current_Val + 1
Loop

What would be ideal is set up a timer and add a value so that it overflows after "Incr_Time_ms". You can then use interrupts, or just poll the flag bit. increment your "Current_Val", clear the flag bit, and then add the value to set up the overflow after "Incr_Time_ms". Wash-Rinse-Repeat until "Current_Val = End_Val"

pxidr84
- 8th May 2012, 20:10
Here is another alternative: Vary the period you increment your speed.
This examply uses PAUSE.

Incr_Time_ms = (1000 * Duration) / (End_Val - Start_Val)
Current_Val = Start_Val
Do Until Current_Val = End_Val
PAUSE Incr_Time_ms
Current_Val = Current_Val + 1
Loop

What would be ideal is set up a timer and add a value so that it overflows after "Incr_Time_ms". You can then use interrupts, or just poll the flag bit. increment your "Current_Val", clear the flag bit, and then add the value to set up the overflow after "Incr_Time_ms". Wash-Rinse-Repeat until "Current_Val = End_Val"

Hi Steve,

I've used what you said, and I've also included a deceleration ramp:
(sorry for renaming the values, it's for a easier integration in my main program).



' PIC18F4431 initialization
DEFINE OSC 40
DEFINE LCD_DREG PORTD
DEFINE LCD_EREG PORTB
DEFINE LCD_RSREG PORTB
DEFINE LCD_EBIT 6
DEFINE LCD_RSBIT 7


tacc var BYTE
tdec var BYTE
cacc var word
cdec var word
ref var word
fmin var word
freq var WORD


tacc=1
tdec=10
fmin=10
ref=1200


PAUSE 4000
LCDOUT $fe,$1


cacc = (1000 * tacc) / (ref - fmin)
cdec = (1000 * tdec) / (ref - fmin)


lp:
if freq<ref then PAUSE cacc
if freq>ref then PAUSE cdec

if freq<ref then freq = freq + 1
if freq>ref then freq = freq - 1

LCDOUT $fe,$2,DEC5 freq
if PORTC.0=%1 then ref=500
goto lp


It works quite good, but I cannot exeed 65 seconds (because 66*1000=66000, 16-bit integer is overflowed).
But, more importantly, I want to integrate the loop in a low priority DT interrupt. I really don't understand how you're using flags, etc. (I'm a beginner lol).

Thanks

SteveB
- 9th May 2012, 02:57
Here is a start (not tested!). But it should get you going in the right direction. I only included the acceleration stuff to keep the logic simpler.
You will need the include files (Get Darrel's from http://darreltaylor.com/DT_INTS-18/downloads.html), and make sure the file names match.

' PIC18F4431 initializationDEFINE OSC 40
DEFINE LCD_DREG PORTD
DEFINE LCD_EREG PORTB
DEFINE LCD_RSREG PORTB
DEFINE LCD_EBIT 6
DEFINE LCD_RSBIT 7

tacc var BYTE
tdec var BYTE
cacc var word
cdec var word
ref var word
fmin var word
freq var WORD

cacc_div VAR BYTE
cdec_div VAR BYTE

tacc=1
tdec=10
fmin=10
ref=1200

PAUSE 4000
LCDOUT $fe,$1

INCLUDE "DT_INTS-18.bas"
INCLUDE "ReEnterPBP-18.bas"
INCLUDE "TimerControl.pbp" ; Timer Control Routines

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, _UpdateTickCount, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR1_INT ; Enable Timer 1 Interrupts

GOSUB ResetTime ' Reset Timer and mSec_Ticks = 0

IF tacc > 65 THEN
cacc_div = 10
ELSE
cacc_div = 1
ENDIF

cacc = ((1000/cacc_div) * tacc) / (ref - fmin)
GOSUB StartTimer ' Start Counting mSec_Ticks


Main:


IF mSec_Ticks > (cacc * cacc_div) THEN
IF freq < ref THEN freq = freq + 1
mSec_Ticks = mSec_Ticks - (cacc * cacc_div)
IF freq >= ref THEN
GOSUB StopTimer
GOSUB ResetTime
ENDIF

LCDOUT $fe,$2,DEC5 freq
IF PORTC.0=%1 THEN ref=500


GOTO Main

I may not be much use the next couple of days, but the building blocks are here (hopefully without too many errors/typos)

Charlie
- 9th May 2012, 10:27
Hi Steve,

It works quite good, but I cannot exeed 65 seconds (because 66*1000=66000, 16-bit integer is overflowed).
But, more importantly, I want to integrate the loop in a low priority DT interrupt. I really don't understand how you're using flags, etc. (I'm a beginner lol).

Thanks

Use a long for your variable - you'll have to work hard to overflow 32 bits.

SteveB
- 9th May 2012, 13:36
Thanks for mentioning that Charlie! (I forgot too)

It would certainly simplify the code. I went with the non-LONG method in the case that Pxidr84 might not have PBP3, and so no LONG variables. But yes, I would definitely use them if available. Either way, hopefully the methodology of the example is instructive in learning to scale integers to fit into the given variable space.

Also, there is a correction I need to make to the code. I forgot an ENDIF:


IF mSec_Ticks > (cacc * cacc_div) THEN
IF freq < ref THEN freq = freq + 1
mSec_Ticks = mSec_Ticks - (cacc * cacc_div)
IF freq >= ref THEN
GOSUB StopTimer
GOSUB ResetTime
ENDIF
ENDIF


Sorry about that.

pxidr84
- 13th May 2012, 16:45
Thanks for mentioning that Charlie! (I forgot too)

It would certainly simplify the code. I went with the non-LONG method in the case that Pxidr84 might not have PBP3, and so no LONG variables. But yes, I would definitely use them if available. Either way, hopefully the methodology of the example is instructive in learning to scale integers to fit into the given variable space.

Also, there is a correction I need to make to the code. I forgot an ENDIF:


IF mSec_Ticks > (cacc * cacc_div) THEN
IF freq < ref THEN freq = freq + 1
mSec_Ticks = mSec_Ticks - (cacc * cacc_div)
IF freq >= ref THEN
GOSUB StopTimer
GOSUB ResetTime
ENDIF
ENDIF


Sorry about that.

I think that my PBP 2.6 support LONGs... I can't exceed 65s, but right now it's not important.

I've maybe find an another way (only for make a timer without PAUSE):

-Use a "fixed" frequency low priority interrupt
-Increment a value at each interrupt execution (for exemple, incr=incr+1)
-And execute the ramp code if "incr" as reached a certain calculated value (actually it's "cacc" and "cdec"), and then reset "incr" to zero.

Really, I don't need a nanosec precision for this ramp.

mackrackit
- 13th May 2012, 19:23
PBP 2.6 suppoerts LONGS with 18F devices. You just need to select "Use PBPL" for the compiler.

SteveB
- 14th May 2012, 01:02
-Use a "fixed" frequency low priority interrupt
-Increment a value at each interrupt execution (for exemple, incr=incr+1)
-And execute the ramp code if "incr" as reached a certain calculated value (actually it's "cacc" and "cdec"), and then reset "incr" to zero.


Did you see post # 8?

pxidr84
- 15th May 2012, 06:27
Did you see post # 8?

Yes I've see your post, but I've not tried it yet, I will do that today.