CAN bit timing calculator

kgschlosser
Posts: 7
Joined: Mon Oct 11, 2021 12:21 pm

CAN bit timing calculator

Postby kgschlosser » Mon Oct 11, 2021 1:54 pm

I am sure there are some of you that will find this code extremely helpful. It calculates the closest match for any bitrate supplied to it. There are adjustments to control the number of returned results. read the comments in the code.

It is exceedingly quick and has a small memory footprint for what it does. if doing an identical match the impact on ram is something along the lines of a couple hundred bytes, if wanting a bunch of possible matches then how much ram is used is going to depend on the number of matches.

If someone can get me the oscillator tolerance in the ESP32 I can add the calculations needed to ensure that the selected timings are within that percentage.
  1. /* The MIT License (MIT)
  2.  *
  3.  * Copyright (c) 2021 Kevin Schlosser
  4.  *
  5.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  6.  * of this software and associated documentation files (the "Software"), to deal
  7.  * in the Software without restriction, including without limitation the rights
  8.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9.  * copies of the Software, and to permit persons to whom the Software is
  10.  * furnished to do so, subject to the following conditions:
  11.  *
  12.  * The above copyright notice and this permission notice shall be included in
  13.  * all copies or substantial portions of the Software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21.  * THE SOFTWARE.
  22.  */
  23. #include "driver/twai.h"
  24.  
  25. #include <math.h>
  26.  
  27. #define TSEG1_MIN  2
  28. #define TSEG1_MAX  16
  29.  
  30. #define TSEG2_MIN  1
  31. #define TSEG2_MAX  8
  32.  
  33. #define BRP_MIN  2
  34. #define BRP_INC  2
  35.  
  36. #define SJW_MAX  4
  37.  
  38. #define FSYS  80000000
  39.  
  40. typedef struct _machine_can_bitrate_t {
  41.     uint32_t bitrate;
  42.     uint16_t brp;
  43.     uint8_t tseg1;
  44.     uint8_t tseg2;
  45.     uint8_t sjw;
  46.     float br_err;
  47.     float sp_err;
  48. } machine_can_bitrate_t;
  49.  
  50.  
  51. // the function needs to be called 2 times, the first time passing NULL to matches and 0 to num_matches.
  52. // The function will return the number of matches found and that number is used to create the proper sized array
  53. // that gets passed to the matches parameter and you must also pass that value to the num_matches parameter.
  54. // see code examples below for usage.  
  55. int get_bitrates(
  56.     uint32_t nominal_bitrate, // target bitrate
  57.     float bitrate_tolerance, // allowed percentage of drift from the target bitrate
  58.     float nominal_sample_point, // target sample point, if 0.0 is given then a CIA standard sample point will get used
  59.     float sample_point_tolerance, // allowed percentage of drift from the target sample point
  60.     uint16_t bus_length, // bus length in meters, round up or down if bus length if fractional
  61.     uint16_t transceiver_delay, // processing time of the transceiver being used (nanoseconds)
  62.     machine_can_bitrate_t *matches, // NULL or array of machine_can_bitrate_t structures
  63.     int num_matches // 0 or the length of matches
  64. ) {
  65.  
  66.     // check to see if a sample point was supplied.
  67.     // If one was not then we use the CIA standard to assign a sample point
  68.     if (nominal_sample_point == 0) {
  69.         if (nominal_bitrate > 800000) {
  70.             nominal_sample_point = 75.0F;
  71.         } else if (nominal_bitrate > 500000) {
  72.             nominal_sample_point = 80.0F;
  73.         } else {
  74.             nominal_bitrate = 87.5F;
  75.         }
  76.     }
  77.  
  78.     float tq;
  79.     float btq;
  80.     float bt = 1.0F / (float) nominal_bitrate;
  81.     float sample_point;
  82.  
  83.     uint32_t t_prop_seg;
  84.     int match_count = 0;
  85.     uint8_t btq_rounded;
  86.  
  87.     machine_can_bitrate_t match;
  88.  
  89.  
  90.     for (match.brp = BRP_MIN;match.brp <= TWAI_BRP_MAX;match.brp += BRP_INC) {
  91.         // the macro used here is to validate the brp. This is done because
  92.         // a V2 or greater revision ESP32 has 2 ranges of brps.
  93.         // The first range is any even number from 2 to 128, ad the second range is every 4th number from 132 to 256.
  94.         // the brp increment starts the same at 2 for both ranges so we need to verify a correct brp when
  95.         // running through the second range
  96.         if (!TWAI_BRP_IS_VALID(match.brp)) {
  97.             continue;
  98.         }
  99.         // calculate the time quanta
  100.         tq = 1.0F / ((float) FSYS / (float) match.brp);
  101.         // calculate the number of time quanta needed for the given bitrate
  102.         btq = bt / tq;
  103.         // we need to use the quanta as a whole and not fractions.
  104.         btq_rounded = (uint8_t) roundf(btq);
  105.  
  106.         // if time quanta < 1.0 then the brp is unsupported for the wanted bitrate
  107.         if (btq_rounded < (TSEG1_MIN + TSEG2_MIN + 1)) {
  108.             continue;
  109.         }
  110.  
  111.         // calculate the actual bitrate for the brp being used.
  112.         match.bitrate = (uint32_t) roundf(
  113.             (float)nominal_bitrate * (1.0F - (roundf(-(btq / (float) btq_rounded - 1.0F) * 10000.0F) / 10000.0F))
  114.         );
  115.  
  116.         // Calculate the amount of drift from the target bitrate
  117.         match.br_err = (float) abs(match.bitrate - nominal_bitrate) / (float) nominal_bitrate * 100.0F;
  118.  
  119.         // if the amount of drift exceeds the allowed amount then the brp cannot be used
  120.         if (match.br_err > bitrate_tolerance) {
  121.             continue;
  122.         }
  123.  
  124.         // because we know the target sample point we are able to calculate a starting tseg1 and tseg2
  125.         // using that sample point
  126.         match.tseg1 = (uint8_t) ((float) btq_rounded * (nominal_sample_point / 100.0F)) - 1;
  127.         match.tseg2 = (uint8_t) (btq_rounded - (match.tseg1 + 1));
  128.  
  129.         if (match.tseg2 < TSEG2_MIN) {
  130.             match.tseg1 += match.tseg2 - TSEG2_MIN;
  131.             match.tseg2 = TSEG2_MIN;
  132.         }
  133.  
  134.         // just because we have a given sample point doesn't mean it will align on a whole tq.
  135.         // so once we get the tseg1 and tseg2 we need to calculate what the "real" sample point is
  136.         sample_point = (float) (match.tseg1 + 1) / (float) btq_rounded * 100.0F;
  137.  
  138.         // once we have the sample point we need to calculate how much it drifts from the target sample point
  139.         match.sp_err = (float) fabs(
  140.             (double) ((sample_point - nominal_sample_point) / nominal_sample_point * 100.0F)
  141.         );
  142.  
  143.         // I could have iterated over all of the available tseg1 values and calculated the tseg2 from that
  144.         // then checked to see if the sample point was within the allotted error for each iteration.
  145.         // This is actually a waste to do do that. The minimum allowed sample point is 50% and if the btq is 10
  146.         // that means the minimum the tseg1 value could be is 5 and that would make the tseg2 have a value of 4.
  147.         // There is a synchronization bit that gets added to make the total of 10 needed.
  148.         // it would be pointless to do iterations for tseg1 values of 1-4. wasted time.
  149.         // you also have to consider the allowed sample point shift that is given. This is what I am focusing on
  150.         // due to it creating the smallest number of iterations.
  151.         // so say we have a supplied 80% sample point with a 5% allowed drift. that would make tseg1 = 7, tseg2 = 2
  152.         //  if we change the tseg1 to 6 and the tseg2 to 3 we now have a 70% sample point which is outside of the
  153.         // allowed 5% deviation.
  154.  
  155.         // so what I have done is the first while loop decreases the tseg1 by 1 and increases the tseg2 by one until
  156.         // the drive is outside of the allowed amount. The second while loop then increases the tseg1 and decreases
  157.         // the tseg2 and adding each iteration to the matches until it is outside of the allowed amount. Best case
  158.         // scenario is no iteration gets performed if the initial tseg1 and tseg2 is not within the sample point
  159.         // tolerance. This saves quite a bit of time. If using an ESP32S2 the total number of brps are 16384 and
  160.         // say we use a target bitrate of 500000bps and a bitrate tolerance of 0.0 there are a total of 23 brps that
  161.         // matched. then having to do 50 iterations for each of the brps brings the total up to 345 iterations for
  162.         // the tseg1. By using the code below it lowers that iteration count to 35 between both while loops.
  163.         // That is a HUGE difference. Running the same code in Python iterating over all of the tseg1 values
  164.         // has a calculation time of 130ms. and using the code below the calculation time is 16ms.
  165.         // That's an 87.69% reduction in the time it takes to run the calculations. I have not measured the time it
  166.         // takes with C code. Typically there is a 200% increase in speed compared to Python
  167.  
  168.         // I also threw in another nicety and that is if there is an exact match it will return immediately with
  169.         // only the one match.
  170.         while (
  171.             match.sp_err <= sample_point_tolerance &&
  172.             match.tseg1 + 1 >= match.tseg2 &&
  173.             match.tseg1 <= TSEG1_MAX &&
  174.             match.tseg1 >= TSEG1_MIN &&
  175.             match.tseg2 <= TSEG2_MAX &&
  176.             match.tseg2 >= TSEG2_MIN
  177.         ) {
  178.             match.tseg1--;
  179.             match.tseg2++;
  180.  
  181.             sample_point = (float) (match.tseg1 + 1) / (float) btq_rounded * 100.0F;
  182.             match.sp_err = (float) fabs(
  183.                 (double) ((sample_point - nominal_sample_point) / nominal_sample_point * 100.0F)
  184.             );
  185.         }
  186.  
  187.         match.tseg1++;
  188.         match.tseg2--;
  189.  
  190.         sample_point = (float) (match.tseg1 + 1) / (float) btq_rounded * 100.0F;
  191.         match.sp_err = (float) fabs(
  192.             (double) ((sample_point - nominal_sample_point) / nominal_sample_point * 100.0F)
  193.         );
  194.  
  195.         while (
  196.             match.sp_err <= sample_point_tolerance &&
  197.             match.tseg1 + 1 >= match.tseg2 &&
  198.             match.tseg1 <= TSEG1_MAX &&
  199.             match.tseg1 >= TSEG1_MIN &&
  200.             match.tseg2 <= TSEG2_MAX &&
  201.             match.tseg2 >= TSEG2_MIN
  202.         ) {
  203.  
  204.             if (num_matches > 0) {
  205.                 t_prop_seg = (uint32_t) (
  206.                     2.0F * (((float) transceiver_delay * 0.000000001F) + ((float) (bus_length * 5) * 0.000000001F))
  207.                 );
  208.                 match.sjw = (uint8_t) (btq_rounded - ((uint8_t) -((float) -t_prop_seg / tq)) - 1);
  209.  
  210.                 if (match.sjw < 3) {
  211.                     match.sjw = 0;
  212.                 } else if (match.sjw == 3) {
  213.                     match.sjw = 1;
  214.                 } else {
  215.                     match.sjw = match.sjw / 2;
  216.                 }
  217.  
  218.                 if (match.sjw > SJW_MAX) {
  219.                     match.sjw = SJW_MAX;
  220.                 }
  221.  
  222.                 if (match.sp_err == 0.0F && match.br_err == 0.0F) {
  223.                     matches[0] = match;
  224.                     return 1;
  225.                 }
  226.  
  227.                 if (match_count == num_matches) {
  228.                     if (num_matches == 1) {
  229.                         if (
  230.                             match.br_err <= matches[0].br_err &&
  231.                             match.sp_err <= matches[0].sp_err
  232.                         ) {
  233.                             matches[0] = match;
  234.                         }
  235.                     } else {
  236.                         return -1;
  237.                     }
  238.                 } else {
  239.                     matches[match_count] = match;
  240.                     match_count++;
  241.                 }
  242.  
  243.             } else if (match.sp_err == 0.0F && match.br_err == 0.0F) {
  244.                 return 1;
  245.             } else {
  246.                 match_count ++;
  247.             }
  248.  
  249.             match.tseg1++;
  250.             match.tseg2--;
  251.  
  252.             sample_point = (float) (match.tseg1 + 1) / (float) btq_rounded * 100.0F;
  253.             match.sp_err = (float) fabs(
  254.                 (double) ((sample_point - nominal_sample_point) / nominal_sample_point * 100.0F)
  255.             );
  256.         }
  257.     }
  258.     return match_count;
  259. }
  260.  
  261.  
  262. // ************* CODE EXAMPLES **************
  263.  
  264. // this code example returns all found bitrates that match the input values.
  265. // if an identical match is found that is the only one that will get populated.
  266. // If I knew what the oscillator drift % was I could fine tune the script so it
  267. // would return only bitrates that are within that percentage
  268. int count = get_bitrates(
  269.     500000,
  270.     2.0F,
  271.     62.0F,
  272.     10.0F,
  273.     1,
  274.     150,
  275.     NULL,
  276.     0
  277. );
  278.  
  279. if (count > 0) {
  280.     machine_can_bitrate_t bitrates[count]
  281.     if (get_bitrates(500000, 2.0F, 62.0F, 10.0F, 1, 150, bitrates, count) > 0) {
  282.         for (int i = 0;i < count;i++) {
  283.             bitrates[i].bitrate;
  284.             bitrates[i].brp;
  285.             bitrates[i].tseg1;
  286.             bitrates[i].tseg2;
  287.             bitrates[i].sjw;
  288.             bitrates[i].br_err;
  289.             bitrates[i].sp_err;
  290.  
  291.         }
  292.     }
  293. }
  294.  
  295. // this code example returns the closest match to the input values
  296. machine_can_bitrate_t bitrates[1]
  297. if (get_bitrates(500000, 2.0F, 62.0F, 10.0F, 1, 150, bitrates, 1) > 0) {
  298.     bitrates[0].bitrate;
  299.     bitrates[0].brp;
  300.     bitrates[0].tseg1;
  301.     bitrates[0].tseg2;
  302.     bitrates[0].sjw;
  303.     bitrates[0].br_err;
  304.     bitrates[0].sp_err;
  305. }

kgschlosser
Posts: 7
Joined: Mon Oct 11, 2021 12:21 pm

Re: CAN bit timing calculator

Postby kgschlosser » Thu Oct 14, 2021 12:44 am

here is an updated version of the code above. I had incorrect values being returned on occasion and it was because of how the compiler was handling setting a structure to an array. I tried using memcpy and had the same results. So instead of filling a structure and then placing it into the array I have declared variables for each of the items that fill the structure and place the items into the fields in the array.

I also added code to round the sample_point, sample point error and bitrate error to the second decimal position. no real reason to deal with decimal positions past that.
  1. /* The MIT License (MIT)
  2.  *
  3.  * Copyright (c) 2021 Kevin Schlosser
  4.  *
  5.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  6.  * of this software and associated documentation files (the "Software"), to deal
  7.  * in the Software without restriction, including without limitation the rights
  8.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9.  * copies of the Software, and to permit persons to whom the Software is
  10.  * furnished to do so, subject to the following conditions:
  11.  *
  12.  * The above copyright notice and this permission notice shall be included in
  13.  * all copies or substantial portions of the Software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21.  * THE SOFTWARE.
  22.  */
  23. #include "driver/twai.h"
  24.  
  25. #include <math.h>
  26.  
  27. #define TSEG1_MIN  2
  28. #define TSEG1_MAX  16
  29.  
  30. #define TSEG2_MIN  1
  31. #define TSEG2_MAX  8
  32.  
  33. #define BRP_MIN  2
  34. #define BRP_MAX  TWAI_BRP_MAX
  35. #define BRP_INC  2
  36.  
  37. #define SJW_MAX  4
  38.  
  39. #define FSYS  80000000
  40. #define IS_BRP_VALID  TWAI_BRP_IS_VALID
  41.  
  42.  
  43. typedef struct _machine_can_bitrate_t {
  44.     uint32_t bitrate;
  45.     uint16_t brp;
  46.     uint8_t tseg1;
  47.     uint8_t tseg2;
  48.     uint8_t sjw;
  49.     float br_err;
  50.     float sp_err;
  51. } machine_can_bitrate_t;
  52.  
  53.  
  54. // function to deal with sample point and sample point error rounding
  55. float fround(float val) {
  56.     return (float) ((uint16_t) (val * 10000.0 + 0.5)) / 100.0;
  57. }
  58.  
  59.  
  60. // the function needs to be called 2 times, the first time passing NULL to matches and 0 to num_matches.
  61. // The function will return the number of matches found and that number is used to create the proper sized array
  62. // that gets passed to the matches parameter and you must also pass that value to the num_matches parameter.
  63. // see code examples below for usage.  
  64. int get_bitrates(
  65.     uint32_t nominal_bitrate, // target bitrate
  66.     float bitrate_tolerance, // allowed percentage of drift from the target bitrate
  67.     float nominal_sample_point, // target sample point, if 0.0 is given then a CIA standard sample point will get used
  68.     float sample_point_tolerance, // allowed percentage of drift from the target sample point
  69.     uint16_t bus_length, // bus length in meters, round up or down if bus length if fractional
  70.     uint16_t transceiver_delay, // processing time of the transceiver being used (nanoseconds)
  71.     machine_can_bitrate_t *matches, // NULL or array of machine_can_bitrate_t structures
  72.     int num_matches // 0 or the length of matches
  73. ) {
  74.  
  75.     // check to see if a sample point was supplied.
  76.     // If one was not then we use the CIA standard to assign a sample point
  77.     if (nominal_sample_point == 0) {
  78.         if (nominal_bitrate > 800000) {
  79.             nominal_sample_point = 75.0F;
  80.         } else if (nominal_bitrate > 500000) {
  81.             nominal_sample_point = 80.0F;
  82.         } else {
  83.             nominal_bitrate = 87.5F;
  84.         }
  85.     }
  86.  
  87.     float tq;
  88.     float btq;
  89.     float bt = 1.0F / (float) nominal_bitrate;
  90.     float sample_point;
  91.  
  92.     uint16_t match_count = 0;
  93.     uint8_t t_prop_seg;
  94.     uint32_t bitrate;
  95.     uint16_t brp;
  96.     uint8_t btq_rounded;
  97.     uint8_t tseg1;
  98.     uint8_t tseg2;
  99.     uint8_t sjw;
  100.     float br_err;
  101.     float sp_err;
  102.  
  103.  
  104.     for (brp = BRP_MIN;brp <= BRP_MAX;brp += BRP_INC) {
  105.         // the macro used here is to validate the brp. This is done because
  106.         // a V2 or greater revision ESP32 has 2 ranges of brps.
  107.         // The first range is any even number from 2 to 128, ad the second range is every 4th number from 132 to 256.
  108.         // the brp increment starts the same at 2 for both ranges so we need to verify a correct brp when
  109.         // running through the second range
  110.         if (!IS_BRP_VALID(brp)) {
  111.             continue;
  112.         }
  113.         // calculate the time quanta
  114.         tq = 1.0F / ((float) FSYS / (float) brp);
  115.         // calculate the number of time quanta needed for the given bitrate
  116.         btq = bt / tq;
  117.         // we need to use the quanta as a whole and not fractions.
  118.         btq_rounded = (uint8_t) roundf(btq);
  119.  
  120.         // if time quanta < 1.0 then the brp is unsupported for the wanted bitrate
  121.         if (btq_rounded < (TSEG1_MIN + TSEG2_MIN + 1)) {
  122.             continue;
  123.         }
  124.  
  125.         // calculate the actual bitrate for the brp being used.
  126.         bitrate = (uint32_t) roundf(
  127.             (float) nominal_bitrate * (1.0F - (roundf(-(btq / (float) btq_rounded - 1.0F) * 10000.0F) / 10000.0F))
  128.         );
  129.  
  130.         // Calculate the amount of drift from the target bitrate
  131.         br_err = fround((float) abs(bitrate - nominal_bitrate) / (float) nominal_bitrate);
  132.  
  133.         // if the amount of drift exceeds the allowed amount then the brp cannot be used
  134.         if (br_err > bitrate_tolerance) {
  135.             continue;
  136.         }
  137.  
  138.         // because we know the target sample point we are able to calculate a starting tseg1 and tseg2
  139.         // using that sample point
  140.         tseg1 = (uint8_t) ((float) btq_rounded * (nominal_sample_point / 100.0F));
  141.         tseg2 = (uint8_t) (btq_rounded - tseg1);
  142.  
  143.         if (tseg2 < TSEG2_MIN) {
  144.             tseg1 += TSEG2_MIN - tseg2;
  145.             tseg2 = TSEG2_MIN;
  146.         }
  147.  
  148.         // just because we have a gien sample point doesn't mean it will align on a whole tq.
  149.         // so once we get the tseg1 and tseg2 we need to calculate what the "real" sample point is
  150.         sample_point = fround((float) tseg1 / (float) btq_rounded);
  151.  
  152.         // once we have the sample point we need to calculate how much it drifts from the target sample point
  153.         sp_err = fround((float) fabs((double) (sample_point - nominal_sample_point)) / nominal_sample_point);
  154.  
  155.         // I could have iterated over all of the available tseg1 values and calculated the tseg2 from that
  156.         // then checked to see if the sample point was within the alloted error for each iteration.
  157.         // This is actually a waste to do do that. The minimum allowed sample point is 50% and if the btq is 10
  158.         // that means the minimum the tseg1 value could be is 5 and that would make the tseg2 have a value of 4.
  159.         // There is a syncronization bit that gets added to make the total of 10 needed.
  160.         // it would be pointless to do iterations for tseg1 values of 1-4. wasted time.
  161.         // you also have to consider the allowed sample point shift that is given. This is what I am focusing on
  162.         // due to it creating the smallest number of iterations.
  163.         // so say we have a supplied 80% sample point with a 5% allowed drift. that would make tseg1 = 7, tseg2 = 2
  164.         //  if we change the tseg1 to 6 and the tseg2 to 3 we now have a 70% sample point which is outside of the
  165.         // allowed 5% deviation.
  166.  
  167.         // so what I have done is the first while loop decreases the tseg1 by 1 and increases the tseg2 by one until
  168.         // the drive is outside of the allowed amount. The second while loop then increases the tseg1 and decreases
  169.         // the tseg2 and ading each iteration to the matches until it is outside of the allowed amount. Best case
  170.         // scenario is no iteration gets performed if the initial tseg1 and tseg2 is not within the sample point
  171.         // tolerance. This saves quite a bit of time. If using an ESP32S2 the total number of brps are 16384 and
  172.         // say we use a target bitrate of 500000bps and a bitrate tolerance of 0.0 there are a total of 23 brps that
  173.         // matched. then having to do 50 iterations for each of the brps brings the total up to 345 iterations for
  174.         // the tseg1. By using the code below it lowers that iteration count to 35 between both while loops.
  175.         // That is a HUGE difference. Running the same code in Python iterateing over all of the tseg1 values
  176.         // has a calculation time of 130ms. and using the code below the calculation time is 16ms.
  177.         // That's an 87.69% reduction in the time it takes to run the calculations.
  178.  
  179.         // I also threw in another niceity and that is if there is an exact match it will return immediatly with
  180.         // only the one match.
  181.         while (
  182.             sp_err <= sample_point_tolerance &&
  183.             tseg1 >= tseg2 &&
  184.             tseg1 <= TSEG1_MAX &&
  185.             tseg1 >= TSEG1_MIN &&
  186.             tseg2 <= TSEG2_MAX &&
  187.             tseg2 >= TSEG2_MIN
  188.         ) {
  189.             tseg1--;
  190.             tseg2++;
  191.  
  192.             sample_point = fround((float) tseg1 / (float) btq_rounded);
  193.             sp_err = fround((float) fabs((double) (sample_point - nominal_sample_point)) / nominal_sample_point);
  194.         }
  195.  
  196.         tseg1++;
  197.         tseg2--;
  198.  
  199.         sample_point = fround((float) tseg1 / (float) btq_rounded);
  200.         sp_err = fround((float) fabs((double) (sample_point - nominal_sample_point)) / nominal_sample_point);
  201.  
  202.         while (
  203.             sp_err <= sample_point_tolerance &&
  204.             tseg1 >= tseg2 &&
  205.             tseg1 <= TSEG1_MAX &&
  206.             tseg1 >= TSEG1_MIN &&
  207.             tseg2 <= TSEG2_MAX &&
  208.             tseg2 >= TSEG2_MIN
  209.         ) {
  210.  
  211.             if (num_matches > 0) {
  212.                 t_prop_seg = (uint8_t) (
  213.                     2.0F * (((float) transceiver_delay * 0.000000001F) + ((float) (bus_length * 5) * 0.000000001F))
  214.                 );
  215.                 sjw = (uint8_t) (btq_rounded - ((uint8_t) -((float) -t_prop_seg / tq)) - 1);
  216.  
  217.                 if (sjw < 3) {
  218.                     sjw = 0;
  219.                 } else if (sjw == 3) {
  220.                     sjw = 1;
  221.                 } else {
  222.                     sjw = sjw / 2;
  223.                 }
  224.  
  225.                 if (sjw > SJW_MAX) {
  226.                     sjw = SJW_MAX;
  227.                 }
  228.  
  229.                 if (sp_err == 0.0F && br_err == 0.0F) {
  230.                     matches[0].bitrate = bitrate;
  231.                     matches[0].brp = brp;
  232.                     matches[0].tseg1 = tseg1 - 1;
  233.                     matches[0].tseg2 = tseg2;
  234.                     matches[0].sjw = sjw;
  235.                     matches[0].br_err = br_err;
  236.                     matches[0].sp_err = sp_err;
  237.                     return 1;
  238.                 }
  239.  
  240.                 if (match_count == num_matches) {
  241.                     if (num_matches == 1) {
  242.                         if (
  243.                             br_err <= matches[0].br_err &&
  244.                             sp_err <= matches[0].sp_err
  245.                         ) {
  246.                             matches[0].bitrate = bitrate;
  247.                             matches[0].brp = brp;
  248.                             matches[0].tseg1 = tseg1 - 1;
  249.                             matches[0].tseg2 = tseg2;
  250.                             matches[0].sjw = sjw;
  251.                             matches[0].br_err = br_err;
  252.                             matches[0].sp_err = sp_err;
  253.                         }
  254.                     } else {
  255.                         return -1;
  256.                     }
  257.                 } else {
  258.                     matches[match_count].bitrate = bitrate;
  259.                     matches[match_count].brp = brp;
  260.                     matches[match_count].tseg1 = tseg1 - 1;
  261.                     matches[match_count].tseg2 = tseg2;
  262.                     matches[match_count].sjw = sjw;
  263.                     matches[match_count].br_err = br_err;
  264.                     matches[match_count].sp_err = sp_err;
  265.                     match_count++;
  266.                 }
  267.  
  268.             } else if (sp_err == 0.0F && br_err == 0.0F) {
  269.                 return 1;
  270.             } else {
  271.                 match_count ++;
  272.             }
  273.  
  274.             tseg1++;
  275.             tseg2--;
  276.  
  277.             sample_point = fround((float) tseg1 / (float) btq_rounded);
  278.             sp_err = fround((float) fabs((double) (sample_point - nominal_sample_point)) / nominal_sample_point);
  279.  
  280.         }
  281.     }
  282.     return (int) match_count;
  283. }
  284.  
  285.  
  286. // ************* CODE EXAMPLES **************
  287.  
  288. // this code example returns all found bitrates that match the input values.
  289. // if an identical match is found that is the only one that will get populated.
  290. // If I knew what the oscillator drift % was I could fine tune the script so it
  291. // would return only bitrates that are within that percentage
  292. int count = get_bitrates(
  293.     500000,
  294.     2.0F,
  295.     62.0F,
  296.     10.0F,
  297.     1,
  298.     150,
  299.     NULL,
  300.     0
  301. );
  302.  
  303. if (count > 0) {
  304.     machine_can_bitrate_t bitrates[count]
  305.     if (get_bitrates(500000, 2.0F, 62.0F, 10.0F, 1, 150, bitrates, count) > 0) {
  306.         for (int i = 0;i < count;i++) {
  307.             bitrates[i].bitrate;
  308.             bitrates[i].brp;
  309.             bitrates[i].tseg1;
  310.             bitrates[i].tseg2;
  311.             bitrates[i].sjw;
  312.             bitrates[i].br_err;
  313.             bitrates[i].sp_err;
  314.  
  315.         }
  316.     }
  317. }
  318.  
  319. // this code example returns the closest match to the input values
  320. machine_can_bitrate_t bitrates[1]
  321. if (get_bitrates(500000, 2.0F, 62.0F, 10.0F, 1, 150, bitrates, 1) > 0) {
  322.     bitrates[0].bitrate;
  323.     bitrates[0].brp;
  324.     bitrates[0].tseg1;
  325.     bitrates[0].tseg2;
  326.     bitrates[0].sjw;
  327.     bitrates[0].br_err;
  328.     bitrates[0].sp_err;
  329. }
I did want to mention that this code can be used for every can interface and not just for the ESP32. There are some things that would have to be changed but nothing to complicated and should be in the specification for the interface.


here are the things that would have to be changed.
  1. #define TSEG1_MIN  2
  2. #define TSEG1_MAX  16
  3.  
  4. #define TSEG2_MIN  1
  5. #define TSEG2_MAX  8
  6.  
  7. #define BRP_MIN  2
  8. #define BRP_MAX  TWAI_BRP_MAX
  9. #define BRP_INC  2
  10.  
  11. #define SJW_MAX  4
  12.  
  13. #define FSYS  80000000
  14. #define IS_BRP_VALID  TWAI_BRP_IS_VALID
the IS_BRP_VALID macro is what checks the brp value. This is done because of the ESP32 revision 2 and it having 2 different brp scales. the first one starts at 2 and ends at 128 and has an increment of 2 and the second starts at 132 and goes to 256 and has an increment of 4.

so is the revision is 2 or greater this is the macro
TWAI_BRP_IS_VALID(brp) (((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) || ((brp) >= 132 && (brp) <= 256 && ((brp) & 0x3) == 0))

and on revision 1 this is the macro
TWAI_BRP_IS_VALID(brp) ((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0)

You will always want to set the BRP_MAX to the absolute max and set the BRP_INC to the lowest increment and create the IS_BRP_VALID so it can validate the brp properly.

so if i am using a revision 2 this validates as False IS_BRP_VALID(134) and this validates as True IS_BRP_VALID(136)

FSYS is the oscillator frequency the crystal uses.

all other values that an interface may use is based of the numbers returned by this function. or have been supplied to this function.
The following is pseudo code and would need to me changed to whatever language is being used. If someone needs a port to C/CPP or Python I can do that easily enough, just have to ask.
  1. sample point = ((tseg1 + 1) / (tseg1 + 1 + tseg2)) * 100.0
  2.  
  3. fcan = fsys / brp
  4. tq = 1.0 / fcan
  5.  
  6. bt = 1.0 / bitrate
  7. btq = int(round(bt / tq))
  8.  
  9. bus_delay = (bus_length * 5) * 1e-9
  10. transceiver_delay = transceiver_delay * 1e-9
  11. t_prop_seg = 2 * (transceiver_delay + bus_delay)
  12. prop_seg = -(-t_prop_seg / tq)
  13.  
  14. phase_seg = btq - prop_seg - 1
  15.  
  16. if phase_seg > 3
  17.     prop_seg += phase_seg % 2
  18.  
  19. if phase_seg < 3
  20.     phase_seg1 = 0
  21.     phase_seg2 = 0
  22.  
  23. else if phase_seg == 3
  24.     phase_seg1 = 1
  25.     phase_seg2 = 2
  26. else
  27.     phase_seg1 = phase_seg / 2
  28.     phase_seg2 = phase_seg1
  29.  
  30. BTR0, BTR1 (sja1000) = brp - 1 + (sjw - 1) * 64, tseg1 - 2 + (tseg2 - 1) * 16
  31. CAN0BT (Silabs) = brp + 64 * (sjw - 1) + tseg1 * 256 + tseg2 * 4096
  32. CANBR (Atmel) = phase_seg2 - 1 | (phase_seg1 - 1) << 4 | (prop_seg - 1) << 8 | (sjw - 1) << 12 | (brp - 1) << 16
  33. CANCTRL (Microchip) = (brp - 1) << 24 | (sjw - 1) << 22 | (phase_seg1 - 1) << 19 | (phase_seg2 - 1) << 16 | (prop_seg - 1) << 0
  34. CNF1, CNF2, CNF3 (Microchip) = brp - 1 + (sjw - 1) * 64, prop_seg - 2 + (phase_seg1 - 1) * 8 + 128, tseg2 - 1
  35. CANBTC (Texas Instruments) = (phase_seg2 - 1) & 0x7 | ((phase_seg1 + prop_seg - 1) & 0xF) << 3 | ((sjw - 1) & 0x3) << 8 | ((brp - 1) & 0xFF) << 16
  36. CXCONR (Renesas) = ((brp - 1 + (prop_seg - 1) * 32) + (phase_seg1 - 2 + (_tseg2 - 1) * 8 + (self.sjw - 1) * 64)) * 256
  37. CIBCR (Renesas) = (((phase_seg1 + prop_seg - 1) & 0x0F) << 20 | ((brp - 1) & 0x3FF) << 8 | ((sjw - 1) & 0x3) << 4 | ((phase_seg2 - 1) & 0x07)) << 8
  38.  

Who is online

Users browsing this forum: No registered users and 58 guests