본문 바로가기

CAN

3. 초기설정 (4) HAL_CAN_Init 함수

일단 HAL_CAN_Init 함수는 다음과 같이 사용됩니다.

CAN_HandleTypeDef hcan; /* 전역 변수 */

hcan.Instance = CAN; /* CAN = (uint32_t *)0x40006400. CAN 컨트롤러(페리페럴)의 베이스 주소 */
hcan.Init.Prescaler = 1024; /* CAN -> BTR [BRP] 에 입력되기 위한 버퍼 */
hcan.Init.Mode = CAN_MODE_NORMAL; /* CAN -> MCR [INITRQ/SLEEP] 에 입력되기 위한 버퍼 */
hcan.Init.SJW = CAN_SJW_1TQ; /* CAN -> BTR [SJW] 에 입력되기 위한 버퍼 */
hcan.Init.BS1 = CAN_BS1_3TQ; /* CAN -> BTR [BS1] 에 입력되기 위한 버퍼 */
hcan.Init.BS2 = CAN_BS2_5TQ; /* CAN -> BTR [BS2] 에 입력되기 위한 버퍼 */
hcan.Init.TTCM = DISABLE; /* CAN -> MCR [TTCM] 에 1을 쓸지 말지를 결정하기 위한 변수 */
hcan.Init.ABOM = DISABLE; /* CAN -> MCR [ABOM] 에 1을 쓸지 말지를 결정하기 위한 변수 */
hcan.Init.AWUM = DISABLE; /* CAN -> MCR [AWUM] 에 1을 쓸지 말지를 결정하기 위한 변수 */
hcan.Init.NART = ENABLE; /* CAN -> MCR [NART] 에 1을 쓸지 말지를 결정하기 위한 변수 */
hcan.Init.RFLM = DISABLE; /* CAN -> MCR [RFLM] 에 1을 쓸지 말지를 결정하기 위한 변수 */
hcan.Init.TXFP = DISABLE; /* CAN -> MCR [TXFP] 에 1을 쓸지 말지를 결정하기 위한 변수 */
if (HAL_CAN_Init( &hcan ) != HAL_OK) /* HAL_CAN_Init을 성공적으로 수행하면 HAL_OK를 반환하고 if문을 건너뛴다. */
{
    Error_Handler(); /* while(1) */
}

그리고 HAL_CAN_Init 함수 내부입니다. 앞서 설명했듯이 CAN 컨트롤러의 모든 레지스터는 hcan의 Instance 포인터 변수로 접근하는 것을 알 수 있습니다.

HAL_StatusTypeDef HAL_CAN_Init( CAN_HandleTypeDef *hcan )

{
    uint32_t status = CAN_INITSTATUS_FAILED; /* 현재 실행중인 이 함수의 내부 상태 기본적으로 실패상태이며 함수의 내용을 성공적으로 수행했을 경우만 _SUCCESS로 바꾼다. */
    uint32_t tickstart = 0U; /* 컨트롤 비트를 셋하고 대응하는 상태 비트가 올바르게 나타나는지 확인할 때 시간 제한을 두는데 이때 기준 시간를 임시 저장하고 있다. */

    if (hcan->State == HAL_CAN_STATE_RESET) /* 핸들이 리셋 상태라면 */
    {
         hcan->Lock = HAL_UNLOCKED; /* 핸들을 풀어준다. */
         HAL_CAN_MspInit(hcan); /* CAN 컨트롤러가 사용할 GPIO와 NVIC 설정을 하는 함수이지만 설명은 생략하겠습니다. */
    }

     hcan->State = HAL_CAN_STATE_BUSY;
     hcan->Instance->MCR &= (~(uint32_t)CAN_MCR_SLEEP); /* SLEEP 비트를 클리어해서 슬립 모드를 탈출한다. */
     hcan->Instance->MCR |= CAN_MCR_INRQ; /* INRQ를 셋하여 초기화 모드에 진입한다. 주의할 점은 INRQ를 셋한다고 무조건 초기화를 수행할 수 있는 것이 아니라 CAN_MSR의 INAK 비트를 확인하여 CAN 컨트롤러가 초기화 모드에 진입을 완료 했는지 확인하고나서 초기화 과정을 수행할 수 있다. 아래 while 블록은 INAK 비트가 H/W에 의해 셋되길 기다리는 동안 시간제한을 두는 블록이다. */

     tickstart = HAL_GetTick(); /* 일단 비교가 시작되는 시점의 SysTick 값을 가져온다. 값은 중요하지 않다. 이 값이 기준값이 된다는 것에 의미가 있다. */

     /* CAN_MSR의 INAK 비트가 셋 될 동안 아래 블록을 반복한다. tickstart 값은 고정이지만 while문을 돌 때마다 HAL_GetTick 함수는 계속 증가되는 값을 반환한다. 두 값의 차이가 CAN_MSR_INAK 보다 커지면 if문에 진입하고 타임 아웃을 반환하면서 HAL_CAN_Init 함수를 탈출한다. SysTick의 오버플로우에 대한 대비가 없기 때문에 약간 불규칙할 수도 있지만 CAN_MSR_INAK = 10이기 때문에 오버플로우의 영향은 매우 작을 것 같다. */
    while ((hcan->Instance->MSR & CAN_MSR_INAK) != CAN_MSR_INAK)
    {
        if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)
        {
            hcan->State = HAL_CAN_STATE_TIMEOUT;
            __HAL_UNLOCK(hcan);
            return HAL_TIMEOUT;
        }
    }

    /* while 문을 통과했다는 의미는 INAK 비트가 셋되었다는 의미지만 다시 한번 검사한다. */
    if ((hcan->Instance->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)

    {

        /* 핸들의 Init의 내용을 CAN_MCR 레지스터에 입력한다. 앞서 설명했듯이 Instance 포인터 변수를 이용한다. */

        if (hcan->Init.TTCM == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_TTCM;
        }
        else
        {
            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_TTCM;
        }

        if (hcan->Init.ABOM == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_ABOM;
        }
        else
        {
            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_ABOM;
        }

        if (hcan->Init.AWUM == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_AWUM;
        }
        else
        {
            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_AWUM;
        }

        if (hcan->Init.NART == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_NART;
        }
        else
        {
            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_NART;
        }

        if (hcan->Init.RFLM == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_RFLM;
        }
        else
        {
            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_RFLM;
        }

        if (hcan->Init.TXFP == ENABLE)
        {
            hcan->Instance->MCR |= CAN_MCR_TXFP;
        }
        else
        {

            hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_TXFP;

        }

         /* 핸들의 Init의 내용을 CAN_BTR 레지스터에 입력한다. 앞서 설명했듯이 Instance 포인터 변수를 이용한다. 또한 앞서 설명했듯이 CAN_BTR 레지스터는 전송 속도 설정을 하는 레지스터이다. */
        hcan->Instance->BTR = (uint32_t)((uint32_t)hcan->Init.Mode) | \
        ((uint32_t)hcan->Init.SJW) | \
        ((uint32_t)hcan->Init.BS1) | \
        ((uint32_t)hcan->Init.BS2) | \
        ((uint32_t)hcan->Init.Prescaler - 1U);

        hcan->Instance->MCR &= ~(uint32_t)CAN_MCR_INRQ; /* INRQ 비트를 클리어하여 초기화 모드를 탈출한다. INRQ 비트를 셋하여 초기화 모드에 진입할 때와 마찬가지로 INRQ 비트를 클리어 했을 때, CAN_MSR의 INAK도 H/W에 의해 클리어 되는지 확인해야 한다. */

 

        /* 앞선 while 블록과 동일한 원리 */

        tickstart = HAL_GetTick();

 

        while ((hcan->Instance->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)

        {

            if ((HAL_GetTick() - tickstart) > CAN_TIMEOUT_VALUE)

            {            

                hcan->State = HAL_CAN_STATE_TIMEOUT;

                __HAL_UNLOCK(hcan);
                return HAL_TIMEOUT;
            }

        }

     

        /* 여기까지 진입했다면 초기화 과정은 완료된 것이다. 최초 _FAILED로 설정되었던 지역변수 status의 값을 _SUCCESS로 바꿔준다. */

        if ((hcan->Instance->MSR & CAN_MSR_INAK) != CAN_MSR_INAK)

        {

             status = CAN_INITSTATUS_SUCCESS; /* 바로 밑의 if문에서 반환 값을 결정하기 위해서 사용된다. */

        }

    }

 

    if (status == CAN_INITSTATUS_SUCCESS)

    {

        hcan->ErrorCode = HAL_CAN_ERROR_NONE; /* 오류 없음 */

        hcan->State = HAL_CAN_STATE_READY; /* 다른 동작을 할 수 있는 상태로 변경. */

        return HAL_OK; /* HAL_CAN_Init 함수가 포함된 if문을 건너뛸 수 있다. */

    }

    else

    {
        hcan->State = HAL_CAN_STATE_ERROR;
        return HAL_ERROR; /* HAL_CAN_Init 함수가 포함된 if문에 진입하여 무한루프에 빠진다. */
    }
}