CS/통신

[통신] CAN DBC

nowkoes 2024. 6. 22. 23:54

CAN DBC

개요

출처: https://www.csselectronics.com/pages/can-dbc-file-database-intro

 

 지난 시간엔 CAN 통신에 대한 기초적인 내용(특징, 장점, 단점, 구조 등)에 대해 다뤘다. 이번 시간에는 구체적으로 CAN 통신을 하기 위해서 어떤 과정을 거쳐야 하는지, 이 과정에서 DBC(Data Binary Code)가 어떤 역할을 하는지 알아보도록 하겠다.


본문

통신 과정

출처: https://ko.aliexpress.com/

 

 앞서 CAN 통신을 하기 위해서는 MCU 내부에 CAN Controller가 내장되어 있어야 한다고 했다. 이러한 컨트롤러와 통신하기 위해서는 트랜시버(transceiver)가 필요하며, 송/수신되는 데이터를 모니터링하기 위해 물리적인 장치가 필요하다. 대표적으로 Peak CAN과 Vector CAN 장비가 있지만, 이 장비들은 가격이 비싸기 때문에 상대적으로 저렴한 CANable Pro v2.0 제품을 이용했다. 이 장비는 PC와 CAN 버스 간의 연결을 도와주는 인터페이스 역할을 한다.

 

연결 예시

 

 예를 들어, 보드에서 온도를 감지하여 이를 PC에서 확인하고 싶다고 가정해 보자. CAN 버스는 CAN_H와 CAN_L 두 가지 신호선을 사용한다. 따라서 보드와 통신 어댑터는 두 개의 선으로 연결되어야 한다. 이때 종단 저항(terminating resistance)을 고려해야 한다. 이는 데이터 신호가 전선을 따라 이동하다가 끝에 도달했을 때 반사되어 원래의 데이터 신호와 충돌하여 데이터 오류를 일으키는 반사를 방지하기 위해 사용된다. 그리고 해당 어댑터에서 보내는 데이터를 보기 위해 PC와 연결한다. 이를 확인하기 위해 파이썬 코드를 다음과 같이 작성해 보았다.

 

import can
import time

bus = can.interface.Bus(bustype='slcan', channel='COM5', bitrate=500000)

try:
    while True:
        message = bus.recv(timeout=0.02).data
        hex_data = ' '.join(f'0x{byte:02X}' for byte in message)
        print(f"Message sent on : ID=0x{message.arbitration_id:X} Data=[{hex_data}]")
        time.sleep(0.1)
        
except KeyboardInterrupt:
    print("Message sending stopped by user")
except can.CanError as e:
    print(f"CAN Error: {e}")

 

 만약 연결하는 방법(파이썬을 이용하여)에 대해 자세하게 알고 싶다면 아래의 접은 글을 확인하자.

더보기
 파이썬에서는 CAN 통신 인터페이스를 제공하는 python-can 라이브러리가 존재한다. 해당 라이브러리를 다음 명령어로 설치하자.

 

pip install python-can

 

 그 후, can 라이브러리를 임포트하고, 어댑터가 지원하는 연결 방식과 연결된 채널, 통신 속도를 설정한다. 필자가 사용한 CANable Pro v2.0의 경우 slcan 방식을 사용한다. 이는 USB 장치가 시리얼 포트와 같은 통신 인터페이스를 제공할 수 있도록 하는 CDC-Communication Device Class를 이용하기 때문에 직렬 통신처럼 사용할 수 있다.

 

bus = can.interface.Bus(bustype='slcan', channel='COM5', bitrate=500000)
print(bus.recv().data)

 

  그리고 현재 보드에서 송신하고 있는 메시지를 받으려면 bus.recv() 함수를 사용하면 된다. 메시지를 보내려면 bus.send() 함수를 사용한다. 이때 메시지를 송신하기 위해 프로토콜을 맞춰야 하는데, 이는 can.Message 메서드를 이용하면 된다. 데이터 프레임에 들어갈 데이터는 16진수로 이루어진 데이터 바이트 형식을 띠어야 한다는 점을 유념하자.

 

msg = can.Message(arbitration_id=0x111, data=bytearray([0] * 8), is_extended_id=False)
bus.send(msg)

DBC 파일

출처: https://www.vector.com/

 

 수신되는 메시지를 보면 알 수 있지만, 해당 데이터가 무엇을 의미하는지, 또한 어떠한 값을 갖고 있는지 파악하기가 어렵다. 따라서 이러한 메시지를 파싱하여 좀 더 보기 쉽게 나타내기 위해 사용되는 것이 바로 CAN DBC 데이터베이스다. 즉, DBC 파일은 CAN 버스 시스템에서 메시지와 신호를 정의하는 표준화된 파일 형식이다. 일반적으로 DBC 파일은 vector에서 제공하는 CANdb++를 사용한다. 하지만 필자는 좀 더 원론적인 내용에 입각하여 설명하고자, vector에서 정의한 dbc를 기준으로 txt 파일로 해당 데이터베이스가 어떤 구조를 띄는지, 어떻게 작성해야 하는지 설명하도록 하겠다.

 

 

 DBC 공식 문서에 따르면 DBC를 정의하는 심볼은 총 10가지가 존재한다. 이중 DBC 파일을 작성하기 위해 필수적으로 사용되는 심볼들은 따로 볼드처리 하도록 하겠다.

 

1. 버전: VERSION "<version>", 해당 DBC 파일의 버전을 나타냄

2. 심볼: NS_ : <symbol>, 어떤 개념이나 객체를 대표하는 데 사용되는 기호나 문자. DBC 파일 내의 모든 심볼들을 기술

3. 통신 속도: BS_, 통신 속도와 관련된 심볼이지만, 더 이상 사용되진 않음. 하지만 DBC 파일엔 포함되어야 함

4. 버스 유닛: BU_ : <network>, CAN 네트워크에 참여하는 각각의 장치 또는 ECU 노드

5. 값: VAL_ <ID> <name> <num of status> "<status>", 특정 메시지의 신호에 대해 가능한 값과 그 값들에 대응하는 의미나 설명을 매핑

  • ex) VAL_ ex_status 3 "limited operation" 2 "failure mode" 1 "operation" 0 "stop";

6. 메시지: BO_ <ID> <name>: <dlc> <network>: CAN 네트워크를 통해 전송되는 각각의 메시지. 하나 이상의 데이터 필드를 포함시켜, 시그널을 통해 세부적으로 정의

  • ex) BO_ 515 ex_message: 8 Vector_XXX; 
  • 이때 ID는 10진수

7. 시그널: SG_ <name>: <start_bit>|<bit_length>@<bite_order><sigend> (<scale>, <offset>) [<min>|<max>] "<unit>" <network>, 메시지 내에 포함된 각각의 데이터 필드

  • ex) BO_ 515 ex_message: 8 Vector_XXX; 
    • SG_ ex_message_signal: 48|8@1+ (0.2,0) [0|50] "A" Vector__XXX
    • 이때 <bite_order>는 1이면 big endian, 0이면 little endian
    • sigend에서 +는 unsigend, -는 sigend

8. 속성: BA_: "<attribute_name>" <symbol> <id> <attribute> <value>, 사용자 정의 속성 값 지정

  • ex) BA_ "GenMsgCycleTime" BO_ 123 500; ID 123에 대해 GenMsgCycleTime 속성 값을 500으로 설정

9. 값 테이블: VAL_TABLE <name> <num of status> "<status>", 특정 신호에 대해 가능한 값과, 그에 대응하는 의미나 설명을 나열하는 테이블. 즉, 값 VAL에 대해 재사용 가능한 형태로 정의하는 것

  • ex) VAL_TABLE_ ex_status 3 "limited operation" 2 "failure mode" 1 "operation" 0 "stop";

10. 주석: CM_ <symbol> <id> "<text>", 메시지, 신호, 노드 등에 대해 주석을 추가할 때 사용 

 

 해당 규칙에 따라 dbc 파일을 텍스트 파일에서 작성하였다면, 확장자를 .dbc 파일로 저장하면 된다.


예제 실습

 

 다음과 같이 DBC 파일을 작성하고, 현재 내가 받고 있는 CAN 메시지를 파싱해보는 코드를 짜보도록 해보자. 먼저 cantools 라이브러릴 임포트하여 dbc를 로드해야 한다.

 

pip install cantools

 

 그리고 dbc 파일을 로드하자.

 

import can
import time
import cantools
import cantools.database

dbc_file = r'C:\Users\우리집\Desktop\example.dbc'
db = cantools.database.load_file(dbc_file)

 

메시지 아이디를 포함하는 리스트를 만들고, CAN 버스와 PC를 연결하는 인터페이스를 선언하자.

 

message_ids = [304]

bus = can.interface.Bus(bustype='slcan', channel='COM5', bitrate=500000)

 

 마지막으로 디코딩된 메시지를 출력하면 된다.

 

while True:
    for message_id in message_ids:
        try:
            data = bus.recv()
            decode_messages = db.decode_message(data.arbitration_id, data.data)
            print(f"Decoded data: {decode_messages}")
        except KeyError:
            print(f"Message ID 0x{message_id:X} not found in DBC file.")
        except Exception as e:
            print(f"An error occurred: {e}")

        time.sleep(0.1)

 

총합본

더보기
import can
import time
import cantools
import cantools.database

dbc_file = r'C:\Users\your_pc\Desktop\example.dbc'
db = cantools.database.load_file(dbc_file)

message_ids = [304]

bus = can.interface.Bus(bustype='slcan', channel='COM5', bitrate=500000)

while True:
    for message_id in message_ids:
        try:
            data = bus.recv()
            decode_messages = db.decode_message(data.arbitration_id, data.data)
            print(f"Decoded data: {decode_messages}")
        except KeyError:
            print(f"Message ID 0x{message_id:X} not found in DBC file.")
        except Exception as e:
            print(f"An error occurred: {e}")

        time.sleep(0.1)

요약

CAN DBC
1. 정의: CAN 버스 시스템에서 메시지와 신호를 정의하는 표준화된 파일 형식
2. 특징
 a. .dbc 확장자지만 txt 파일로 편집이 가능
 b. CAN에서 송수신되는 메시지 파싱
3. 구성: VERSION, NS_, BS_, BU_, VAL_, BO_, SG_, BA_, VAL_TABLE_, CM_

 

반응형

'CS > 통신' 카테고리의 다른 글

[통신] CAN 통신 개요  (2) 2024.06.09