Custom board with STM32H743 - problems with CAN

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
Post Reply
User avatar
WZab
Posts: 6
Joined: Tue Jan 21, 2020 7:45 pm

Custom board with STM32H743 - problems with CAN

Post by WZab » Wed Jan 22, 2020 2:16 pm

I try to get CAN working on the custom board https://github.com/elhep/MCORD_HUB/tree/master/Hub_PCB based on NUCLEO_H743ZI.
I have compiled and uploaded the MicroPython built for Nucleo (with one modification described in viewtopic.php?f=12&t=7637 ).
However, when testing CAN, I have found that the base clock for CAN is 8 MHz instead of 100MHz, even though the pyb.freq reports:

Code: Select all

>>> pyb.freq()
(400000000, 200000000, 100000000, 100000000)
To get CAN working at 100kb/s I had to set the following parameters:

Code: Select all

can.init(pyb.CAN.NORMAL,extframe=False,prescaler=8,sjw=1,bs1=7,bs2=2)
Another problem was with defining the filters.
When I tried to set the filter as described in documentation ( http://docs.micropython.org/en/v1.12/li ... b.CAN.html ), I get the following error:

Code: Select all

>>> can.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'CAN' has no attribute 'LIST16'
OK. I have checked the sources and found that the `CAN.LIST16` constant is not defined, but I have also found its definition in `can.h`:

Code: Select all

#define LIST16 (1)
So I have modified the command, but it still produced an error:

Code: Select all

>>> can.setfilter(0, 1, 0, (123, 124, 125, 126))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: CAN filter parameter error
I have searched for "CAN filter parameter error" in the sources and found it in the `pyb_can.c` source in the `pyb_can_setfilter` function.
Analysis of that function have shown, that the above filter definition does not work for FDCAN controller available in STM32H7 chips:

Code: Select all

    #if MICROPY_HW_ENABLE_FDCAN
    FDCAN_FilterTypeDef filter = {0};
    filter.IdType = FDCAN_STANDARD_ID;
    // TODO check filter index
    filter.FilterIndex = args[ARG_bank].u_int;

    // Check filter mode
    if (((args[ARG_mode].u_int != FDCAN_FILTER_RANGE) &&
         (args[ARG_mode].u_int != FDCAN_FILTER_DUAL)  &&
         (args[ARG_mode].u_int != FDCAN_FILTER_MASK))) {
        goto error;
    }
Therefore, I had to find the above constants in the `stm32h7xx_hal_fdcan.h` file in `stm32libs1` and learn how to build the filter definition.

Code: Select all

#define FDCAN_FILTER_RANGE         ((uint32_t)0x00000000U) /*!< Range filter from FilterID1 to FilterID2                        */
#define FDCAN_FILTER_DUAL          ((uint32_t)0x00000001U) /*!< Dual ID filter for FilterID1 or FilterID2                       */
#define FDCAN_FILTER_MASK          ((uint32_t)0x00000002U) /*!< Classic filter: FilterID1 = filter, FilterID2 = mask            */
#define FDCAN_FILTER_RANGE_NO_EIDM ((uint32_t)0x00000003U) /*!< Range filter from FilterID1 to FilterID2, EIDM mask not applied */
Finally, I was able to create a working filter for messages with ID between 0x100 and 0x120:

Code: Select all

>>> can.setfilter(0, 0, 0, (0x100,0x120))
>>> 
So the problem is solved, but I think it would be good to describe in the documentation the differences between CAN and FDCAN operation.
Yet another remark. It seems that `rxcallback` method doesn't work. I had to poll for received messages:

Code: Select all

while True:
    if can.any(0):
      print(can.recv(0))
With best regards,
Wojtek

User avatar
WZab
Posts: 6
Joined: Tue Jan 21, 2020 7:45 pm

Re: Custom board with STM32H743 - problems with CAN

Post by WZab » Wed Jan 22, 2020 6:45 pm

Regarding the CAN clock frequency problem, it seems that MicroPython should call `LL_RCC_SetFDCANClockSource` with the proper PLL (which one?) before calling ` __HAL_RCC_FDCAN_CLK_ENABLE();` in the `fdcan.c`.

Code: Select all

/**
  * @brief  Configure FDCANx Kernel clock source
  * @rmtoll D2CCIP1R        FDCANSEL        LL_RCC_SetFDCANClockSource
  * @param  ClkSource This parameter can be one of the following values:
  *         @arg @ref LL_RCC_FDCAN_CLKSOURCE_HSE
  *         @arg @ref LL_RCC_FDCAN_CLKSOURCE_PLL1Q
  *         @arg @ref LL_RCC_FDCAN_CLKSOURCE_PLL2Q
  * @retval None
  */
__STATIC_INLINE void LL_RCC_SetFDCANClockSource(uint32_t ClkSource)
{
  MODIFY_REG(RCC->D2CCIP1R, RCC_D2CCIP1R_FDCANSEL, ClkSource);
}
Unfortunately, at the moment I can't verify if it helps.
Regards,
Wojtek

skpang
Posts: 2
Joined: Mon Feb 24, 2020 9:16 am

Re: Custom board with STM32H743 - problems with CAN

Post by skpang » Sun Mar 01, 2020 7:31 pm

I've seen the same problem based on the Nucleo H743ZI port.

I've connected a 12MHz external crystal to the board and managed to fixed the bitrate problem.

In mpconfigboard.h I used:
#define MICROPY_HW_CLK_PLLM (1)
#define MICROPY_HW_CLK_PLLN (80)
#define MICROPY_HW_CLK_PLLP (2)
#define MICROPY_HW_CLK_PLLQ (2)
#define MICROPY_HW_CLK_PLLR (2)

#define MICROPY_HW_CLK_PLL2M (1)
#define MICROPY_HW_CLK_PLL2N (20)
#define MICROPY_HW_CLK_PLL2P (2)
#define MICROPY_HW_CLK_PLL2Q (3)
#define MICROPY_HW_CLK_PLL2R (2)

In stm32h7xx_hal_conf.h
#define HSE_VALUE (12000000)

Then to select the right PLL to use for CAN.
system_stm32.c
Change:
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB
to
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB|RCC_PERIPHCLK_FDCAN;

Then add:
PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;

//PLL2
PeriphClkInitStruct.PLL2.PLL2M = MICROPY_HW_CLK_PLL2M;
PeriphClkInitStruct.PLL2.PLL2N = MICROPY_HW_CLK_PLL2N;
PeriphClkInitStruct.PLL2.PLL2P = MICROPY_HW_CLK_PLL2P;
PeriphClkInitStruct.PLL2.PLL2Q = MICROPY_HW_CLK_PLL2Q;
PeriphClkInitStruct.PLL2.PLL2R = MICROPY_HW_CLK_PLL2R;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_1;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;

Now this will give you the right bitrate.
can = CAN(1, CAN.NORMAL, extframe=False, prescaler=1, sjw=1, bs1=119, bs2=40)
This will give you 500kbps

//1000k
can = CAN(1, CAN.NORMAL, extframe=False, prescaler=1, sjw=1, bs1=63, bs2=16)

The code for the fdcan.c is not complete. You can't set the bitrate for FD data rate and no setting for the BRS flag.
Hope someone will add this. Tried to do this myself but it is a bit beyond my understand of python.

Post Reply