]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lib.git/commitdiff
CAN timing parameters calculation fixed, interface for manual specification of timing...
authorMichal Horn <hornmich@fel.cvut.cz>
Thu, 9 Oct 2014 10:44:00 +0000 (12:44 +0200)
committerMichal Horn <hornmich@fel.cvut.cz>
Thu, 9 Oct 2014 10:44:00 +0000 (12:44 +0200)
The previous algorithm for timing parameters calculation from desired
baudrate was working only for few baudrates values. When tested from
100kb to 1mb with step 100kb, only 100, 500 and 800kb were working
correctly. The new algorithm is much better. Now only 300, 600 and 900kb
are not working correctly, when communicating with PC via USB2CAN.

For the cases that non working baudrate is desired or more control
over the frame timing is needed, an interface for manual timing
specificatiion has been implemented.

Fixes #979

Signed-off-by: Michal Horn <hornmich@fel.cvut.cz>
rpp/include/rpp/can.h
rpp/src/rpp/can.c

index 4907b7aae01cf8c16b475af7431e3dddd7e229e9..af00b82f4fb336e18c14eb511166c3060d7197aa 100644 (file)
@@ -34,6 +34,11 @@ enum rpp_can_frame_type {
        RPP_CAN_MIXED,
 };
 
+enum rpp_can_timing_calculation {
+       RPP_CAN_TIMING_CALC_MANUAL,
+       RPP_CAN_TIMING_CALC_AUTO
+};
+
 struct rpp_can_tx_config {
        enum rpp_can_frame_type type;
        uint8_t controller;
@@ -48,8 +53,73 @@ struct rpp_can_rx_config {
        uint32_t mask;
 };
 
+/**
+ * CAN controller configuration.
+ */
 struct rpp_can_ctrl_config {
+       /**
+        * Desired baudrate for automatic calculation in bits per second.
+        * Not used, when RPP_CAN_TIMING_CALC_MANUAL is set in timing_calc_method.
+        */
        uint32_t baudrate;
+       /**
+        * CAN input frequency in Hz.
+        */
+       uint32_t clk;
+       /* FIXME: modify the timing calculation to variable propagation delay. */
+       /**
+        * Minimal propagation delay, not used in the calculation yet.
+        */
+       uint16_t prop_delay;
+       /**
+        * RPP_CAN_TIMING_CALC_MANUAL for manually specified timing parameters,
+        * RPP_CAN_TIMING_CALC_AUTO for automatic parameters calculation from desired baudrate.
+        */
+       uint8_t timing_calc_method;
+       /**
+        * Timing parameters, may be NULL when  RPP_CAN_TIMING_CALC_MANUAL
+        * is set in timing_calc_method.
+        */
+       struct rpp_can_timing_cfg *timing_config;
+};
+
+/**
+ * CAN timing specification. Used for storing manually specified timing parameters, which derive
+ * the baudrate or for storing automatically calculated parameters from specified baudrate.
+ */
+struct rpp_can_timing_cfg {
+       /**
+        * Baudrate prescaler (1-64), specifies the lenght of time quantum (tQ).
+        */
+       uint16_t brp;
+       /**
+        * The length of the propagation segment (1-8) in tQ.
+        */
+       uint8_t prop_seg;
+       /**
+        * The length of the phase buffer segment 1 (1-8) in tQ.
+        */
+       uint8_t phase_seg1;
+       /**
+        * The length of the phase buffer segment 2 (1-8) in tQ.
+        */
+       uint8_t phase_seg2;
+       /**
+        * The synchronization jump width (1-4) in tQ.
+        */
+       uint8_t sjw;
+       /**
+        * The length of the time quantum (tQ) in ns. Is calculated automatically from baudrate.
+        */
+       uint32_t tq;
+       /**
+        * The error rate from desired baudrate. Is calculated automatically during the timing parameters calculation.
+        */
+       uint32_t error;
+       /**
+        * The Sample point delay from the start of the frame in ns. Is calculated automatically from the baudrate.
+        */
+       int sampl_pt;
 };
 
 struct rpp_can_config {
@@ -58,7 +128,6 @@ struct rpp_can_config {
        struct rpp_can_tx_config *tx_config;
        struct rpp_can_rx_config *rx_config;
        struct rpp_can_ctrl_config *ctrl;
-
 };
 
 #define CAN_EFF_FLAG 0x80000000U
index 5f5505e10c2de943b9cefb7c402c68ae5a740106..2e3e9d0032614bcdf7dac3da9cea103ba16afaa8 100644 (file)
@@ -314,86 +314,178 @@ static int8_t can_reset(canBASE_t *controller)
     return SUCCESS;
 }
 
-static int8_t can_configure(canBASE_t *controller, const struct rpp_can_ctrl_config cfg)
-{
-    const uint32_t FREQ = 80000000;
-
-    // Minimal propagation time
-    // NOTE: The time is just guessed from working code
-    const double PROP_MIN = 0.00000075;
-
-    double bit_time, prop_part;
-    uint32_t prescaler, ts, ts1, ts2, prop;
-    uint32_t n;
-    double check;
-
-    /**
-    * Refer to TMS570LS3137 manual (spnu499a) for more information
-    * about calculation of bit timing parameters
-    */
-
-    bit_time = 1.0/cfg.baudrate;
-
-    // We shall abort if bit time isn't integer multiple of CAN clock
-    /**
-    * Some tolerance is needed due to the inaccuracy of binary representation
-    * Feel free to replace those few lines with more elegant solution
-    */
-    check = bit_time * FREQ;
-    if (fabs(check - nearbyint(check)) >= 0.001)
-    {
-       return FAILURE;
-    }
-
-
-    /**
-    * If baudrate is low enough, we set
-    * Prop_Seg = 1, Phase_Seg1 = Phase_Seg2 = SJW = 4
-    * which should be optimal.
-    */
-    if (cfg.baudrate <= 250000)
-    {
-        // 10 stands for 10 time quanta of bit time
-        prescaler = FREQ*bit_time / 10;
-
-        ts2 = ts1 = 4;
-        prop = 1;
-    }
+static int8_t can_set_timing(canBASE_t *controller, const struct rpp_can_timing_cfg cfg) {
+       controller->BTR = (((cfg.brp-1) >> 6) << 16)
+                               | ((cfg.phase_seg2-1) << 12)
+                               | (((cfg.phase_seg1+cfg.prop_seg)-1) << 8)
+                               | ((cfg.sjw-1) << 6)
+                               | ((cfg.brp-1) & 63);
+       return SUCCESS;
+}
 
-    else
-    {
-        prop_part = PROP_MIN / bit_time;
-
-        if (prop_part <= 0.5)
-        {
-            n = 10;
-            prop = (int) (prop_part / 0.1) + 1;
-        }
-        else
-        {
-            n = 20;
-            prop = (int) (prop_part / 0.05) + 1;
-        }
-
-        ts = n - 1 - prop;
-        ts1 = ts / 2;
-        ts2 = (ts % 2 ? ts1 + 1 : ts1);
-
-        prescaler = FREQ*bit_time / n;
-    }
+/* This code has been taken from can-calc-bit-timing.c */
+/* can-calc-bit-timing.c: Calculate CAN bit timing parameters
+ *
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from:
+ *   can_baud.c - CAN baudrate calculation
+ *   Code based on LinCAN sources and H8S2638 project
+ *   Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ *   Copyright 2005      Stanislav Marek
+ *   email:pisa@cmp.felk.cvut.cz
+ *
+ *   This software is released under the GPL-License.
+ */
+static int can_update_spt(int phase_seg2_min, int phase_seg2_max, int prop_seg_max, int phase_seg1_max,
+                         int sampl_pt, int tseg, int *tseg1, int *tseg2)
+{
+       *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
+       if (*tseg2 < phase_seg2_min)
+               *tseg2 = phase_seg2_min;
+       if (*tseg2 > phase_seg2_max)
+               *tseg2 = phase_seg2_max;
+       *tseg1 = tseg - *tseg2;
+       if (*tseg1 > prop_seg_max + phase_seg1_max) {
+               *tseg1 = prop_seg_max + phase_seg1_max;
+               *tseg2 = tseg - *tseg1;
+       }
+       return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+}
 
-    /**
-    * We make TSeg2 equal SJW since our calculation guarantees
-    * that TSeg1 >= TSeg2 AND Tseg2 <= 4
-    */
-    // All actual values are by 1 higher than those programmed
-    controller->BTR = (((prescaler-1) >> 6) << 16)
-                      | ((ts2-1) << 12)
-                      | (((ts1+prop)-1) << 8)
-                      | ((ts2-1) << 6)
-                      | ((prescaler-1) & 63);
+/* This code has been taken from can-calc-bit-timing.c */
+/* can-calc-bit-timing.c: Calculate CAN bit timing parameters
+ *
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from:
+ *   can_baud.c - CAN baudrate calculation
+ *   Code based on LinCAN sources and H8S2638 project
+ *   Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ *   Copyright 2005      Stanislav Marek
+ *   email:pisa@cmp.felk.cvut.cz
+ *
+ *   This software is released under the GPL-License.
+ */
+static int8_t can_calculate_timing(const struct rpp_can_ctrl_config cfg, struct rpp_can_timing_cfg *timing) {
+       static const int prop_seg_min = 1;
+       static const int prop_seg_max = 8;
+       static const int phase_seg1_min = 1;
+       static const int phase_seg1_max = 8;
+       static const int phase_seg2_min = 1;
+       static const int phase_seg2_max = 8;
+       static const int brp_min = 1;
+       static const int brp_max = 64;
+       static const int brp_inc = 1;
+
+       if (timing == NULL) {
+               return FAILURE;
+       }
+
+       int sampl_pt;
+       long best_error = 1000000000, error;
+       int best_tseg = 0, best_brp = 0, brp = 0;
+       int spt_error = 1000, spt = 0;
+       long rate = 0;
+       int tseg = 0, tseg1 = 0, tseg2 = 0;
+       uint64_t v64;
+
+       /* Use CIA recommended sample points */
+       if (cfg.baudrate > 800000) {
+               sampl_pt = 750;
+       }
+       else if (cfg.baudrate > 500000) {
+               sampl_pt = 800;
+       }
+       else {
+               sampl_pt = 875;
+       }
+
+       /* tseg even = round down, odd = round up */
+       for (tseg = (prop_seg_max + phase_seg1_max + phase_seg2_max) * 2 + 1;
+                tseg >= (prop_seg_min + phase_seg1_min + phase_seg2_min) * 2;
+                tseg--) {
+               /* Compute all posibilities of tseg choices (tseg=tseg1+tseg2) */
+               brp = cfg.clk / ((1 + tseg / 2) * cfg.baudrate) + tseg % 2;
+               /* chose brp step which is possible in system */
+               brp = (brp / brp_inc) * brp_inc;
+               if ((brp < brp_min) || (brp > brp_max))
+                       continue;
+               rate = cfg.clk / (brp * (1 + tseg / 2));
+               error = cfg.baudrate - rate;
+               /* tseg brp biterror */
+
+               if (error < 0)
+                       error = -error;
+               if (error > best_error)
+                       continue;
+               best_error = error;
+               if (error == 0) {
+                       spt = can_update_spt(phase_seg2_min, phase_seg2_max, prop_seg_max, phase_seg1_max, sampl_pt, tseg / 2, &tseg1, &tseg2);
+                       error = sampl_pt - spt;
+                       if (error < 0)
+                               error = -error;
+                       if (error > spt_error)
+                               continue;
+                       spt_error = error;
+               }
+               best_tseg = tseg / 2;
+               best_brp = brp;
+               if (error == 0)
+                       break;
+       }
+
+       if (best_error && (cfg.baudrate / best_error < 10))
+               return -1;
+
+       tseg2 = best_tseg + 1 - (sampl_pt * (best_tseg + 1)) / 1000;
+       spt = can_update_spt(phase_seg2_min, phase_seg2_max, prop_seg_max, phase_seg1_max, sampl_pt, best_tseg, &tseg1, &tseg2);
+
+       if (tseg2 > tseg1) {
+               /* sample point < 50% */
+               timing->phase_seg1 = tseg1 / 2;
+       } else {
+               /* keep phase_seg{1,2} equal around the sample point */
+               timing->phase_seg1 = tseg2;
+       }
+       timing->prop_seg = tseg1 - timing->phase_seg1;
+       /* Check prop_seg range if necessary */
+       if (prop_seg_min || prop_seg_max) {
+               if (timing->prop_seg < prop_seg_min)
+                       timing->prop_seg = prop_seg_min;
+               else if (timing->prop_seg > prop_seg_max)
+                       timing->prop_seg = prop_seg_max;
+               timing->phase_seg1 = tseg1 - timing->prop_seg;
+       }
+       timing->phase_seg2 = tseg2;
+       timing->sjw = 1;
+       timing->brp = best_brp;
+       timing->error = best_error;
+       timing->sampl_pt = spt;
+       v64 = (uint64_t)timing->brp * 1000000000UL;
+       v64 /= cfg.clk;
+       timing->tq = (int)v64;
+
+       return SUCCESS;
+}
 
-    return SUCCESS;
+static int8_t can_configure(canBASE_t *controller, const struct rpp_can_ctrl_config cfg)
+{
+       if (cfg.timing_calc_method == RPP_CAN_TIMING_CALC_AUTO) {
+               struct rpp_can_timing_cfg timing_cfg;
+               if (can_calculate_timing(cfg, &timing_cfg) == SUCCESS) {
+                       return can_set_timing(controller, timing_cfg);
+               }
+               else {
+                       return FAILURE;
+               }
+       }
+       else if (cfg.timing_calc_method == RPP_CAN_TIMING_CALC_MANUAL && cfg.timing_config != NULL) {
+               return can_set_timing(controller, *cfg.timing_config);
+       }
+       else {
+               return FAILURE;
+       }
 }
 
 static inline void can_leave_init(canBASE_t *controller)