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;
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 {
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
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)