STM32 FDCAN 필터 설정

STM32F4 CAN 필터 설정을 정리한 포스트가 있는데, 이 글에서는 FDCAN 필터 설정이 기존 CAN과 약간 차이가 있어 정리해둔다.

 

STM32 CAN 32비트 멀티 필터 설정

J1939 프로토콜의 CAN 데이터를 수신하여 처리하려면 CAN 통신속도 250kbps, 샘플포인트 87%로 설정해야 한다. 내부클럭(HSI)을 사용할 경우 MCU의 상태에 따라 클럭이 틀어져서 CAN 데이터를 수신 못하는

memories.tistory.com

CAN FD 설명은 아래 포스트를 참고하면 기존 CAN과 차이점을 자세히 알 수 있다. 간단히 설명하면 CAN 버스 네트워크에 높은 부하을 줄이기 위해 Bosch에서 새롭게 발표한 CAN 프로토콜이며, 데이터 길이를 8바이트에서 64바이트로 확장하고, 최대 전송 속도를 15Mbit/s까지 허용하며, CRC 필드를 추가하여 데이터 무결성을 강화한 프로토콜이다.

 

CAN FD 통신이란?

차량 통신 네트워크 기술은 차량 내 전자제어시스템 (ECU) 간의 정보를 전달하기 위해 개발되었습니다. ...

blog.naver.com

프로토콜의 세부사항까지 보면 좋겠지만, 그 부분은 칩셋 제조사가 대부분 포팅한 드라이버를 제공하기 때문에 우리는 제공하는 드라이버 및 API에 대한 사용법을 익히면 되기 때문에, 이 글에서는 STM32 FDCAN 설정 방법에 대해 간략히 정리한다.

 

STM32 FDCAN Init 과정은 아래와 같으며, 기존 CAN 2.0x를 사용할 경우 프레임 형식을 FDCAN_FRAME_CLASSIC으로 설정해야 한다. CAN FD을 사용할 경우는 FDCAN_FRAME_FD_NO_BRS 또는 FDCAN_FRAME_FD_BRS를 사용한다.

  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = DISABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = DISABLE;
  hfdcan1.Init.NominalPrescaler = 2;
  hfdcan1.Init.NominalSyncJumpWidth = 1;
  hfdcan1.Init.NominalTimeSeg1 = 13;
  hfdcan1.Init.NominalTimeSeg2 = 2;
  hfdcan1.Init.DataPrescaler = 1;
  hfdcan1.Init.DataSyncJumpWidth = 1;
  hfdcan1.Init.DataTimeSeg1 = 1;
  hfdcan1.Init.DataTimeSeg2 = 1;
  hfdcan1.Init.StdFiltersNbr = 1;
  hfdcan1.Init.ExtFiltersNbr = 2;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }

기존 STM32F4 CAN 2.0B 설정과 동일하게 CAN 비트 타이밍을 Prescaler, TimeSeq1, TimeSeq2로 설정해야 한다. 위 설정은 전송 속도 500kbsp, 샘플 포인트는 87.5%로 설정한 것이다.

 

그리고 StdFiltersNbr과 ExtFiltersNbr은 필터 개수를 미리 정의한 것이며, Standard 필터는 1개, Extended 필터는 2개로 설정되어 있다. 필터 개수는 각각 최대 8개까지 가능하다.

 

CAN ID에 대한 필터를 설정하기 위해 HAL_FDCAN_ConfigFilter() 함수를 이용하며, 필터 설정의 예시는 아래와 같다.

  FDCAN_FilterTypeDef sFilterConfig1;
  sFilterConfig1.IdType = FDCAN_EXTENDED_ID;
  sFilterConfig1.FilterIndex = 0;
  sFilterConfig1.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig1.FilterID1 = 0x18010000;
  sFilterConfig1.FilterID2 = 0x18010006;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1) != HAL_OK) {
    Error_Handler();
  }

  if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK) {
    Error_Handler();
  }

위 필터는 0x1801000 ~ 0x18010006 범위의 필터를 RXFIFO0에 저장하고, 그 이외의 매치되지 않는 CAN ID를 Reject하여 버리도록 설정한 것이다.

 

STM32 필터 설정 방법은 4가지를 제공하고 있으며, 각 설정에 따른 설명은 아래와 같으며, 주로 사용할 필터 타입은 RANGE와 MASK 모드를 사용하지만, 사용 환경에 따라 필터 타입을 결정하면 될 것이다.

#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 */

위 필터 설정으로 HW 필터를 사용하기 때문에 CPU load에 영향을 미치지 않고, 불필요한 CAN ID를 받지 않을 수 있다.

 

STM32 FDCAN에서 수신된 데이터를 받기 위해 인터럽트를 사용할 수 있지만, 간단히 폴링방식으로 아래와 같이 코드를 작성하면 CAN에서 수신되는 데이터를 읽을 수 있다.

  uint8_t data[8] = {0, };
  FDCAN_RxHeaderTypeDef RxHeader;
  while (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) > 0) {
    if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, data) != HAL_OK) {
      printf("ERROR:: HAL_FDCAN_GetRxMessage() : ErrCode = 0x%x\r\n", hfdcan1.ErrorCode);
      continue;
    }
    recv_message(RxHeader.Identifier);
  }

위 필터 설정에서 매치되는 CAN ID는 RXFIFO0 저장하도록 설정하였기 때문에, RX_FIFO0 버퍼에 데이터가 존재할 경우 읽도록 작성한 것이며, RX_FIFO0 버퍼가 오버플로우 되는 것을 방지하기 위해 수신된 모든 데이터를 읽도록 처리하였다.

 

마지막으로 STM32 FDCAN에서 데이터를 송신하는 방법은 아래와 같이 작성할 수 있다.

  FDCAN_TxHeaderTypeDef TxHeader;

  TxHeader.Identifier = can_id;
  TxHeader.IdType = FDCAN_EXTENDED_ID;
  TxHeader.TxFrameType = FDCAN_DATA_FRAME;
  TxHeader.DataLength = FDCAN_DLC_BYTES_8;
  TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
  TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
  TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  TxHeader.MessageMarker = 0x0;

  HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, data);
  if (status == HAL_OK) {
    return ERROR_NONE;
  }

보내는 데이터 역시 TxFIFO에 저장하여 보내기 때문에 연속해서 데이터를 보낼 경우 TxFIFO 버퍼가 오버플로우가 발생할 수 있으며, 각 메시지 전송사이에 1ms 마진이 필요하다.