[목차]
① CAN통신 프로그래밍 1/3 - https://makeutil.tistory.com/93 - CAN 통신 개요
② CAN통신 프로그래밍 2/3 - https://makeutil.tistory.com/94 - 라즈베리파이에서 CAN 장치 설정
③ CAN통신 프로그래밍 3/3 - https://makeutil.tistory.com/95 - CAN 통신 샘플코드
일반적으로 리눅스의 경우 CAN통신을 위한 다양한 라이브러리가 포함되어있거나 또는 포함할 수 있다. 이번에는 이미 공개된 CAN 통신코드를 일부 수정하여 만든 CAN 데이터 전송 예제이다.
1. CAN 통신 예제코드
CAN 이론설명때 CAN도 ISO레이어를 사용한다고 언급했다. 리눅스에서 CAN은 네트워크로 인식된다. 라즈베리파이 설정에서 네트워크 장치 명령으로 확인한 것이 기억이 나야한다. 결론적으로 리눅스에서 CAN은 네트워크 프로그래밍을로 통신이 가능하다는 것이므로 Socket통신을 이용하여 통신한다.
예제코드는 아래와 같다. 아래의 소스코드는 CAN을 통해서 네트워크로 데이터를 보내는 예제이다. 이때 can_frame에 id를 넣어준다. 동일한 네트워크의 각 CAN장치는 각 모듈의 ID와 맞는 데이터만 취하도록 구성되어있다. 그리고 보내고자하는 패킷(데이터)길이를 넣어준 다음부터 나머지는 애플리케이션에서 필요한 정보를 넣어준다.
1.1. C기반 CAN Example
C의 라이브리의 함수를 이용하여 만들 수 있는 샘플 코드이다.
ex) gcc -o can_ex1 can_ex1.c
/* RaspberryPi 4 for Automotive IoT Kit
* TITLE : C Based Example for CAN
* File : can_ex1.c
* Auth : wikipedia.org
* Ment : Original Soruce - http://en.wikipedia.org/wiki/SocketCAN */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(void)
{
int s;
int nbytes;
struct sockaddr_can addr;
struct can_frame frame;
struct ifreq ifr;
const char *ifname = "vcan0";
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) {
perror("Error while opening socket");
return -1;
}
strcpy(ifr.ifr_name, ifname);
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
printf("%s at index %d\n", ifname, ifr.ifr_ifindex);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("Error in socket bind");
return -2;
}
frame.can_id = 0x10;
frame.can_dlc = 8;
frame.data[0] = 0x11;
frame.data[1] = 0x22;
frame.data[2] = 0x33;
frame.data[3] = 0x44;
frame.data[4] = 0x55;
frame.data[5] = 0x66;
frame.data[6] = 0x77;
frame.data[7] = 0x88;
nbytes = write(s, &frame, sizeof(struct can_frame));
printf("Wrote %d bytes\n", nbytes);
return 0;
}
1.2. CPP 기반의 샘플코드
생긴건 C처럼 보이지만 내부에서 사용하는 부분이 g++의 라이브러리를 이용한다. 이런경우 g++로 빌드하여야 한다.
ex) g++ can_ex1 can_ex1.cpp
/* CAN 통신 예제 코드
AUTH : mkdev.co.kr */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(int argc, char** argv)
{
int can_fd;
struct sockaddr_can can_addr;
struct can_frame can_frame;
struct ifreq can_ifr;
const char* can_dev = "can0"; // 하드웨어 이용시.
//const char *can_dev = "vcan0"; // 가상 장치 이용시.
strcpy(can_ifr.ifr_name, can_dev);
ioctl(can_fd, SIOCGIFINDEX, &can_ifr);
can_addr.can_family = AF_CAN;
can_addr.can_ifindex = can_ifr.ifr_ifindex;
fcntl(can_fd, F_SETFL, O_NONBLOCK);
if(bind(can_fd, reinterpret_cast<struct sockaddr *>(&can_addr), sizeof(can_addr)) < 0)
{
printf("Binding Error!\n");
exit(1);
}
can_frame.can_id = 0x100; // CAN ID
can_frame.can_dlc = 8; // 데이터 길이
can_frame.data[0] = 0x31; // 데이터 0~7 (8Byte)
can_frame.data[1] = 0x32;
can_frame.data[2] = 0x33;
can_frame.data[3] = 0x34;
can_frame.data[4] = 0x35;
can_frame.data[5] = 0x36;
can_frame.data[6] = 0x37;
can_frame.data[7] = 0x38;
ssize_t nbytes = write(can_fd, &can_frame, sizeof(struct can_frame)); // 전송
if(nbytes<1)
{
printf("Send Error!\n");
return -1;
}
close(can_fd);
return 0;
}
어떤가 네트워크 프로그래밍과 동일하지 않은가? 다른 부분은 socket 설정시 AF_CAN이라던지 CAN에 특화된 내용을 포함하고 있다.
기타 하드웨어 초기화등과 관련된 내용은 CAN모듈의 데이터 시트를 참고하기 바란다. 아쉽게도 본문을 작성하는 이 시점에서 장비를 사용할 수 없어 결과는 생략하지만 설정이 제대로 되어있다면 분명 정상적으로 구동된다. 더 자세한 내용이 필요한 독자는 구글에서 can programming으로 검색하면, 필자가 참조한 설명과 문서코드가 포함된 소스를 다운로드 받을 수 있다. 어렵지도 않거니와 별도로 책자가 없는 관계로 해당 문서를 보면 좀더 코딩에 도움이 될 거라 생각하면서 마친다.
# 라즈베리파이 CAN 통신.... 끝.
최근댓글