본문 바로가기
임베디드 개발/STM32 (ARM Cortex-M)

STM32 ] CAN Interrupt

by eteo 2023. 1. 31.

 

CAN 통신 송수신 이전글 :

2023.01.29 - [DSP, MCU/STM32 (ARM Cortex-M)] - STM32 ] CAN 송수신

 

STM32 ] CAN 송수신

CAN 통신 설정 이전글 : 2023.01.27 - [DSP, MCU/STM32 (ARM Cortex-M)] - STM32 ] CAN 통신 설정 (bxCAN peripheral) STM32 ] CAN 통신 설정 (bxCAN peripheral) HTML 삽입 미리보기할 수 없는 소스 STM32F4xx 시리즈에는 bxCAN이라고

eteo.tistory.com

 

 

 

bxCAN 의 interrupt source

 

1. Transmit Request Completed

2. Frame is received in RX FIFO0

3. Frame is received in RX FIFO1

4. During CAN status change or Error

 

STM32엔 CAN 모듈이 두개에 있으니 Interrupt Vector table 에는 CAN 과 관련된 총 8 IRQ 가 있다.

 

 

 

 

CAN Interrupt diagram

 

NVIC 엔진으로 가는 4개의 라인이 있다. 각 bxCAN이 이 4종류의 인터럽트를 트리거 할 수 있다.

 

그리고 각각의 라인의 Interrupt cause는 CAN_IER 레지스터에 각 bit에 의해 enable or disable 될 수 있다.

 

 

 

Transmit Interrupt

 

CAN_TSR 레지스터의 RQCPx bit 가 set 된 경우(=Transmit mailbox x 가 비게되는 경우)

 

 

 

 

 

FIFO0 & FIFO1 Interrupt

 

1. CAN_RFxR 레지스터의 FMPx bit 가 '00'이 아닌 경우.(= 새로운 메시지를 수신한 경우) 해당 bit 은 0-3 값을 가질 수 있고 소프트웨어에서 FIFO로부터 메시지를 읽어가면 줄어듬

2. CAN_RFxR 레지스터의 FULLx bit 가 set 된 경우(=FIFO에 메세지 3개가 꽉차 pending상태인 경우)

3. CAN_RFxR  레지스터의 FOVRx bit 가 set 된 경우(=FIFO가 Full인 상태에서 다른 메시지가 수신되어 Overrun 이 일어난 경우)

 

 

 

 

 

 

 

 

Status Change Error Interrupt

 

 

 

 

 

CAN_ESR 레지스터에서 다음의 Error Condition 이 감지되었을 때 Interrupt 가 트리거 될 수 있다.

 

 

이 중 중요한 것은 BOFF bit 이다. TEC(Transmit Error Counter) 값이 255를 넘어 overflow 되면 bus-off 상태에 들어가고 해당 bit 가 set 된다. 그리고 bus-off 상태가 되면 bxCAN 은 더이상 송수신이 불가해진다.

 

bxCAN에는 CAN 마스터 컨트롤 레지스터에서 ABOM bit 를 설정하면 자동 bus-off recovery 기능을 enable 할 수 있는데 해당 기능을 사용하지 않는 경우 bus-off 가 감지되었을 때 device 를 reset 해 주는 역할을 소프트웨어에서 해줄 필요도 있다.

 

 

 

이 외에도 Sleep mode 상태에서 Frame 의 SOF bit 를 감지하면 Wakeup interrupt 를 트리거 할 수 있고, bxCAN 이 Sleep mode 로 들어갈 때도 interrupt 를 트리거하도록 할 수 있다.

 

 

 

 

 

 

 

Interrupt 구현

 

다음과 같이 CubeMX에서 Interrupt 를 Enable 하고 Code Generation 을 누르면

 

 

 

 

can.c 의 HAL_CAN_MspInit() 함수 안에 다음과 같은 코드가 추가 된다.

 

    /* CAN1 interrupt Init */
    HAL_NVIC_SetPriority(CAN1_TX_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
    HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
    HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn);
  /* USER CODE BEGIN CAN1_MspInit 1 */

 

 

HAL_NVIC_SetPriority() 함수의 두번째 파라미터인 PreemptPriority 값은 낮을수록 높은 우선순위를 갖는다.

 

 

그리고 startup_.s 파일에 가면 IRQHandler name 이 추가되고,

 

 

 

stm32f4xx_it.c 파일에 가면 아래와 같이 IRQHandler 함수가 자동생성 되었다.

 

void CAN1_TX_IRQHandler(void)
{
  HAL_CAN_IRQHandler(&hcan1);
}

void CAN1_RX0_IRQHandler(void)
{
  HAL_CAN_IRQHandler(&hcan1);
}

void CAN1_RX1_IRQHandler(void)
{
  HAL_CAN_IRQHandler(&hcan1);
}

void CAN1_SCE_IRQHandler(void)
{
  HAL_CAN_IRQHandler(&hcan1);
}

 

 

HAL_CAN_IRQHandler() 함수에선 조건에 맞는 콜백함수를 호출하는데 stm32f4xx_hal_can.c 에 weak 로 정의되어 있다.

 

/* Callbacks functions ********************************************************/
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan);

 

 

 

 

Interrupt Enable 함수

 

다이어그램을 보면 알 수 있듯이 각 Interrupt Source 의 CAN_IER 레지스터 bit 를 Enable 해주어야 하는데 아래 함수를 통해 할 수 있다.

 

HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs)

 

두번째 파라미터 들어갈 수 있는 Interrupt Cause 는 stm32f4xx_hal_can.h 에 정의되어 있다.

 

/** @defgroup CAN_Interrupts CAN Interrupts
  * @{
  */
/* Transmit Interrupt */
#define CAN_IT_TX_MAILBOX_EMPTY     ((uint32_t)CAN_IER_TMEIE)   /*!< Transmit mailbox empty interrupt */

/* Receive Interrupts */
#define CAN_IT_RX_FIFO0_MSG_PENDING ((uint32_t)CAN_IER_FMPIE0)  /*!< FIFO 0 message pending interrupt */
#define CAN_IT_RX_FIFO0_FULL        ((uint32_t)CAN_IER_FFIE0)   /*!< FIFO 0 full interrupt            */
#define CAN_IT_RX_FIFO0_OVERRUN     ((uint32_t)CAN_IER_FOVIE0)  /*!< FIFO 0 overrun interrupt         */
#define CAN_IT_RX_FIFO1_MSG_PENDING ((uint32_t)CAN_IER_FMPIE1)  /*!< FIFO 1 message pending interrupt */
#define CAN_IT_RX_FIFO1_FULL        ((uint32_t)CAN_IER_FFIE1)   /*!< FIFO 1 full interrupt            */
#define CAN_IT_RX_FIFO1_OVERRUN     ((uint32_t)CAN_IER_FOVIE1)  /*!< FIFO 1 overrun interrupt         */

/* Operating Mode Interrupts */
#define CAN_IT_WAKEUP               ((uint32_t)CAN_IER_WKUIE)   /*!< Wake-up interrupt                */
#define CAN_IT_SLEEP_ACK            ((uint32_t)CAN_IER_SLKIE)   /*!< Sleep acknowledge interrupt      */

/* Error Interrupts */
#define CAN_IT_ERROR_WARNING        ((uint32_t)CAN_IER_EWGIE)   /*!< Error warning interrupt          */
#define CAN_IT_ERROR_PASSIVE        ((uint32_t)CAN_IER_EPVIE)   /*!< Error passive interrupt          */
#define CAN_IT_BUSOFF               ((uint32_t)CAN_IER_BOFIE)   /*!< Bus-off interrupt                */
#define CAN_IT_LAST_ERROR_CODE      ((uint32_t)CAN_IER_LECIE)   /*!< Last error code interrupt        */
#define CAN_IT_ERROR                ((uint32_t)CAN_IER_ERRIE)   /*!< Error Interrupt                  */

 

 

 

송수신 및 에러 인터럽트 사용 예시

 

  /* USER CODE BEGIN 2 */
  CAN_Filter_Config();
  
  HAL_CAN_ActivateNotification(&hcan1, CAN_IT_TX_MAILBOX_EMPTY | CAN_IT_RX_FIFO0_MSG_PENDING | 
  					CAN_IT_BUSOFF);
  
  HAL_CAN_Start(&hcan1);

  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  
  }

 

 

/* USER CODE BEGIN 4 */
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
{
  char msg[50];
  sprintf(msg, "Message Transmitted: M0\r\n");
  HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}

void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
{
  char msg[50];
  sprintf(msg, "Message Transmitted: M1\r\n");
  HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}

void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)
{
  char msg[50];
  sprintf(msg, "Message Transmitted: M2\r\n");
  HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  CAN_RxHeaderTypeDef RxHeader;

  uint8_t rcvd_msg[8];

  char msg[50];

  if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, rcvd_msg) != HAL_OK)
  {
    Error_Handler();
  }

  sprintf(msg, "Message Received : %s\r\n", rcvd_msg);

  HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);

  CAN1_tx();
}

void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
  char msg[50];
  sprintf(msg, "CAN Error Detected\r\n");
  HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}

/* USER CODE END 4 */

 

 

 

 

 

Reference : Fastbit Embedded Brain Academy