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

Post Reply