본문 바로가기

CAN

6. 데이터 수신 (풀링 방식) (2) CanRxMsgTypeDef 구조체 (3) HAL_CAN_Receive 함수

(2) CanRxMsgTypeDef 구조체


CanTxMsgTypeDef 구조체와 비슷한 기능을 합니다. RX 메일박스 레지스터들과 대응되며 RX 메일박스에 있는 가장 최근에 수신된 CAN 프레임에 대한 정보를 임시 저장하는 구조체입니다. CAN_HandleTypeDef 구조체는 CanTxMsgTypeDef형 포인터 변수만 가지고 있기 때문에 CanTxMsgTypeDef 구조체와 마찬가지로 사용자가 CanRxMsgTypeDef형의 변수를 만들어서 CAN_HandleTypeDef형 핸들에 직접 연결시켜주어야 합니다. RX 메일 박스에서 가져온 내용을 임시 저장하는 변수이기 때문에 각 맴버 변수들은 RX 메일박스의 필드들과 거의 100% 대응됩니다.


(3) HAL_CAN_Receive 함수


HAL_StatusTypeDef HAL_CAN_Receive( CAN_HandleTypeDef *hcan, uint8_t FIFONumber, uint32_t Timeout )
{
    uint32_t tickstart = 0;
    __HAL_LOCK(hcan); /* 핸들을 잠근다. */

    if (hcan->State == HAL_CAN_STATE_BUSY_TX) /* 핸들이 송신 상태였다면, */
    {
        hcan->State = HAL_CAN_STATE_BUSY_TX_RX; /* 핸들을 송수신 상태로 바꾼다. */
    }
    else /* 핸들이 송신 상태가 아니라면, */
    {
        hcan->State = HAL_CAN_STATE_BUSY_RX; /* 핸들을 송신 상태로 바꾼다. */
    }

    tickstart = HAL_GetTick(); /* 현재 SysTick의 틱 값을 가져온다. 다음에 나오는 while문의 타임아웃을 위한 기준 틱이다. */

    /* CAN_RFxR 레지스터의 FMP 필드가 0인동안 반복하지만, 타임아웃 */
    while (__HAL_CAN_MSG_PENDING(hcan, FIFONumber) == 0U)
    {
        /* 입력 변수의 Timeout 값이 HAL_MAX_DELAY(0xFFFFFFFF)가 아니라면, HAL_MAX_DELAY라면 무한정 기다린다. */
        if (Timeout != HAL_MAX_DELAY)
        {
            /* while문 진입 후 SysTick의 틱 값을 다시 얻어와 tickstart 값을 빼서 경과한 시간을 계산한다. 이때 경과시간이 Timeout보다 크면 탈출한다. */
           if ((Timeout == 0U) || ((HAL_GetTick() - tickstart) > Timeout))
           {
                /* 핸들을 풀어주고, 타임아웃 상태로 만든 뒤 탈출. */
                hcan->State = HAL_CAN_STATE_TIMEOUT;
                __HAL_UNLOCK(hcan);
                return HAL_TIMEOUT;
           }
        }
    }
    /* while문을 탈출했다는 것은 메시지가 수신되었다는 의미이다. RX 메일박스에서 메시지 임시 저장용도로 사용되는 CanTxMsgTypeDef형 변수로 데이터를 복사한다.*/

    /* CAN_RIxR 레지스터에서 비트2만 남기고 모두 지우고, IDE 맴버 변수에 복사한다. */
    hcan->pRxMsg->IDE = (uint8_t)0x04U & hcan->Instance->sFIFOMailBox[FIFONumber].RIR;
    if (hcan->pRxMsg->IDE == CAN_ID_STD) /* 표준 식별자를 사용한 프레임이라면, */
    {
        /* CAN_RIxR 레지스터에서 하위 11비트만 남기고 모두 지우고, StdId 맴버 변수에 복사한다. */
        hcan->pRxMsg->StdId = 0x000007FFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RIR >> 21U);
    }
    else /* 확장 식별자를 사용한 프레임이라면, */
    {
        /* CAN_RIxR 레지스터에서 하위 29비트만 남기고 모두 지우고, StdId 맴버 변수에 복사한다. */
        hcan->pRxMsg->ExtId = 0x1FFFFFFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RIR >> 3U);
    }

    /* CAN_RIxR 레지스터에서 비트2만 남기고 모두 지우고, RTR 맴버 변수에 복사한다. */
    hcan->pRxMsg->RTR = (uint8_t)0x02U & hcan->Instance->sFIFOMailBox[FIFONumber].RIR;

    /* CAN_RDTxR 레지스터에서 하위 4비트만 남기고 모두 지우고, DLC맴버 변수에 복사한다. */
    hcan->pRxMsg->DLC = (uint8_t)0x0FU & hcan->Instance->sFIFOMailBox[FIFONumber].RDTR;

    /* CAN_RDTxR 레지스터에서 비트8부터 비트15까지의 값을 FIM 맴버 변수에 복사한다. */
    hcan->pRxMsg->FMI = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDTR >> 8U);

    /* CAN_RDL/HR 레지스터에서 데이터 값을 Data 맴버 변수에 복사한다.*/
    hcan->pRxMsg->Data[0] = (uint8_t)0xFFU & hcan->Instance->sFIFOMailBox[FIFONumber].RDLR;
    hcan->pRxMsg->Data[1] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 8U);
    hcan->pRxMsg->Data[2] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 16U);
    hcan->pRxMsg->Data[3] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 24U);
    hcan->pRxMsg->Data[4] = (uint8_t)0xFFU & hcan->Instance->sFIFOMailBox[FIFONumber].RDHR;
    hcan->pRxMsg->Data[5] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 8U);
    hcan->pRxMsg->Data[6] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 16U);
    hcan->pRxMsg->Data[7] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 24U);

    /* RX FIFO를 풀어준다. 릴리즈(release) 해 준다. */
    if (FIFONumber == CAN_FIFO0) /* RX FIFO 0을 선택했다면, */
    {
        /* CAN_RF0R 레지스터에서 RFOM0 비트에 1을 쓴다. */
        __HAL_CAN_FIFO_RELEASE(hcan, CAN_FIFO0);
    }
    else /* RX FIFO 1을 선택했다면, */
    {
        /* CAN_RF1R 레지스터에서 RFOM1 비트에 1을 쓴다. */
        __HAL_CAN_FIFO_RELEASE(hcan, CAN_FIFO1);
    }

    if (hcan->State == HAL_CAN_STATE_BUSY_TX_RX) /* 현재 핸들의 상태가 송수신 상태라면, */
    {
        /* 수신은 완료했으므로 다시 송신 상태로 바꿔준다.*/
        hcan->State = HAL_CAN_STATE_BUSY_TX;
    }
    else /* 현재 핸들의 상태가 송수신 상태가 아니라면, */
    {
        hcan->State = HAL_CAN_STATE_READY; /* 준비 상태로 바꿔준다. */
    }

    __HAL_UNLOCK(hcan); /* 핸들을 풀어준다. */

    return HAL_OK;
}