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.
pull/6702/head
iabdalkader 2020-12-06 20:28:21 +02:00 zatwierdzone przez Damien George
rodzic a13d1b50c9
commit 20f8ce1982
2 zmienionych plików z 67 dodań i 2 usunięć

Wyświetl plik

@ -49,7 +49,7 @@ Class Methods
Methods Methods
------- -------
.. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False) .. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False, baudrate=0, sample_point=75)
Initialise the CAN bus with the given parameters: Initialise the CAN bus with the given parameters:
@ -67,6 +67,11 @@ Methods
- *auto_restart* sets whether the controller will automatically try and restart - *auto_restart* sets whether the controller will automatically try and restart
communications after entering the bus-off state; if this is disabled then communications after entering the bus-off state; if this is disabled then
: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
calculate a CAN bit-timing (overriding *prescaler*, *bs1* and *bs2*) that satisfies both
the baudrate and the desired *sample_point*.
- *sample_point* given in a percentage of the bit time, the *sample_point* specifies the position
of the last bit sample with respect to the whole bit time. The default *sample_point* is 75%.
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);

Wyświetl plik

@ -140,9 +140,41 @@ STATIC void pyb_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki
} }
} }
STATIC uint32_t pyb_can_get_source_freq() {
uint32_t can_kern_clk = 0;
// Find CAN kernel clock
#if defined(STM32H7)
switch (__HAL_RCC_GET_FDCAN_SOURCE()) {
case RCC_FDCANCLKSOURCE_HSE:
can_kern_clk = HSE_VALUE;
break;
case RCC_FDCANCLKSOURCE_PLL: {
PLL1_ClocksTypeDef pll1_clocks;
HAL_RCCEx_GetPLL1ClockFreq(&pll1_clocks);
can_kern_clk = pll1_clocks.PLL1_Q_Frequency;
break;
}
case RCC_FDCANCLKSOURCE_PLL2: {
PLL2_ClocksTypeDef pll2_clocks;
HAL_RCCEx_GetPLL2ClockFreq(&pll2_clocks);
can_kern_clk = pll2_clocks.PLL2_Q_Frequency;
break;
}
}
#else // F4 and F7 and assume other MCUs too.
// CAN1/CAN2/CAN3 on APB1 use GetPCLK1Freq, alternatively use the following:
// can_kern_clk = ((HSE_VALUE / osc_config.PLL.PLLM ) * osc_config.PLL.PLLN) /
// (osc_config.PLL.PLLQ * clk_init.AHBCLKDivider * clk_init.APB1CLKDivider);
can_kern_clk = HAL_RCC_GetPCLK1Freq();
#endif
return can_kern_clk;
}
// init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8) // init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8)
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) { 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) {
enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart }; enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point };
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} }, { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} },
{ MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, { 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
{ MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} }, { MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
{ MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} }, { MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
{ MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point
}; };
// parse args // 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
// set the CAN configuration values // set the CAN configuration values
memset(&self->can, 0, sizeof(self->can)); memset(&self->can, 0, sizeof(self->can));
// Calculate CAN bit timing from baudrate if provided
if (args[ARG_baudrate].u_int != 0) {
uint32_t baudrate = args[ARG_baudrate].u_int;
uint32_t sampoint = args[ARG_sample_point].u_int;
uint32_t can_kern_clk = pyb_can_get_source_freq();
bool timing_found = false;
// The following max values work on all MCUs for classical CAN.
for (int brp = 1; brp < 512 && !timing_found; brp++) {
for (int bs1 = 1; bs1 < 16 && !timing_found; bs1++) {
for (int bs2 = 1; bs2 < 8 && !timing_found; bs2++) {
if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) &&
((sampoint * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) {
args[ARG_bs1].u_int = bs1;
args[ARG_bs2].u_int = bs2;
args[ARG_prescaler].u_int = brp;
timing_found = true;
}
}
}
}
if (!timing_found) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point"));
}
}
// init CAN (if it fails, it's because the port doesn't exist) // init CAN (if it fails, it's because the port doesn't exist)
if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int, if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int,
args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) { args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) {