stm32/pyb_can: Make pyb.CAN baud calculation a little more forgiving.

Not every baudrate or sample point combination has an exact match,
but getting within 1% on sample point and .1% on baud rate should
always be good enough.

Because the search goes from shorter bit periods (lowest brp) and
increases, the first match which meets this criteria should still mostly be
the best available.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
Angus Gratton 2025-01-08 12:07:41 +11:00 committed by Damien George
parent 1d8943ac7b
commit e8d3df51dc
2 changed files with 24 additions and 9 deletions

View file

@ -67,11 +67,17 @@ Methods
:meth:`~CAN.restart()` can be used to leave the bus-off state :meth:`~CAN.restart()` can be used to leave the bus-off state
- *baudrate* if a baudrate other than 0 is provided, this function will try to automatically - *baudrate* if a baudrate other than 0 is provided, this function will try to automatically
calculate the CAN nominal bit time (overriding *prescaler*, *bs1* and *bs2*) that satisfies calculate the CAN nominal bit time (overriding *prescaler*, *bs1* and *bs2*) that satisfies
both the baudrate and the desired *sample_point*. both the *baudrate* (within .1%) and the desired *sample_point* (to the nearest 1%). For more precise
- *sample_point* given in a percentage of the nominal bit time, the *sample_point* specifies the position control over the CAN timing, set the *prescaler*, *bs1* and *bs2* parameters directly.
of the bit sample with respect to the whole nominal bit time. The default *sample_point* is 75%. - *sample_point* specifies the position of the bit sample with respect to the whole nominal bit time,
expressed as an integer percentage of the nominal bit time. The default *sample_point* is 75%.
This parameter is ignored unless *baudrate* is set.
- *num_filter_banks* for classic CAN, this is the number of banks that will be assigned to CAN(1), - *num_filter_banks* for classic CAN, this is the number of banks that will be assigned to CAN(1),
the rest of the 28 are assigned to CAN(2). the rest of the 28 are assigned to CAN(2).
The remaining parameters are only present on boards with CAN FD support, and configure the optional CAN FD
Bit Rate Switch (BRS) feature:
- *brs_prescaler* is the value by which the CAN FD input clock is divided to generate the - *brs_prescaler* is the value by which the CAN FD input clock is divided to generate the
data bit time quanta. The prescaler can be a value between 1 and 32 inclusive. data bit time quanta. The prescaler can be a value between 1 and 32 inclusive.
- *brs_sjw* is the resynchronisation jump width in units of time quanta for data bits; - *brs_sjw* is the resynchronisation jump width in units of time quanta for data bits;
@ -82,10 +88,11 @@ Methods
it can be a value between 1 and 16 inclusive it can be a value between 1 and 16 inclusive
- *brs_baudrate* if a baudrate other than 0 is provided, this function will try to automatically - *brs_baudrate* if a baudrate other than 0 is provided, this function will try to automatically
calculate the CAN data bit time (overriding *brs_prescaler*, *brs_bs1* and *brs_bs2*) that satisfies calculate the CAN data bit time (overriding *brs_prescaler*, *brs_bs1* and *brs_bs2*) that satisfies
both the baudrate and the desired *brs_sample_point*. both the *brs_baudrate* (within .1%) and the desired *brs_sample_point* (to the nearest 1%). For more
- *brs_sample_point* given in a percentage of the data bit time, the *brs_sample_point* specifies the position precise control over the BRS timing, set the *brs_prescaler*, *brs_bs1* and *brs_bs2* parameters directly.
of the bit sample with respect to the whole data bit time. The default *brs_sample_point* is 75%. - *brs_sample_point* specifies the position of the bit sample with respect to the whole nominal bit time,
expressed as an integer percentage of the nominal bit time. The default *brs_sample_point* is 75%.
This parameter is ignored unless *brs_baudrate* is set.
The time quanta tq is the basic unit of time for the CAN bus. tq is the CAN The time quanta tq is the basic unit of time for the CAN bus. tq is the CAN
prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1); prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1);

View file

@ -25,6 +25,7 @@
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "py/objarray.h" #include "py/objarray.h"
#include "py/runtime.h" #include "py/runtime.h"
@ -199,12 +200,19 @@ static void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point,
uint32_t max_brp, uint32_t max_bs1, uint32_t max_bs2, uint32_t min_tseg, uint32_t max_brp, uint32_t max_bs1, uint32_t max_bs2, uint32_t min_tseg,
mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) { mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) {
uint32_t can_kern_clk = pyb_can_get_source_freq(); uint32_t can_kern_clk = pyb_can_get_source_freq();
mp_uint_t max_baud_error = baudrate / 1000; // Allow .1% deviation
const mp_uint_t MAX_SAMPLE_ERROR = 5; // round to nearest 1%, which is the param resolution
sample_point *= 10;
// Calculate CAN bit timing. // Calculate CAN bit timing.
for (uint32_t brp = 1; brp < max_brp; brp++) { for (uint32_t brp = 1; brp < max_brp; brp++) {
for (uint32_t bs1 = min_tseg; bs1 < max_bs1; bs1++) { for (uint32_t bs1 = min_tseg; bs1 < max_bs1; bs1++) {
for (uint32_t bs2 = min_tseg; bs2 < max_bs2; bs2++) { for (uint32_t bs2 = min_tseg; bs2 < max_bs2; bs2++) {
if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) && mp_int_t calc_baud = can_kern_clk / (brp * (1 + bs1 + bs2));
((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) { mp_int_t calc_sample = ((1 + bs1) * 1000) / (1 + bs1 + bs2);
mp_int_t baud_err = baudrate - calc_baud;
mp_int_t sample_err = sample_point - calc_sample;
if (abs(baud_err) < max_baud_error &&
abs(sample_err) < MAX_SAMPLE_ERROR) {
*bs1_out = bs1; *bs1_out = bs1;
*bs2_out = bs2; *bs2_out = bs2;
*prescaler_out = brp; *prescaler_out = brp;