리눅스 네트워크 프로그래밍 - CAN(Controller Area Network) 통신

반응형

 

리눅스 네트워크 프로그래밍 - CAN(Controller Area Network) 통신

본 글에서는 차량 내부에서 사용되는 CAN(Controller Area Network) 통신을 수행하는 리눅스 어플리케이션 프로그램을 작성하는 방법을 설명한다.

 

환경은 다음과 같다.

  • CAN 인터페이스에 대한 디바이스 드라이버 등은 BSP 레벨에서 이미 포팅이 되어 있다.
  • CAN 네트워크에서 장치 간 공통으로 사용되는 baudrate 등 타이밍 관련 설정은 완료되어 있다.
  • 어플리케이션 프로그램은 C 언어로 작성된다.

 

CAN 인터페이스 초기화

C 언어로 작성된 어플리케이션 프로그램 상에서 CAN 인터페이스를 초기화하는 절차는 다음과 같다.

  • socket() 함수로 CAN 인터페이스에 대한 소켓 파일 디스크립터를 연다
  • ioctl() 함수의 SIOCGIFINDEX 명령을 통해 CAN 인터페이스의 인터페이스 식별번호를 가져온다.
  • bind() 함수로 CAN 인터페이스를 바인드한다.

 

CAN 프레임 전송

C 언어로 작성된 어플리케이션 프로그램 상에서 CAN 프레임을 전송하는 절차는 다음과 같다.

  • struct can_frame 구조체에 전송 데이터를 채운다.
  • write() 함수로 struct can_frame 구조체를 전달하여 프레임을 전송한다.

 

CAN 프레임 수신

C 언어로 작성된 어플리케이션 프로그램 상에서 CAN 프레임을 수신하는 절차는 다음과 같다.

  • read() 함수로 데이터를 수신한다 → struct can_frame 구조체 포인터를 파라미터로 전달하여 데이터를 받아 온다.
  • struct can_frame 구조체 내의 정보에 따라 수신 프레임을 처리한다.

여기서 read() 함수는 기본적으로 블로킹 함수이므로 프로그램 구조에 따라 수신 쓰레드를 별도로 생성하여 read() 함수를 호출하거나, CAN 소켓 속성을 논블로킹으로 변경 후 호출해야 할 수도 있다.

 

 

프로그램 예제 코드

CAN 인터페이스를 초기화하고, CAN 프레임을 전송 및 수신하는 어플리케이션 프로그램의 예제 코드는 다음과 같다.

참고로 아래 코드를 빌드하기 위해 별도의 라이브러리를 링크할 필요는 없다.

#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>


/**
 * @brief CAN 인터페이스를 초기화한다.
 * @param[in] ifname CAN 인터페이스 이름
 * @retval 양수: CAN 소켓 디스크립터
 * @retval -1: 실패
 */  
int InitCanInterface(const char *ifname)
{
  /* 
   * CAN 소켓을 생성한다.
   */
  int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
  if (sock == -1) {
  printf("Fail to create can socket for %s - %m\n", ifname);
    return -1;
  }
  printf("Success to create can socket for %s\n", ifname);

  /* 
   * CAN 인터페이스 식별번호를 획득한다.
   */
  struct ifreq ifr;
  strcpy(ifr.ifr_name, ifname);
  int ret = ioctl(sock, SIOCGIFINDEX, &ifr);
  if (ret == -1) {
    perror("Fail to get can interface index -");
    return -1;
  }
  printf("Success to get can interface index: %d\n", ifr.ifr_ifindex);

  /*
   * CAN 소켓을 바인딩한다.
   */
  struct sockaddr_can addr;
  addr.can_family = AF_CAN;
  addr.can_ifindex = ifr.ifr_ifindex;
  ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
  if (ret == -1) {
    perror("Fail to bind can socket -");
    return -1;
  }
  printf("Success to bind can socket\n");

  return sock;
}

/**
 * @brief CAN 프레임을 전송한다.
 * @param[in] sock CAN 소켓 디스크립터
 * @param[in] id CAN id (11비트 또는 29비트 길이)
 * @param[in] data 전송할 CAN 프레임 데이터
 * @param[in] data_len 전송할 CAN 프레임 데이터의 길이
 * @retval 0: 성공
 * @retval -1: 실패
 */ 
int TransmitCanFrame(const int sock, const uint32_t id, const uint8_t *data, const size_t data_len)
{
  /*
   * 전송할 CAN 프레임을 설정한다.
   */
  struct can_frame frame;
  frame.can_id = id & 0x1fffffff;
  frame.can_id |= (1 << 31);
  memcpy(frame.data, data, data_len);
  frame.can_dlc = data_len;

  /*
   * 전송한다.
   */
  int tx_bytes = write(sock, &frame, sizeof(frame));
  if (tx_bytes == -1) {
    perror("Fail to transmit can frame -");
    return -1;
  } 
  printf("Success to transmit can frame - %d bytes is transmitted\n", tx_bytes);
  return 0;
}


/// CAN 프레임 최대 길이
#define CAN_FRAME_MAX_LEN 8 

/**
 * @brief CAN 프레임을 수신한다.
 * @param[in] 프레임을 수신한 CAN 소켓 디스크립터
 * @retval 0: 성공
 * @retval -1: 실패
 */ 
int ReceiveCanFrame(const int sock)
{
  /*
   * CAN 프레임을 수신한다.
   */ 
  struct can_frame frame;
  int rx_bytes = read(sock, &frame, sizeof(frame));
  if (rx_bytes < 0) {
    perror("Fail to receive can frame - ");
    return -1;
  } else if (rx_bytes < (int)sizeof(struct can_frame)) {
    printf("Incomplete can frame is received - rx_bytes: %d\n", rx_bytes);
    return -1;
  } else if (frame.can_dlc > CAN_FRAME_MAX_LEN) {
    printf("Invalid dlc: %u\n", frame.can_dlc);
    return -1;
  }

  /*
   * 프레임 유형에 따라 처리한다.
   */
  if (((frame.can_id >> 29) & 1) == 1) {
    printf("Error frame is received\n");
  } else if (((frame.can_id >> 30) & 1) == 1) {
    printf("RTR frame is received\n");
  } else {
    if (((frame.can_id >> 31) & 1) == 1) {
      printf("11bit long std can frame is received\n");
    } else {
      printf("29bit long ext can frame is received\n");
    }
    
    // TODO: 프레임 처리
  }

  return 0;
}


/// CAN ID 
#define CAN_ID 0x13e

/**
 * @brief 테스트 어플리케이션 메인함수
 */ 
int main(void)
{
  /*
   * CAN 인터페이스를 초기화한다.
   */
  int sock = InitCanInterface("can0");
  if (sock < 0) {
    return -1;
  }

  /*
   * CAN 데이터 프레임을 송신한다.
   */ 
  uint8_t can_data[CAN_FRAME_MAX_LEN] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
  TransmitCanFrame(sock, CAN_ID, can_data, sizeof(can_data));

  /*
   * CAN 프레임을 수신한다. (별도의 수신 쓰레드 내에서 호출 가능)
   */  
  ReceiveCanFrame(sock);

  return 0;
}

 

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음

댓글

Designed by JB FACTORY