kopia lustrzana https://github.com/micropython/micropython
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
rodzic
a13d1b50c9
commit
20f8ce1982
|
@ -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);
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
Ładowanie…
Reference in New Issue