Skip to content

Commit 20f8ce1

Browse files
iabdalkaderdpgeorge
authored andcommitted
stm32/pyb_can: Add ability to calculate CAN bit timing from baudrate.
Calculate the bit timing from baudrate if provided, allowing sample point override. This makes it a lot easier to make CAN work between different MCUs with different clocks, prescalers etc. Tested on F4, F7 and H7 Y/V variants.
1 parent a13d1b5 commit 20f8ce1

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

docs/library/pyb.CAN.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Class Methods
4949
Methods
5050
-------
5151

52-
.. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False)
52+
.. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False, baudrate=0, sample_point=75)
5353

5454
Initialise the CAN bus with the given parameters:
5555

@@ -67,6 +67,11 @@ Methods
6767
- *auto_restart* sets whether the controller will automatically try and restart
6868
communications after entering the bus-off state; if this is disabled then
6969
:meth:`~CAN.restart()` can be used to leave the bus-off state
70+
- *baudrate* if a baudrate other than 0 is provided, this function will try to automatically
71+
calculate a CAN bit-timing (overriding *prescaler*, *bs1* and *bs2*) that satisfies both
72+
the baudrate and the desired *sample_point*.
73+
- *sample_point* given in a percentage of the bit time, the *sample_point* specifies the position
74+
of the last bit sample with respect to the whole bit time. The default *sample_point* is 75%.
7075

7176
The time quanta tq is the basic unit of time for the CAN bus. tq is the CAN
7277
prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1);

ports/stm32/pyb_can.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,41 @@ STATIC void pyb_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki
140140
}
141141
}
142142

143+
STATIC uint32_t pyb_can_get_source_freq() {
144+
uint32_t can_kern_clk = 0;
145+
146+
// Find CAN kernel clock
147+
#if defined(STM32H7)
148+
switch (__HAL_RCC_GET_FDCAN_SOURCE()) {
149+
case RCC_FDCANCLKSOURCE_HSE:
150+
can_kern_clk = HSE_VALUE;
151+
break;
152+
case RCC_FDCANCLKSOURCE_PLL: {
153+
PLL1_ClocksTypeDef pll1_clocks;
154+
HAL_RCCEx_GetPLL1ClockFreq(&pll1_clocks);
155+
can_kern_clk = pll1_clocks.PLL1_Q_Frequency;
156+
break;
157+
}
158+
case RCC_FDCANCLKSOURCE_PLL2: {
159+
PLL2_ClocksTypeDef pll2_clocks;
160+
HAL_RCCEx_GetPLL2ClockFreq(&pll2_clocks);
161+
can_kern_clk = pll2_clocks.PLL2_Q_Frequency;
162+
break;
163+
}
164+
}
165+
#else // F4 and F7 and assume other MCUs too.
166+
// CAN1/CAN2/CAN3 on APB1 use GetPCLK1Freq, alternatively use the following:
167+
// can_kern_clk = ((HSE_VALUE / osc_config.PLL.PLLM ) * osc_config.PLL.PLLN) /
168+
// (osc_config.PLL.PLLQ * clk_init.AHBCLKDivider * clk_init.APB1CLKDivider);
169+
can_kern_clk = HAL_RCC_GetPCLK1Freq();
170+
#endif
171+
172+
return can_kern_clk;
173+
}
174+
143175
// init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8)
144176
STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
145-
enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart };
177+
enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point };
146178
static const mp_arg_t allowed_args[] = {
147179
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} },
148180
{ MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} },
@@ -151,6 +183,8 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp
151183
{ MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
152184
{ MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
153185
{ MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
186+
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
187+
{ MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point
154188
};
155189

156190
// parse args
@@ -162,6 +196,32 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp
162196
// set the CAN configuration values
163197
memset(&self->can, 0, sizeof(self->can));
164198

199+
// Calculate CAN bit timing from baudrate if provided
200+
if (args[ARG_baudrate].u_int != 0) {
201+
uint32_t baudrate = args[ARG_baudrate].u_int;
202+
uint32_t sampoint = args[ARG_sample_point].u_int;
203+
uint32_t can_kern_clk = pyb_can_get_source_freq();
204+
bool timing_found = false;
205+
206+
// The following max values work on all MCUs for classical CAN.
207+
for (int brp = 1; brp < 512 && !timing_found; brp++) {
208+
for (int bs1 = 1; bs1 < 16 && !timing_found; bs1++) {
209+
for (int bs2 = 1; bs2 < 8 && !timing_found; bs2++) {
210+
if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) &&
211+
((sampoint * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) {
212+
args[ARG_bs1].u_int = bs1;
213+
args[ARG_bs2].u_int = bs2;
214+
args[ARG_prescaler].u_int = brp;
215+
timing_found = true;
216+
}
217+
}
218+
}
219+
}
220+
if (!timing_found) {
221+
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point"));
222+
}
223+
}
224+
165225
// init CAN (if it fails, it's because the port doesn't exist)
166226
if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int,
167227
args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) {

0 commit comments

Comments
 (0)