본문으로 바로가기

CAN Interrupts

category 카테고리 없음 2025. 3. 1. 20:37

Understanding bxCAN Interrupt requests (IRQs)

bxCAN 인터럽트가 발생하는 경우

  1. 송신 이벤트(Tx Events)
    • 메시지 전송 요청이 완료될 때 (Mailbox 0, 1, 2)
  2. 수신 이벤트(Rx Events)
    • RX FIFO 0에 새로운 프레임이 수신될 때
    • RX FIFO 1에 새로운 프레임이 수신될 때
  3. 상태 변화(Status Change Events)
    • CAN 버스 상태 변화 (Bus-off, 오류 발생 등)
  4. 오류 발생(Error Events)
    • 수신/송신 오류 증가
    • FIFO 오버런(overrun) 발생
    • Bus-off 상태로 전환

 

bxCAN 인터럽트 벡터 개수

  • bxCAN1: 4개의 인터럽트 벡터(IRQ)
  • bxCAN2: 4개의 인터럽트 벡터(IRQ)
  • 총 8개의 IRQ가 CAN1 및 CAN2에 할당됨

 

bxCAN 인터럽트 발생 원인 및 처리 방식

① 송신 인터럽트 (Transmit Interrupt)

  • Mailbox 0, 1, 2에서 메시지 전송 완료 시 발생
  • 관련 레지스터: CAN_TSR (Transmit Status Register)
  • RQCP0 (Request Completed for Mailbox 0) 비트가 1이 되면 인터럽트 발생

② RX FIFO 0 인터럽트 (Receive FIFO 0 Interrupt)

  • FIFO 0에 새로운 메시지가 들어오면 발생
  • 관련 레지스터: CAN_RF0R (Receive FIFO 0 Register)
  • FMP0 (FIFO Message Pending) 비트가 1 이상이면 인터럽트 발생
  • FIFO가 가득 차거나(FULL0 비트) 오버런 발생(FOVR0 비트) 시 인터럽트 발생

③ RX FIFO 1 인터럽트 (Receive FIFO 1 Interrupt)

  • RX FIFO 0과 동일한 방식으로 동작 (FIFO 1 사용)

④ 상태 변화 및 오류 인터럽트 (Status Change & Error Interrupt)

  • ESR (Error Status Register)에 의해 감지됨
  • 주요 원인:
    • EWGF (Error Warning Flag): 오류 카운터가 일정 값(96) 이상 증가하면 경고 발생
    • BOFF (Bus-off 상태): 전송 오류(TEC)가 255를 초과하면 노드가 Bus-off 상태가 되어 통신 불가
    • WKUI (Wakeup Interrupt): Sleep Mode에서 Start of Frame(SOF) 감지 시 발생

 

Bus-off 상태 및 복구

  • Bus-off란?
    • 송신 오류(TEC)가 255를 초과하면 노드가 Bus-off 상태로 전환
    • Bus-off 상태에서는 송·수신이 불가능하며, CAN 버스로부터 차단
  • Bus-off 복구 방법
    1. 자동 복구 (Auto Bus-Off Management, ABOM) 활성화
      • ABOM 비트를 1로 설정하면 자동 복구 가능
      • 128개의 연속적인 Recessive(1) 비트를 감지하면 자동 복구됨
    2. 소프트웨어를 통한 복구
      • CAN_Init()을 다시 호출하여 CAN 컨트롤러를 초기화 모드 → 정상 모드로 전환

 

WKUI - Wakeup Interrupt)

  • CAN 컨트롤러가 Sleep Mode에 있을 때, Start of Frame(SOF)이 감지되면 발생
  • CAN RX 엔진이 활성화되어 있어야 감지

IRQs Exercise

 

IRQ 활성화 (msp.c)

void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
	GPIO_InitTypeDef gpio_can1;
	__HAL_RCC_CAN1_CLK_ENABLE();

	gpio_can1.Pin = GPIO_PIN_11 | GPIO_PIN_12;
	gpio_can1.Mode = GPIO_MODE_AF_PP;
	gpio_can1.Pull = GPIO_NOPULL;
	gpio_can1.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	gpio_can1.Alternate = GPIO_AF9_CAN1;
	HAL_GPIO_Init(GPIOA, &gpio_can1);

	HAL_NVIC_SetPendingIRQ(CAN1_TX_IRQn, 15, 0);
	HAL_NVIC_SetPendingIRQ(CAN1_RX0_IRQn, 15, 0);
	HAL_NVIC_SetPendingIRQ(CAN1_RX1_IRQn, 15, 0);
	HAL_NVIC_SetPendingIRQ(CAN1_SCE_IRQn, 15, 0);

	HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
	HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
	HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
	HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn);
}

 

인터럽트 서비스 루틴(ISR) 구현 (it.c)

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);
}

 

Callback function Implementation

CAN 하드웨어에서 인터럽트를 활성화하려면 메인함수에서 activate notification API 호출

HAL_CAN_ActivateNotification(&hcan1, CAN_IT_TX_MAILBOX_EMPTY | CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_BUSOFF)

 

Callback 함수를 모두 구현하고, 인터럽트 모드이므로 Rx 함수는 제거

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

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

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

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	CAN_RxHeaderTypeDef RxHeader;
	uint8_t message[5];
	char msg[50];

	//while(HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0));
	if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, message)!= HAL_OK) Error_handler();

	sprintf(msg, "Message Received %s\r\n", message);
	HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
}

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