Move some define in sam3x sd implementaion. Define sd speeds macro to clarify the...
[bertos.git] / bertos / algo / ramp.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
5  * All Rights Reserved.
6  * -->
7  *
8  * \brief Compute, save and load ramps for stepper motors (implementation)
9  *
10  *
11  * \author Simone Zinanni <s.zinanni@develer.com>
12  * \author Bernie Innocenti <bernie@codewiz.org>
13  * \author Giovanni Bajo <rasky@develer.com>
14  * \author Daniele Basile <asterix@develer.com>
15  *
16  *
17  * The formula used by the ramp is the following:
18  *
19  * <pre>
20  *            a * b
21  * f(t) = -------------
22  *         lerp(a,b,t)
23  * </pre>
24  *
25  * Where <code>a</code> and <code>b</code> are the maximum and minimum speed
26  * respectively (minimum and maximum wavelength respectively), and <code>lerp</code>
27  * is a linear interpolation with a factor:
28  *
29  * <pre>
30  * lerp(a,b,t) =  a + t * (b - a)  =  (a * (1 - t)) + (b * t)
31  * </pre>
32  *
33  * <code>t</code> must be in the [0,1] interval. It is easy to see that the
34  * following holds true:
35  *
36  * <pre>
37  * f(0) = b,   f(1) = a
38  * </pre>
39  *
40  * And that the function is monotonic. So, the function effectively interpolates
41  * between the maximum and minimum speed through its domain ([0,1] -> [b,a]).
42  *
43  * The curve drawn by this function is similar to 1 / (sqrt(n)), so it is slower
44  * than a linear acceleration (which would be 1/n).
45  *
46  * The floating point version uses a slightly modified function which accepts
47  * the parameter in the domain [0, MT] (where MT is maxTime, the length of the
48  * ramp, which is a setup parameter for the ramp). This is done to reduce the
49  * number of operations per step. The formula looks like this:
50  *
51  * <pre>
52  *               a * b * MT
53  * g(t) = ----------------------------
54  *           (a * MT) + t * (b - a)
55  * </pre>
56  *
57  * It can be shown that this <code>g(t) = f(t * MT)</code>. The denominator
58  * is a linear interpolation in the range [b*MT, a*MT], as t moves in the
59  * interval [0, MT]. So the interpolation interval of the function is again
60  * [b, a]. The implementation caches the value of the numerator and parts
61  * of the denominator, so that the formula becomes:
62  *
63  * <pre>
64  * alpha = a * b * MT
65  * beta = a * MT
66  * gamma = b - a
67  *
68  *                alpha
69  * g(t) = ----------------------
70  *           beta + t * gamma
71  * </pre>
72  *
73  * and <code>t</code> is exactly the parameter that ramp_evaluate() gets,
74  * that is the current time (in range [0, MT]). The operations performed
75  * for each step are just an addition, a multiplication and a division.
76  *
77  * The fixed point version of the formula instead transforms the original
78  * function as follows:
79  *
80  * <pre>
81  *                   a * b                         a
82  *  f(t) =  -------------------------  =  --------------------
83  *                 a                         a
84  *           b * ( - * (1 - t) + t )         - * (1 - t) + t
85  *                 b                         b
86  * </pre>
87  *
88  * <code>t</code> must be computed by dividing the current time (24 bit integer)
89  * by the maximum time (24 bit integer). This is done by precomputing the
90  * reciprocal of the maximum time as a 0.32 fixed point number, and multiplying
91  * it to the current time. Multiplication is performed 8-bits a time by
92  * FIX_MULT32(), so that we end up with a 0.16 fixed point number for
93  * <code>t</code> (and <code>1-t</code> is just its twos-complement negation).
94  * <code>a/b</code> is in the range [0,1] (because a is always less than b,
95  * being the minimum wavelength), so it is precomputed as a 0.16 fixed point.
96  * The final step is then computing the denominator and executing the division
97  * (32 cycles using the 1-step division instruction in the DSP).
98  *
99  * The assembly implementation is needed for efficiency, but a C version of it
100  * can be easily written, in case it is needed in the future.
101  *
102  */
103
104 #include "ramp.h"
105 #include <cfg/debug.h>
106
107 #include <string.h> // memcpy()
108
109 void ramp_compute(struct Ramp *ramp, uint32_t clocksRamp, uint16_t clocksMinWL, uint16_t clocksMaxWL)
110 {
111         ASSERT(clocksMaxWL >= clocksMinWL);
112
113         // Save values in ramp struct
114         ramp->clocksRamp = clocksRamp;
115         ramp->clocksMinWL = clocksMinWL;
116         ramp->clocksMaxWL = clocksMaxWL;
117
118 #if RAMP_USE_FLOATING_POINT
119         ramp->precalc.gamma = ramp->clocksMaxWL - ramp->clocksMinWL;
120         ramp->precalc.beta = (float)ramp->clocksMinWL * (float)ramp->clocksRamp;
121         ramp->precalc.alpha = ramp->precalc.beta * (float)ramp->clocksMaxWL;
122
123 #else
124     ramp->precalc.max_div_min = ((uint32_t)clocksMinWL << 16) / (uint32_t)clocksMaxWL;
125
126     /* Calcola 1/total_time in fixed point .32. Assumiamo che la rampa possa al
127      * massimo avere 25 bit (cioĆ© valore in tick fino a 2^25, che con il
128      * prescaler=3 sono circa 7 secondi). Inoltre, togliamo qualche bit di precisione
129      * da destra (secondo quanto specificato in RAMP_CLOCK_SHIFT_PRECISION).
130      */
131     ASSERT(ramp->clocksRamp < (1UL << (24 + RAMP_CLOCK_SHIFT_PRECISION)));
132     ramp->precalc.inv_total_time = 0xFFFFFFFFUL / (ramp->clocksRamp >> RAMP_CLOCK_SHIFT_PRECISION);
133     ASSERT(ramp->precalc.inv_total_time < 0x1000000UL);
134
135 #endif
136 }
137
138
139 void ramp_setup(struct Ramp* ramp, uint32_t length, uint32_t minFreq, uint32_t maxFreq)
140 {
141         uint32_t minWL, maxWL;
142
143         minWL = TIME2CLOCKS(FREQ2MICROS(maxFreq));
144         maxWL = TIME2CLOCKS(FREQ2MICROS(minFreq));
145
146         ASSERT2(minWL < 65536UL, "Maximum frequency too high");
147         ASSERT2(maxWL < 65536UL, "Minimum frequency too high");
148         ASSERT(maxFreq > minFreq);
149
150         ramp_compute(
151                 ramp,
152                 TIME2CLOCKS(length),
153                 TIME2CLOCKS(FREQ2MICROS(maxFreq)),
154                 TIME2CLOCKS(FREQ2MICROS(minFreq))
155         );
156 }
157
158 void ramp_default(struct Ramp *ramp)
159 {
160         ramp_setup(ramp, RAMP_DEF_TIME, RAMP_DEF_MINFREQ, RAMP_DEF_MAXFREQ);
161 }
162
163 #if RAMP_USE_FLOATING_POINT
164
165 float ramp_evaluate(const struct Ramp* ramp, float curClock)
166 {
167         return ramp->precalc.alpha / (curClock * ramp->precalc.gamma + ramp->precalc.beta);
168 }
169
170 #else
171
172 INLINE uint32_t fix_mult32(uint32_t m1, uint32_t m2)
173 {
174         uint32_t accum = 0;
175         accum += m1 * ((m2 >> 0) & 0xFF);
176         accum >>= 8;
177         accum += m1 * ((m2 >> 8) & 0xFF);
178         accum >>= 8;
179         accum += m1 * ((m2 >> 16) & 0xFF);
180         return accum;
181 }
182
183 //   a*b >> 16
184 INLINE uint16_t fix_mult16(uint16_t a, uint32_t b)
185 {
186         return (b*(uint32_t)a) >> 16;
187 }
188
189 uint16_t FAST_FUNC ramp_evaluate(const struct Ramp* ramp, uint32_t curClock)
190 {
191         uint16_t t = FIX_MULT32(curClock >> RAMP_CLOCK_SHIFT_PRECISION, ramp->precalc.inv_total_time);
192         uint16_t denom =  fix_mult16((uint16_t)~t + 1, ramp->precalc.max_div_min) + t;
193         uint16_t cur_delta = ((uint32_t)ramp->clocksMinWL << 16) / denom;
194
195         return cur_delta;
196 }
197
198 #endif
199
200