Add test suite for ramp and pwm driver.
authorasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Mon, 17 Mar 2008 10:23:05 +0000 (10:23 +0000)
committerasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Mon, 17 Mar 2008 10:23:05 +0000 (10:23 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1191 38d2e660-2303-0410-9eaa-f027e97ec537

drv/pwm_test.c [new file with mode: 0644]
drv/ramp_test.c [new file with mode: 0644]

diff --git a/drv/pwm_test.c b/drv/pwm_test.c
new file mode 100644 (file)
index 0000000..cb2dc84
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * \file
+ * <!--
+ * This file is part of BeRTOS.
+ *
+ * Bertos is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction.  Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License.  This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
+ * -->
+ *
+ *
+ * \brief Test for PWM driver (implementation)
+ *
+ * \version $Id$
+ *
+ * \author Daniele Basile <asterix@develer.com>
+ */
+
+#include <cpu/types.h>
+#include <cpu/irq.h>
+
+#include <drv/pwm.h>
+#include <drv/pwm_at91.h>
+
+#include <cfg/macros.h>
+#include <cfg/debug.h>
+
+/*
+ * Esample of value for duty cycle"
+ *
+ * - 100% => 0xFFFFFFFF
+ * - 80%  => 0xCCCCCCCC
+ * - 75%  => 0xBFFFFFFF
+ * - 50%  => 0x7FFFFFFF
+ * - 25%  => 0x3FFFFFFF
+ * - 33%  => 0x55555555
+ * - 16%  => 0x2AAAAAAA
+ */
+
+#define PWM_TEST_CH0                     0
+#define PWM_TEST_CH0_FREQ            100UL // 100Hz
+#define PWM_TEST_CH0_DUTY           0xCCCC // 80%
+
+#define PWM_TEST_CH1                     1
+#define PWM_TEST_CH1_FREQ           1000UL  // 1000Hz
+#define PWM_TEST_CH1_DUTY           0xBFFF  // 75%
+
+#define PWM_TEST_CH2                     2
+#define PWM_TEST_CH2_FREQ            12356  // 12356Hz
+#define PWM_TEST_CH2_DUTY           0x7FFF  // 50%
+
+#define PWM_TEST_CH3                     3
+#define PWM_TEST_CH3_FREQ         100000UL  // 100KHz
+#define PWM_TEST_CH3_DUTY           0x5555  // 33%
+
+#define PWM_TEST_CH_SET(index) \
+       do { \
+                       pwm_setFrequency(PWM_TEST_CH##index , PWM_TEST_CH##index##_FREQ); \
+                       pwm_setDuty(PWM_TEST_CH##index, PWM_TEST_CH##index##_DUTY); \
+                       pwm_enable(PWM_TEST_CH##index, true); \
+       } while (0)
+
+/**
+ * Test suit for genation of pwm waveform.
+ *
+ */
+void pwm_test(void)
+{
+
+       pwm_init();
+       kputs("Init pwm..\n");
+
+       PWM_TEST_CH_SET(0);
+       kprintf("Set pwm ch[%d] =>freq[%ld], duty[%d]\n", PWM_TEST_CH0, PWM_TEST_CH0_FREQ, PWM_TEST_CH0_DUTY);
+       PWM_TEST_CH_SET(1);
+       kprintf("Set pwm ch[%d] =>freq[%ld], duty[%d]\n", PWM_TEST_CH1, PWM_TEST_CH1_FREQ, PWM_TEST_CH1_DUTY);
+       PWM_TEST_CH_SET(2);
+       kprintf("Set pwm ch[%d] =>freq[%ld], duty[%d]\n", PWM_TEST_CH2, PWM_TEST_CH2_FREQ, PWM_TEST_CH2_DUTY);
+       PWM_TEST_CH_SET(3);
+       kprintf("Set pwm ch[%d] =>freq[%ld], duty[%d]\n", PWM_TEST_CH3, PWM_TEST_CH3_FREQ, PWM_TEST_CH3_DUTY);
+}
+
+
+
+
+
+
diff --git a/drv/ramp_test.c b/drv/ramp_test.c
new file mode 100644 (file)
index 0000000..92de940
--- /dev/null
@@ -0,0 +1,175 @@
+/*!
+ * \file
+ * <!--
+ * Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Test for compute, save and load ramps for stepper motors (implementation)
+ *
+ * \version $Id$
+ *
+ * \author Simone Zinanni <s.zinanni@develer.com>
+ * \author Bernardo Innocenti <bernie@develer.com>
+ * \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>
+
+static bool ramp_test_single(uint32_t minFreq, uint32_t maxFreq, uint32_t length)
+{
+    struct Ramp r;
+    uint16_t cur, old;
+    uint32_t clock;
+    uint32_t oldclock;
+
+    ramp_setup(&r, length, minFreq, maxFreq);
+
+    cur = old = r.clocksMaxWL;
+    clock = 0;
+    oldclock = 0;
+
+    kprintf("testing ramp: (length=%lu, min=%lu, max=%lu)\n", length, minFreq, maxFreq);
+    kprintf("              [length=%lu, max=%04x, min=%04x]\n", r.clocksRamp, r.clocksMaxWL, r.clocksMinWL);
+
+       int i = 0;
+    int nonbyte = 0;
+
+    while (clock + cur < r.clocksRamp)
+    {
+        oldclock = clock;
+        old = cur;
+
+        clock += cur;
+        cur = ramp_evaluate(&r, clock);
+
+        if (old < cur)
+        {
+            uint16_t t1 = FIX_MULT32(oldclock >> RAMP_CLOCK_SHIFT_PRECISION, r.precalc.inv_total_time);
+            uint16_t t2 = FIX_MULT32(clock >> RAMP_CLOCK_SHIFT_PRECISION,    r.precalc.inv_total_time);
+            uint16_t denom1 = FIX_MULT32((uint16_t)((~t1) + 1), r.precalc.max_div_min) + t1;
+            uint16_t denom2 = FIX_MULT32((uint16_t)((~t2) + 1), r.precalc.max_div_min) + t2;
+
+            kprintf("    Failed: %04x @ %lu   -->   %04x @ %lu\n", old, oldclock, cur, clock);
+            kprintf("    T:     %04x -> %04x\n", t1, t2);
+            kprintf("    DENOM: %04x -> %04x\n", denom1, denom2);
+
+            cur = ramp_evaluate(&r, clock);
+            return false;
+        }
+               i++;
+               if ((old-cur) >= 256)
+                       nonbyte++;
+    }
+
+
+
+    kprintf("Test finished: %04x @ %lu [min=%04x, totlen=%lu, numsteps:%d, nonbyte:%d]\n", cur, clock, r.clocksMinWL, r.clocksRamp, i, nonbyte);
+
+    return true;
+}
+
+
+void ramp_test(void)
+{
+    bool ok = true;
+
+    ok = ramp_test_single(200,  5000, 3000000) && ok;
+    ok = ramp_test_single(1000, 2000, 1000000) && ok;
+
+    if (ok)
+        kputs("Ramp test succeeded!\n");
+       else
+        kputs("Ramp test fail!\n");
+}
+
+