본문 바로가기

CAN

5. 데이터 송신 (인터럽트 방식) (5) HAL_CAN_IRQHandler와 CAN_Transmit_IT 함수

전송이 완료되면 인터럽트가 발생하면서 CAN_ISR인 HAL_CAN_IRQHandler에 진입하게 됩니다. 송신이 완료되었을 때 뿐만 아니라 수신이 완료된 경우, 오류가 발생한 경우에도 HAL_CAN_IRQHandler에 진입하게 됩니다. 송신과 오류에 의한 진입의 경우에도 인터럽트 소스와 이밴트 상태를 검사하는 방법은 송신과 동일합니다. 여기서는 송신에 대해서만 자세히 알아보고 나머지는 주석처리했습니다.

void HAL_CAN_IRQHandler( CAN_HandleTypeDef *hcan )
{
    /* 송신 완료에 의해 CAN_ISR 진입. */

    /* CAN_IER 레지스터의 TMEIE 비트가 셋되어 있다면 */
    if (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME))
    {
        /* 어떤 메일박스의 전송이 완료되었는지 검사하기 위해 CAN_TSR 레지스터의 RQCPx 비트들을 검사한다. */
        if ( (__HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_0)) ||
            (__HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_1)) ||
            (__HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_2)) )
        {
            /* HAL_CAN_IRQHandler 함수 다음에 설명 */
            CAN_Transmit_IT(hcan);
        }
    }

    /* RX FIFO 0에 대한 수신 완료에 의해 CAN_ISR 진입 */
    /* CAN_IER 레지스터의 FMPIE0 비트가 1로 셋되어 있고, CAN_RF0R 레지스터의 FMP 필드가 0이 아닌 경우 */
    if ( (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP0)) &&
        (__HAL_CAN_MSG_PENDING(hcan, CAN_FIFO0) != 0) )
    {
        /* 생략 */
        CAN_Receive_IT(hcan, CAN_FIFO0);
    }

    /* RX FIFO 1에 대한 수신 완료에 의해 CAN_ISR 진입 */
    /* CAN_IER 레지스터의 FMPIE0 비트가 1로 셋되어 있고, CAN_RF0R 레지스터의 FMP 필드가 0이 아닌 경우 */
    if ( (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP1)) &&
        (__HAL_CAN_MSG_PENDING(hcan, CAN_FIFO1) != 0) )
    {
        /* 생략 */
        CAN_Receive_IT(hcan, CAN_FIFO1);
    }

     /* 참고로 CAN_ESR 레지스터에 있는 모든 오류 플래그는 직접 인터럽트 소스가 되지 못한다. CAN_MSR 레지스터의 ERRI 비트가 인터럽트 소스가 되고, 각 오류 플래그를 ERRI 비트에 연결시키는 방법을 사용해야 한다.*/

    /* 수신 또는 송신 오류 카운터가 96 이상이어서 CAN_ISR 진입 */

    /* CAN_IER 레지스터의 EWGIE 비트가 셋되어 있고,(CAN_ESR 레지스터의 EWGF 비트가 CAN_MSR 레지스터의 ERRI 비트와 연결되어 있다면) CAN_IER 레지스터의 ERRIE 비트가 셋되어 있고(ERRI 비트를 인터럽트 소스로 설정했다면), CAN_ESR 레지스터의 EWGF 비트가 셋된 경우. */

    if ( (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EWG)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EWG)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR)) )
    {
        /* 핸들의 자세한 오류 내용 기입. 마지막 if 문에서 이용한다. */
        hcan->ErrorCode |= HAL_CAN_ERROR_EWG;
    }    

    /* 수신 또는 송신 오류 카운터가 96 이상이어서 CAN_ISR 진입 */
    if ( (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EPV)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EPV)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR)) )
    {
        hcan->ErrorCode |= HAL_CAN_ERROR_EPV;
    }

    /* 수신 또는 송신 오류 카운터가 255 이상이어서 CAN_ISR 진입 */
    if ( (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_BOF)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_BOF)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR)) )
    {
        hcan->ErrorCode |= HAL_CAN_ERROR_BOF;
    }

    /* CAN 컨트롤러가 프레임에서 오류를 검출하여 CAN_ISR 진입 */
    /* 단, LEC는 여러 개의 오류가 검출되도 마지막에 검출된 오류만 나타낸다. */
    if ( (!HAL_IS_BIT_CLR(hcan->Instance->ESR, CAN_ESR_LEC)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_LEC)) &&
        (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR)) )
    {
        switch (hcan->Instance->ESR & CAN_ESR_LEC)
        {
            case(CAN_ESR_LEC_0):
            hcan->ErrorCode |= HAL_CAN_ERROR_STF;
            break;

            case(CAN_ESR_LEC_1):*/
            hcan->ErrorCode |= HAL_CAN_ERROR_FOR;
            break;

            case(CAN_ESR_LEC_1 | CAN_ESR_LEC_0):
            hcan->ErrorCode |= HAL_CAN_ERROR_ACK;
            break;

            case(CAN_ESR_LEC_2):
            hcan->ErrorCode |= HAL_CAN_ERROR_BR;
            break;

            case(CAN_ESR_LEC_2 | CAN_ESR_LEC_0):
            hcan->ErrorCode |= HAL_CAN_ERROR_BD;
            break;

            case(CAN_ESR_LEC_2 | CAN_ESR_LEC_1):
            hcan->ErrorCode |= HAL_CAN_ERROR_CRC;
            break;

            default:
            break;
        } 

        /* 이미 정보는 얻었으므로 CAN_ESR 레지스터의 LEC 필드를 클리어한다. */
        hcan->Instance->ESR &= ~(CAN_ESR_LEC);
    }

    if (hcan->ErrorCode != HAL_CAN_ERROR_NONE) /* 오류가 있다면, */
    {
        hcan->Instance->MSR |= CAN_MSR_ERRI; /* 1을 쓰면 클리어되는 비트이다. */
        hcan->State = HAL_CAN_STATE_READY; /* 다시 전송을 시작하기 위해서 */
        HAL_CAN_ErrorCallback(hcan); /* 비어 있는 함수 */
    }
}
static HAL_StatusTypeDef CAN_Transmit_IT( CAN_HandleTypeDef *hcan )
{
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_TME); /* CAN_IER 레지스터의 TMEIE 비트를 클리어 */

    if (hcan->State == HAL_CAN_STATE_BUSY_TX)
    {
        /* 모든 오류에 관한 인터럽트 연결/소스를 클리어 한다. */
        __HAL_CAN_DISABLE_IT(hcan, CAN_IT_EWG);
        __HAL_CAN_DISABLE_IT(hcan, CAN_IT_EPV);
        __HAL_CAN_DISABLE_IT(hcan, CAN_IT_BOF);
        __HAL_CAN_DISABLE_IT(hcan, CAN_IT_LEC);
        __HAL_CAN_DISABLE_IT(hcan, CAN_IT_ERR);
    }

    if (hcan->State == HAL_CAN_STATE_BUSY_TX_RX) /* 핸들의 상태가 송수신 상태라면, */
    {
        /* 송신은 끝났으므로 수신만 하는 상태로 바꿔준다. */
        hcan->State = HAL_CAN_STATE_BUSY_RX;
    }
    else /* 그렇지 않다면, */
    {
        hcan->State = HAL_CAN_STATE_READY; /* 준비사태로 바꿔준다. */
    }

    HAL_CAN_TxCpltCallback(hcan); /* 비어 있다. */
    return HAL_OK;
}