본문 바로가기

CAN

3. 초기설정 (3) CAN_HandleTypeDef 구조체

HAL 함수를 분석하기 전에 CAN 컨트롤러를 관리하는 CAN_HandleTypeDef 구조체를 살펴보겠습니다.

typedef struct
{
    CAN_TypeDef* Instance;
    CAN_InitTypeDef Init;
    CanTxMsgTypeDef* pTxMsg;
    CanRxMsgTypeDef* pRxMsg;
    HAL_LockTypeDef Lock;
    __IO HAL_CAN_StateTypeDef State;
    __IO uint32_t ErrorCode;
}CAN_HandleTypeDef;

일단 구조체는 위와 같습니다만, 구조체 안에 또다른 사용자 정의 자료형이 있어 복잡해 보입니다. 구조체의 내용을 직관적으로 파악하기 위해 그림을 그려봤습니다. HAL드라이버에서 컨트롤+F로 ‘자료형;’을 검색하면 더 자세하게 알 수 있습니다.

stm32f can flow memory map

위 그림에서 CAN_TypeDef 구조체의 맴버 변수들의 offset은 CAN 페리페럴의 레지스터들과 대응하는 offset을 가지고 있습니다. 따라서 CAN_TypeDef의 포인터 변수은 Instance에 CAN(0x40006400)을 넣으면 이 주소를 베이스 주소로 CAN_TypeDef 구조체의 맴버 변수를 이용해 CAN 페리페럴의 모든 레지스터들을 편하게 접근할 수 있습니다. 여기서는 이정도로만 설명하겠습니다. Init 구조체는 초기 설정값들을 가지고 있습니다. Init 구조체 안에 있는 초기 설정값은 Instance 주소를 베이스 주소로 원하는 레지스터의 오프셋만큼 이동하여 그 레지스터에 설정값을 입력합니다. 그리고 송수신 파트에서 더 자세히 설명하겠지만 pTxMsg와 pRxMsg 포인터 변수가 TX, RX 메일박스에 대응되는 CanTxMsgTypeDef, CanRxMsgTypeDef 구조체를 가리키고 있습니다. 송신할 데이터는 pTxMsg가 가리키고 있는 CanTxMsgTypeDef 구조체에 데이터를 먼저 채워 넣고 TX 메일박스에 다시 입력하게 됩니다. 그리고 필터를 거쳐서 수신된 데이터는 RX 메일박스에서 CanRxMsgTypeDef 구조체로 복사해온 후에 사용자 어플리케이션에서 처리하게 됩니다. 이 과정을 보면 CAN의 모든 과정이 CAN_HandleTypeDef 구조체 하나에 통합되어 있는 것을 볼 수 있습니다. 이러한 구조체 또는 변수를 ‘핸들’이라고 합니다. 각 레지스터에 대응되는 자료형을 직접 만들어 사용할 수도 있겠지만, 그렇게 되면 변수가 많아져 코드가 길어질수록 관리하기 힘들어질 것 같습니다. CAN_HandleTypeDef 구조체와 같이 하나로 통합하면, 데이터 관리뿐만 아니라 디버깅에도 유용합니다. Lock 변수는 핸들의 잠김 상태를 나타냅니다. RTOS 등을 사용하면 여러 개의 테스크가 동일한 핸들을 사용할 수 있는데 이때 핸들 안에 자신의 잠김 상태를 나타내는 변수가 있다면 서로 다른 테스크가 배타적으로 핸들에 접근할 수 있게 됩니다. 마치 ‘세마포어’같군요. ‘세마포어’는 어떤 데이터에 접근할 수 있는 배타적인 권한을 의미하는데 대부분의 RTOS에서 제공하고 있는 기능입니다. 다음에 기회가 되면 FreeRTOS를 사용하여 RTOS에 대해서도 블로그해 볼 예정입니다. State 변수는 핸들이 현재 어떤 용도로 사용되고 있는지 또는 어떤 상태인지를 나타냅니다. ErrorCode는 State가 오류 상태일 때의 자세한 오류 내용을 나타냅니다.