mirror of
https://github.com/markqvist/OpenModem.git
synced 2024-10-01 03:15:46 -04:00
201 lines
6.3 KiB
C
201 lines
6.3 KiB
C
/*!
|
|
* \file
|
|
* <!--
|
|
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
|
|
* All Rights Reserved.
|
|
* -->
|
|
*
|
|
* \brief Compute, save and load ramps for stepper motors (implementation)
|
|
*
|
|
*
|
|
* \author Simone Zinanni <s.zinanni@develer.com>
|
|
* \author Bernie Innocenti <bernie@codewiz.org>
|
|
* \author Giovanni Bajo <rasky@develer.com>
|
|
* \author Daniele Basile <asterix@develer.com>
|
|
*
|
|
*
|
|
* The formula used by the ramp is the following:
|
|
*
|
|
* <pre>
|
|
* a * b
|
|
* f(t) = -------------
|
|
* lerp(a,b,t)
|
|
* </pre>
|
|
*
|
|
* Where <code>a</code> and <code>b</code> are the maximum and minimum speed
|
|
* respectively (minimum and maximum wavelength respectively), and <code>lerp</code>
|
|
* is a linear interpolation with a factor:
|
|
*
|
|
* <pre>
|
|
* lerp(a,b,t) = a + t * (b - a) = (a * (1 - t)) + (b * t)
|
|
* </pre>
|
|
*
|
|
* <code>t</code> must be in the [0,1] interval. It is easy to see that the
|
|
* following holds true:
|
|
*
|
|
* <pre>
|
|
* f(0) = b, f(1) = a
|
|
* </pre>
|
|
*
|
|
* And that the function is monotonic. So, the function effectively interpolates
|
|
* between the maximum and minimum speed through its domain ([0,1] -> [b,a]).
|
|
*
|
|
* The curve drawn by this function is similar to 1 / (sqrt(n)), so it is slower
|
|
* than a linear acceleration (which would be 1/n).
|
|
*
|
|
* The floating point version uses a slightly modified function which accepts
|
|
* the parameter in the domain [0, MT] (where MT is maxTime, the length of the
|
|
* ramp, which is a setup parameter for the ramp). This is done to reduce the
|
|
* number of operations per step. The formula looks like this:
|
|
*
|
|
* <pre>
|
|
* a * b * MT
|
|
* g(t) = ----------------------------
|
|
* (a * MT) + t * (b - a)
|
|
* </pre>
|
|
*
|
|
* It can be shown that this <code>g(t) = f(t * MT)</code>. The denominator
|
|
* is a linear interpolation in the range [b*MT, a*MT], as t moves in the
|
|
* interval [0, MT]. So the interpolation interval of the function is again
|
|
* [b, a]. The implementation caches the value of the numerator and parts
|
|
* of the denominator, so that the formula becomes:
|
|
*
|
|
* <pre>
|
|
* alpha = a * b * MT
|
|
* beta = a * MT
|
|
* gamma = b - a
|
|
*
|
|
* alpha
|
|
* g(t) = ----------------------
|
|
* beta + t * gamma
|
|
* </pre>
|
|
*
|
|
* and <code>t</code> is exactly the parameter that ramp_evaluate() gets,
|
|
* that is the current time (in range [0, MT]). The operations performed
|
|
* for each step are just an addition, a multiplication and a division.
|
|
*
|
|
* The fixed point version of the formula instead transforms the original
|
|
* function as follows:
|
|
*
|
|
* <pre>
|
|
* a * b a
|
|
* f(t) = ------------------------- = --------------------
|
|
* a a
|
|
* b * ( - * (1 - t) + t ) - * (1 - t) + t
|
|
* b b
|
|
* </pre>
|
|
*
|
|
* <code>t</code> must be computed by dividing the current time (24 bit integer)
|
|
* by the maximum time (24 bit integer). This is done by precomputing the
|
|
* reciprocal of the maximum time as a 0.32 fixed point number, and multiplying
|
|
* it to the current time. Multiplication is performed 8-bits a time by
|
|
* FIX_MULT32(), so that we end up with a 0.16 fixed point number for
|
|
* <code>t</code> (and <code>1-t</code> is just its twos-complement negation).
|
|
* <code>a/b</code> is in the range [0,1] (because a is always less than b,
|
|
* being the minimum wavelength), so it is precomputed as a 0.16 fixed point.
|
|
* The final step is then computing the denominator and executing the division
|
|
* (32 cycles using the 1-step division instruction in the DSP).
|
|
*
|
|
* The assembly implementation is needed for efficiency, but a C version of it
|
|
* can be easily written, in case it is needed in the future.
|
|
*
|
|
*/
|
|
|
|
#include "ramp.h"
|
|
#include <cfg/debug.h>
|
|
|
|
#include <string.h> // memcpy()
|
|
|
|
void ramp_compute(struct Ramp *ramp, uint32_t clocksRamp, uint16_t clocksMinWL, uint16_t clocksMaxWL)
|
|
{
|
|
ASSERT(clocksMaxWL >= clocksMinWL);
|
|
|
|
// Save values in ramp struct
|
|
ramp->clocksRamp = clocksRamp;
|
|
ramp->clocksMinWL = clocksMinWL;
|
|
ramp->clocksMaxWL = clocksMaxWL;
|
|
|
|
#if RAMP_USE_FLOATING_POINT
|
|
ramp->precalc.gamma = ramp->clocksMaxWL - ramp->clocksMinWL;
|
|
ramp->precalc.beta = (float)ramp->clocksMinWL * (float)ramp->clocksRamp;
|
|
ramp->precalc.alpha = ramp->precalc.beta * (float)ramp->clocksMaxWL;
|
|
|
|
#else
|
|
ramp->precalc.max_div_min = ((uint32_t)clocksMinWL << 16) / (uint32_t)clocksMaxWL;
|
|
|
|
/* Calcola 1/total_time in fixed point .32. Assumiamo che la rampa possa al
|
|
* massimo avere 25 bit (cioé valore in tick fino a 2^25, che con il
|
|
* prescaler=3 sono circa 7 secondi). Inoltre, togliamo qualche bit di precisione
|
|
* da destra (secondo quanto specificato in RAMP_CLOCK_SHIFT_PRECISION).
|
|
*/
|
|
ASSERT(ramp->clocksRamp < (1UL << (24 + RAMP_CLOCK_SHIFT_PRECISION)));
|
|
ramp->precalc.inv_total_time = 0xFFFFFFFFUL / (ramp->clocksRamp >> RAMP_CLOCK_SHIFT_PRECISION);
|
|
ASSERT(ramp->precalc.inv_total_time < 0x1000000UL);
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
void ramp_setup(struct Ramp* ramp, uint32_t length, uint32_t minFreq, uint32_t maxFreq)
|
|
{
|
|
uint32_t minWL, maxWL;
|
|
|
|
minWL = TIME2CLOCKS(FREQ2MICROS(maxFreq));
|
|
maxWL = TIME2CLOCKS(FREQ2MICROS(minFreq));
|
|
|
|
ASSERT2(minWL < 65536UL, "Maximum frequency too high");
|
|
ASSERT2(maxWL < 65536UL, "Minimum frequency too high");
|
|
ASSERT(maxFreq > minFreq);
|
|
|
|
ramp_compute(
|
|
ramp,
|
|
TIME2CLOCKS(length),
|
|
TIME2CLOCKS(FREQ2MICROS(maxFreq)),
|
|
TIME2CLOCKS(FREQ2MICROS(minFreq))
|
|
);
|
|
}
|
|
|
|
void ramp_default(struct Ramp *ramp)
|
|
{
|
|
ramp_setup(ramp, RAMP_DEF_TIME, RAMP_DEF_MINFREQ, RAMP_DEF_MAXFREQ);
|
|
}
|
|
|
|
#if RAMP_USE_FLOATING_POINT
|
|
|
|
float ramp_evaluate(const struct Ramp* ramp, float curClock)
|
|
{
|
|
return ramp->precalc.alpha / (curClock * ramp->precalc.gamma + ramp->precalc.beta);
|
|
}
|
|
|
|
#else
|
|
|
|
INLINE uint32_t fix_mult32(uint32_t m1, uint32_t m2)
|
|
{
|
|
uint32_t accum = 0;
|
|
accum += m1 * ((m2 >> 0) & 0xFF);
|
|
accum >>= 8;
|
|
accum += m1 * ((m2 >> 8) & 0xFF);
|
|
accum >>= 8;
|
|
accum += m1 * ((m2 >> 16) & 0xFF);
|
|
return accum;
|
|
}
|
|
|
|
// a*b >> 16
|
|
INLINE uint16_t fix_mult16(uint16_t a, uint32_t b)
|
|
{
|
|
return (b*(uint32_t)a) >> 16;
|
|
}
|
|
|
|
uint16_t FAST_FUNC ramp_evaluate(const struct Ramp* ramp, uint32_t curClock)
|
|
{
|
|
uint16_t t = FIX_MULT32(curClock >> RAMP_CLOCK_SHIFT_PRECISION, ramp->precalc.inv_total_time);
|
|
uint16_t denom = fix_mult16((uint16_t)~t + 1, ramp->precalc.max_div_min) + t;
|
|
uint16_t cur_delta = ((uint32_t)ramp->clocksMinWL << 16) / denom;
|
|
|
|
return cur_delta;
|
|
}
|
|
|
|
#endif
|
|
|
|
|