(3) CanTxMsgTypeDef 구조체
TX 메일박스 레지스터들과 대응되며 TX 메일박스에 들어갈 CAN 프레임에 대한 정보를 임시 저장하는 구조체입니다. CAN_HandleTypeDef 구조체는 CanTxMsgTypeDef형 포인터 변수만 가지고 있기 때문에 사용자가 CanTxMsgTypeDef형의 변수를 만들어서 CAN_HandleTypeDef형 핸들에 직접 연결시켜주어야 합니다. TX 메일 박스에 들어갈 내용을 임시 저장하는 변수이기 때문에 각 맴버 변수들은 TX 메일박스의 필드들과 거의 100% 대응됩니다.
(4) HAL_CAN_Transmit_IT 함수
HAL_CAN_Transmit_IT 함수는 다음과 같이 사용됩니다.
/* 전송할 데이터 입력 */
hcan.pTxMsg->Data[3] = 0xAA;
hcan.pTxMsg->Data[2] = 0xBB;
hcan.pTxMsg->Data[1] = 0xCC;
hcan.pTxMsg->Data[0] = 0xDD;
hcan.pTxMsg->DLC = 4; /* 전송할 데이터의 길이 */
hcan.pTxMsg->ExtId = 0x1FFFFFE1; /* 표준 식별자 + 확장 식별자 */
hcan.pTxMsg->IDE = CAN_ID_EXT; /* CAN_ID_EXT = 1 */
hcan.pTxMsg->RTR = CAN_RTR_DATA; /* CAN_RTR_DATA = 0 */
hcan.pTxMsg->StdId = 0x00000000; /* 확장 식별자를 사용할 경우에 사용되지 않을 변수지만 0으로 클리어해 둔다. */
if (HAL_CAN_Transmit_IT( &hcan ) != HAL_OK)
{
Error_Handler(); /* while(1) */
}
HAL_CAN_Transmit_IT 함수는hcan.pTxMsg의 내용을 대응하는 TX 메일박스 필드에 복사하고, 전송이 완료된 경우 CAN_TSR 레지스터의 RQCPx 비트가 셋 되는데 이때 인터럽트가 발생하도록 하기 위해서 CAN_IER 레지스터의 TMEIE 비트를 셋합니다. 마지막으로 CAN 컨트롤러에게 전송을 요청하기 위해CAN_TIxR의 TXRQ 비트를 셋합니다. 그럼 CAN 컨트롤러는 알아서 전송을 시작하고, 전송이 완료되면 CAN_TSR 레지스터의 RQCPx 비트가 셋되면서 인터럽트가 발생합니다. 그런데 CAN_TSR 레지스터에 보면 TME 필드가 있습니다. TME는 Transmit Mailbox Empty의 약자로 CAN_IER 레지스터의 TMEIE (Transmit Mailbox Empty Interrupt Enable)와 대응하는 것처럼 보이지만 그렇지 않습니다. TME는 리셋했을 때도 1입니다. 리셋했을 때는 TX 메일박스에 아무것도 없을 것이므로 당연합니다. 만약 TME를 인터럽트 소스로 설정하면 CAN_IER의 TMEIE를 1로 셋하자마자 전송이 완료되기도 전에 인터럽트가 발생하게 될 것입니다. 이것을 방지하기 위해 STM32측에서는 TMEIE의 인터럽트 소스를 TME가 아닌 RQCPx로 설정했습니다. 전송 요청후 전송이 완료되어 RQCPx(x=0~2) 중 하나라도1로 셋되면 인터럽트가 발생하여 CAN_ISR로 진입하게 됩니다. 참고로 CAN_ISR은 CEC_ISR과 동일한 주소를 갖습니다. 그럼 이제부터 HAL_CAN_Transmit_IT 함수를 분석해보도록 하겠습니다. HAL_CAN_Transmit_IT 함수 안에서 사용된 함수는 그 기능만 주석에 달아놨습니다. 그렇게 복잡하지 않은 함수들입니다.
HAL_StatusTypeDef HAL_CAN_Transmit_IT( CAN_HandleTypeDef *hcan)
{
/* 사용할 TX 메일박스 번호를 저장하는 변수이다. 0부터 2까지의 값을 가질 수 있고, CAN_TXSTATUS_NOMAILBOX = 4이다.*/
uint32_t transmitmailbox = CAN_TXSTATUS_NOMAILBOX;
/* CAN_TSR 레지스터에서 TX 메일박스 0부터 비어 있는 메일박스를 선택한다. */
if ( ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) || \
((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) || \
((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2) )
{
/* 핸들을 잠근다. */
__HAL_LOCK(hcan);
/* 만약 TX 메일박스 0이 비어 있다면, */
if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
{
transmitmailbox = 0U;
}
/* 만약 TX 메일박스 1이 비어 있다면, */
else if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
{
transmitmailbox = 1U;
}
/* 만약 TX 메일박스 2가 비어 있다면, */
else
{
transmitmailbox = 2U;
}
/* CAN_TypeDef형의 포인터 변수인 Instance에 대해서는 위에서 설명. 비어 있는 TX 메일박스의 TIxR 레지스터의 TXRQ 비트를 1과 AND 연산. 어차피 메일 박스가 비게 되면 H/W가 클리어하고 비어 있기 때문에 접근했을 텐데 필요 있을까 싶다. 만약 이 비트를 무조건 0으로 만들기 위한 것이라면 ‘&=’이 아닌 ‘&= ~’ 이 와야 하는 것 아닐까? */
hcan->Instance->sTxMailBox[transmitmailbox].TIR &= CAN_TI0R_TXRQ;
if (hcan->pTxMsg->IDE == CAN_ID_STD) /* 표준 식별자를 선택했다면 */
{
/* HAL_CAN_Transmit_IT 함수를 호출하기 전에 설정했던 변수들을 TIxR 레지스터의 각 필드에 적절히 넣어준다. 이때 CanTxMsgTypeDef 구조체의 ExtId는 사용되지 않는다. */
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->StdId << 21U) | \
hcan->pTxMsg->RTR);
}
else /* 확장 식별자를 선택했다면 */
{
/* 마찬가지. 이때 CanTxMsgTypeDef 구조체의 ExtId는 사용되지 않는다.*/
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->ExtId << 3U) | \
hcan->pTxMsg->IDE | \
hcan->pTxMsg->RTR);
}
/* DLC 필드 설정 */
hcan->pTxMsg->DLC &= (uint8_t)0x0000000FU; /* 비트0~비트3만 남긴다. */
/* TX 메일바긋의 TDTxR 레지스터의 DLC 필드를 모두 0으로 만든다. */
hcan->Instance->sTxMailBox[transmitmailbox].TDTR &= 0xFFFFFFF0U;
/* TX 메일바긋의 TDTxR 레지스터의 DLC 필드에 HAL_CAN_Transmit_IT 함수를 호출하기 전에 설정했던 값을 넣어준다. */
hcan->Instance->sTxMailBox[transmitmailbox].TDTR |= hcan->pTxMsg->DLC;
/* 데이터 필드를 채운다. */
hcan->Instance->sTxMailBox[transmitmailbox].TDLR = (((uint32_t)hcan->pTxMsg->Data[3] << 24U) |
((uint32_t)hcan->pTxMsg->Data[2] << 16U) |
((uint32_t)hcan->pTxMsg->Data[1] << 8U) |
((uint32_t)hcan->pTxMsg->Data[0]));
hcan->Instance->sTxMailBox[transmitmailbox].TDHR = (((uint32_t)hcan->pTxMsg->Data[7] << 24U) |
((uint32_t)hcan->pTxMsg->Data[6] << 16U) |
((uint32_t)hcan->pTxMsg->Data[5] << 8U) |
((uint32_t)hcan->pTxMsg->Data[4]));
if (hcan->State == HAL_CAN_STATE_BUSY_RX) /* 핸들이 현재 송신 상태라면 */
{
/* 송수신 상태로 바꿔준다. */
hcan->State = HAL_CAN_STATE_BUSY_TX_RX;
}
else /* 핸들이 현재 송신 상태가 아니라면 */
{
/* 송신 상태로 바꿔준다. */
hcan->State = HAL_CAN_STATE_BUSY_TX;
}
hcan->ErrorCode = HAL_CAN_ERROR_NONE; /* 여기까지 완료되면 오류 없음.*/
__HAL_UNLOCK(hcan); /* 핸들을 풀어준다. */
/* RQCPx에 대한 인터럽트 뿐만 아니라 각종 오류에 대한 인터럽트도 활성화 시킨다. 이에 대한 자세한 내용은 레퍼런스 매뉴얼 확인. */
__HAL_CAN_ENABLE_IT( hcan, CAN_IT_EWG );
__HAL_CAN_ENABLE_IT( hcan, CAN_IT_EPV );
__HAL_CAN_ENABLE_IT( hcan, CAN_IT_BOF );
__HAL_CAN_ENABLE_IT( hcan, CAN_IT_LEC );
__HAL_CAN_ENABLE_IT( hcan, CAN_IT_ERR );
/* RQCPx에 대한 인터럽트를 활성화시킨다. 즉, CAN_IER 레지스터의 TMEIE 비트를 셋한다. */
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_TME);
/* Request transmission */
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ;
}
else /* 3개의 TX 메일박스 중 어떤 것도 비어 있지 않다면, */
{
/* 핸들을 오류상태로 만들고 탈출한다 */
hcan->State = HAL_CAN_STATE_ERROR;
return HAL_ERROR;
}
return HAL_OK;
}
'CAN' 카테고리의 다른 글
5. 데이터 송신 (6) 오실로스코프로 데이터 프레임 파형 관찰 (7) 기타 의문점.. (0) | 2017.03.22 |
---|---|
5. 데이터 송신 (인터럽트 방식) (5) HAL_CAN_IRQHandler와 CAN_Transmit_IT 함수 (0) | 2017.03.22 |
5. 데이터 송신 (1) 데이터 프레임 (2) 리모트 프레임 (2) | 2017.03.21 |
4. 필터설정 (6) CAN_FilterConfTypeDef 구조체와 HAL_CAN_ConfigFilter 함수 (0) | 2017.03.21 |
4. 필터설정 (2)~(5) 필터 종류 (0) | 2017.03.21 |