summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2025-01-08 12:07:41 +1100
committerDamien George <damien@micropython.org>2025-01-17 12:13:17 +1100
commite8d3df51dc036337803fbe047fff77266b3b69f4 (patch)
treea290b1cb5b9092f16d02eccee479d9957b8e2c1c
parent1d8943ac7b57b89efa5400d966957e6613a7604d (diff)
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>
-rw-r--r--docs/library/pyb.CAN.rst21
-rw-r--r--ports/stm32/pyb_can.c12
2 files changed, 24 insertions, 9 deletions
diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst
index 57a85d54b..eb21d8223 100644
--- a/docs/library/pyb.CAN.rst
+++ b/docs/library/pyb.CAN.rst
@@ -67,11 +67,17 @@ Methods
: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
calculate the CAN nominal bit time (overriding *prescaler*, *bs1* and *bs2*) that satisfies
- both the baudrate and the desired *sample_point*.
- - *sample_point* given in a percentage of the nominal bit time, the *sample_point* specifies the position
- of the bit sample with respect to the whole nominal bit time. The default *sample_point* is 75%.
+ both the *baudrate* (within .1%) and the desired *sample_point* (to the nearest 1%). For more precise
+ control over the CAN timing, set the *prescaler*, *bs1* and *bs2* parameters directly.
+ - *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),
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
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;
@@ -82,10 +88,11 @@ Methods
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
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*.
- - *brs_sample_point* given in a percentage of the data bit time, the *brs_sample_point* specifies the position
- of the bit sample with respect to the whole data bit time. The default *brs_sample_point* is 75%.
-
+ both the *brs_baudrate* (within .1%) and the desired *brs_sample_point* (to the nearest 1%). For more
+ precise control over the BRS timing, set the *brs_prescaler*, *brs_bs1* and *brs_bs2* parameters directly.
+ - *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
prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1);
diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c
index dc05f965d..723ff6f9d 100644
--- a/ports/stm32/pyb_can.c
+++ b/ports/stm32/pyb_can.c
@@ -25,6 +25,7 @@
*/
#include <string.h>
+#include <stdlib.h>
#include "py/objarray.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,
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();
+ 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.
for (uint32_t brp = 1; brp < max_brp; brp++) {
for (uint32_t bs1 = min_tseg; bs1 < max_bs1; bs1++) {
for (uint32_t bs2 = min_tseg; bs2 < max_bs2; bs2++) {
- if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) &&
- ((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) {
+ mp_int_t calc_baud = can_kern_clk / (brp * (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;
*bs2_out = bs2;
*prescaler_out = brp;