-
진단 통신으로 제어기 확인하기application 2025. 1. 26. 13:10
시작하기 전에
CAN 통신 데이터를 수집하고, 수집한 데이터에서 메시지 아이디들을 찾고, 찾은 메시지 아이디들을 dbc에서 찾는 방식으로 차에 어떤 제어기들이 있는 지 확인할 수 있다. (dbc에서 m_id들을 찾기 :: hsl's tsmaster 사용기) 형상 관리를 위한 최소한의 점검이다. 이 점검을 통해서 차에 있는 혹은 없는 제어기들을 확인할 수 있다. 제어기들의 하드웨어와 소프트웨어가 설계 사양에 맞는지까지 확인하기는 쉽지 않다. 필요가 있으면 발명이 있다.
UDS 서비스 중에 Read Data By Identifier라는 것이 있다. 자동차사들은 진단 통신 사양서에 이 서비스를 명시한다. 그래서 제어기의 하드웨어, 소프트웨어 정보를 읽도록 한다. Read Data By Identifier 서비스로 읽은 하드웨어와 소프트웨어 사양을 기준이 되는 설계 사양과 비교하여 차량 형상을 점검할 수 있다.
개요
- Read Data By Identifier 요청 전송
- Read Data By Identifier 응답 처리
- 진단 요청과 응답 m_id 짝 찾기
- 제어기들의 ECU ID를 읽어 차량 형상 확인하기
Read Data By Identifier 요청 전송
- UDS 메시지의 구조는 아래 그림과 같다.
- m_id는 제어기별로 지정되어 있다.
- 나는 제어기별 m_id 정보가 없다. 진단 통신에는 0x700부터 0x7FF까지의 m_id가 사용된다.
- m_id를 0x700부터 0x7FF까지 1씩 변경하면서 Read Data By Identifier 요청을 보내고 응답을 기다리는 방식으로 m_id를 찾을 것이다. 이를 스캐닝이라고 하겠다.
- Read Data By Identifier의 SID(Service IDentifier)는 0x22이다.
- Sub-function Byte는 옵션이며, Read Data By Identifier 서비스에는 sub-function이 없다.
- DID(Data IDentifier)는 2 byte이다.
- 표준 DID가 있으나 OEM별로 고유의 DID를 사용하는 경우가 더 많은 것 같다.
- 현대자동차 베뉴의 경우, ECU ID의 DID는 0xF1 0x00 이다.
- "Read Data By Identifier DID = ECU ID"는 너무 길다. Read ECU ID라고 부르겠다.
- Read ECU ID 서비스 요청은 "0x22 0xF1 0x00" 3 바이트가 된다.
- CAN 메시지에는 서비스 요청의 길이를 표시하는 3이 PCI(Protocol Control Info) 자리에 들어간다. "0x03 0x22 0xF1 0x00"
- UDS on CAN에서 메시지의 길이는 8 바이트를 사용한다. Read ECU ID의 경우 4 바이트를 Padding해야 한다.
- Padding 바이트로 0xAA를 사용한다. 0xAA는 바이너리로 1과 0이 교차한다. b1010101010101010
- Read ECU ID 요청은 "0x03 0x22 0xF1 0x11 0xAA 0xAA 0xAA 0xAA"가 된다.
- m_id를 바꿔가며 Read ECU ID 요청을 주기적으로 전송하여 스캐닝을 하는 미니프로그램을 작성한다.
- 주기적으로 전송하기 위해서 timer_diag_req를 정의한다.
- 타이머가 트리거할 때마다 m_id를 +1 만큼 증가하고 Read ECU ID 요청을 전송하도록 On Timer에 diag_req 함수를 정의한다.
- on_timer_diag_req() 함수는 아래 코드와 같다. 아래의 기능들을 수행한다.
- m_id (m_id_diag_req)를 1 증가한다.
- req에 미리 담아둔 Read ECU ID 요청 ("0x03 0x22 0xF1 0x11 0xAA 0xAA 0xAA") 메시지를 전송한다. CAN과 CAN-FD의 경우 메시지 설정이 다르다. 이를 고려하도록 하였다.
- m_id가 0x7FF가 되면 timer_diag_req를 정지시킨다.
def on_timer_diag_req() -> None: global k_is_can_fd global m_id_diag_req global req global is_timer_diag_req_on global is_timer_tester_present_on m_id_diag_req += 1 # diag_req 메시지를 만든다. if k_is_can_fd: # CAN-FD 메시지 msg_diag_req = RawCAN(m_id_diag_req, 8, CH1, 0, req, 0x306) else: # CAN 메시지 msg_diag_req = RawCAN(m_id_diag_req, 8, CH1, 0, req) # diag_req 메시지를 전송한다. if com.transmit_can_async(msg_diag_req) == 0: # app.log_text(f'{msg_diag_req}', lvlOK) # 출력량이 너무 많아서 디버깅할 때만 사용한다. # 트레이스 윈도에서 전송 메시지를 확인할 수 있다. pass # 모든 m_id를 처리한 후에 timer_diag_req를 멈춘다. if m_id_diag_req >= k_m_id_scan_stop: timer_diag_req.Stop() is_timer_diag_req_on = False app.log_text('timer_diag_req stopped', lvlOK) timer_tester_present.Stop() is_timer_tester_present_on = False app.log_text('timer_tester_present stopped', lvlOK) m_id_diag_req = k_m_id_scan_start
- 미니프로그램을 시작하면 timer_diag_req는 정지 상태이다. 이 타이머를 시작시켜야 한다. 그리고 req에 Read ECU ID 요청도 넣어줘야 한다. 키보드에서 Ctrl+1(Ctrl키를 누른 상태에서 숫자 1)을 누르면 작업이 이뤄지도록 한다.
- On Shortcut에 마우스 우클릭하여 아래 그림과 같이 팝-업 메뉴를 열고, Shortcut을 클릭한 후에 Ctrl키를 누른 상태에서 숫자 1을 누르면 "Ctrl+1"이 입력된다. Ctrl+1을 입력했을 때 수행할 함수의 이름을 입력한다. 그리고 코드 창에서 함수를 정의한다.
- 미니프로그램의 전체 코드를 첨부한다.
Read Data By Identifier 응답 처리
- Read ECU ID의 응답은 길어서 여러 메시지들에 분리되어 전송되기도 한다. TP (Transport Protocol)가 필요하다.
- PC는 제어기에 비해 매우 빠르고 메모리도 매우 크다. 제어기가 여러 메시지들로 응답을 보낸다고 할 때, PC는 제어기에게 Block Size(BS)나 Wait Time (Stmin)에 제한 없이 보내라는 FC (Flow Control) 메시지를 보내는 것만으로 Read ECU ID 요청에 적절한 TP를 구현하게 된다. (TSMaster의 UDS 기능에 완전한 TP가 구현되어 있다. TSMaster의 UDS 기능은 고가이다.)
- 수신 메시지들에서 진단 메시지들 (0x700 ~ 0x7FF)을 추려서 점검해야 한다. On CAN Rx에 any_message를 대상으로 수신 메시지를 처리하는 함수 on_canfd_rx_any_message( )를 정의한다. CAN-FD가 아닌 CAN인 경우도 대상으로 하기 위해 CAN FD (Incl. Classical CAN) 체크 박스를 체크한다.
def on_canfd_rx_any_message(ACAN: RawCAN) -> None: # 진단 통신 m_id 범위 # 0x700 부터 0x7FF 까지 # k_m_id_diag_lowest = 0x700 # k_m_id_diag_highest = 0x7FF if ((k_m_id_diag_lowest <= ACAN.id) and (ACAN.id <= k_m_id_diag_highest)): # 실제 처리는 on_can_dia_resp()에서 이뤄진다. on_can_diag_resp(ACAN)
- on_can_diag_resp( ) 함수의 코드는 아래와 같다.
- 수식 메시지 (ACAN)의 첫 바이트 (ACAN.data[0])에서 PCI (Protocol Control Info)를 추출한다. PCI가 0x10인 경우, 응답을 여러 메시지들로 분리하여 전송할 것이며 본 메시지가 첫 번째 메시지(First Frame)라는 의미이다. First Frame을 수신한 경우, FC (Flow Control) 메시지를 전송해야 한다.
- FC 메시지는 "0x30 0x00 0x00 0xAA 0xAA 0xAA 0xAA 0xAA" 이다. 첫 번째 0x00은 제어기가 보내는 메시지의 수에 (Block Size)에 제한이 없음을 표시한다. 두 번째 0x00은 제어기가 보내는 메시지들 사이의 전송 간격에 제한이 없음을 표시한다.
def on_can_diag_resp(ACAN: RawCAN) -> None: # TP와 무관한 부분의 코드를 삭제했다. # TP로 전송된 분해된 response를 조립한다. pci = ACAN.data[0] & 0xF0 if pci == 0x00: # single frame # single frame을 처리하는 코드를 삭제했다. elif pci == 0x10: # first frame # 같은 m_id의 다음 응답 처리를 위해 초기화 한다. # counter는 초기화하지 않는다. 응답 횟수를 추적하기 위해서 # diag_resp[m_id_hex] = {} # diag_resp[m_id_hex]['length'] = 0 # diag_resp[m_id_hex]['counter'] = 1 diag_resp[m_id_hex]['s_id'] = 0x00 diag_resp[m_id_hex]['assembled'] = [] diag_resp[m_id_hex]['hex'] = [] diag_resp[m_id_hex]['length'] = (ACAN.data[0] & 0x0F) * 256 + ACAN.data[1] diag_resp[m_id_hex]['assembled'].extend(ACAN.data[2:8]) # NOTE # TSMaster의 UDS 기능을 사용하지 않는 경우 # flow control 메시지를 전송해야 한다. # k_flow_control = [0x30, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA] # is can_fd? if k_is_can_fd: # CAN-FD 메시지 msg_diag_req = RawCAN(m_id_diag_req, 8, CH1, 0, k_flow_control, 0x306) else: # CAN 메시지 msg_diag_req = RawCAN(m_id_diag_req, 8, CH1, 0, k_flow_control) if com.transmit_can_async(msg_diag_req) == 0: app.log_text(f'Tx fc = {msg_diag_req}', lvlOK) # 이하 코드를 삭제했다.
- 진단기(이 경우 미니프로그램)는 제어기가 여러 메시지들로 분리해서 보낸 응답을 모아서(assemble)해서 전체 응답을 구성한다.
- ESC에서 받은 응답과 해석의 예는 아래와 같다.
# 조립된 제어기의 Read ECU ID 요청에 대한 응답 ['62', 'f1', '00', '4e', '58', '34', '20', '45', '53', '43', '20', '03', '20', '41', '30', '31', '20', '23', '09', '03', '20', '35', '38', '39', '31', '30', '2d', '4e', '38', '37', '30', '30', 'aa', 'aa'] # ASCII로 표현한 Read ECU ID의 응답 'NX4 ESC \x03 A01 #\t\x03 58910-N8700'
진단 요청과 응답 m_id 짝 찾기
- 진단 스펙이 있으면 안 해도 될 일이다.
- 위에서 설명한대로 m_id를 0x700에서 0x7FF까지 변경하며 진단 요청을 전송하는 방법으로 스캐닝을 한다.
- 진단 요청 후 500msec 동안 응답을 기다린다.
- 500msec는 실험을 통해서 정한 충분히 긴 시간이다. 대부분의 제어기들은 진단 요청 후 몇 십 msec 이내에 응답을 한다.
- 펑셔널 진단 요청의 경우 "대부분"의 (요청에 따라 응답 하지 않는 제어기들이 있기 때문에 "전체"가 아닐 수도 있다.) 제어기들이 응답을 한다. 이를 고려하여 응답 대기 시간을 500msec로 길게 잡았다.
- 응답 대기 시간 안에 응답이 있는 경우, 요청 m_id와 응답 m_id를 한 쌍으로 판정한다.
- 응답 대기 시간 안에 응답이 없는 경우, 요청 m_id는 사용되지 않는 것으로 판정한다.
- TSMaster 미니프로그램을 작성할 수도 있지만 스캐닝을 하면서 측정한 CAN 버스 데이터를 blf 파일로 저장하고, 파일을 분석하는 방법으로 m_id 짝을 찾았다. 진단 요청과 응답 m_id 짝 찾기 :: hsl's tsmaster 사용기
- 찾은 짝은 13쌍으로 아래와 같다.
진단 요청/ 응답 m_id 짝 7C4/ 7CC 770/ 778 7F1/ 7F9 7D4/ 7DC 796/ 79E 7D1/ 7D9 7E0/ 7E8 7C3/ 7CB 7B3/ 7BB 7C6/ 7CE 7A0/ 7A8 7D2/ 7DA 7E1/ 7E9
- 펑셔널 진당 요청에 응답한 제어기는 총 10개로 m_id들은 아래와 같다.
펑셔널 요청에 응답한 m_id 778 79E 7A8 7CB 7CC 7D9 7DA 7DC 7E8 7E9
제어기들의 ECU ID를 읽어 차량 형상 확인하기
- Read ECU ID 요청으로 스캐닝한 응답을 정리하면 차량에 장착된 제어기들의 종류와 버전을 알 수 있(어야한)다.
- 진단 응답 해석하기 :: hsl's tsmaster 사용기
- 아래는 Read ECU ID 요청에 대한 FATC 제어기의 실제 응답이다.
'QX 97250-K2300HEATER-CONTROL FATC V1-02-903-00\x00\x00\x00'
- QX는 베뉴를 나타내는 차종 코드일 것이다.
- 97250-K2300은 아마도 제어기의 품번일 것이다.
- HEATER-CONTROL에서 무슨 제어기인지 알 수 있다.
- FATC는 제어기 이름일 것이다. 구글링으로 Full Automatic Temperature Control 이라고 찾았다.
- V1-02-903-00이 버전 정보일 것이다.
- ECU ID 관리가 잘 이뤄진다면, 전체 제어기들의 ECU ID들을 읽어서 형상 관리를 할 수 있을 것이다.
- 모든 제어기들이 FATC처럼 명확하게 응답하면 좋은데, 현실은 그렇지 않다. 실제 응답은 아래와 같다.
0x778 ecu_id = '19' 0x79e ecu_id = '0.3\x00\x00\x00\x00\x00\x00\x00\x00\x00' 0x7a8 ecu_id = '220' 0x7bb ecu_id = 'QX 97250-K2300HEATER-CONTROL FATC V1-02-903-00\x00\x00\x00' 0x7cc ecu_id = 'QX MFC AT KOR LHD 1.00 1.01 99211-K2100 210329' 0x7ce ecu_id = '941\x00' 0x7d9 ecu_id = 'QX ESC \x05 100\x19\x04\x01 58910-K2500' 0x7dc ecu_id = 'QX MDPS C 1.00 1.06 56340-K2000 0B06'
- "진단 요청과 응답 m_id 짝 찾기" 에서 찾은 응답 m_id가 13개이다. Read ECU ID에 응답한 제어기들은 8개이다. 5개는 응답을 하지 않았다. 응답을 한 제어기들도 응답 양식에 맞춰 응답한 것인지 불분명하다. (양산차인데 이래도 되는거구나 ^^.)
- 모든 제어기들이 ECU ID 요청에 응답한다면, 이 응답들과 기준 설계에 따른 정답들을 비교하여 형상 관리에 이용할 수 있다.
- (이왕 ECU ID에 응답을 하도록 한다면, FATC, MFC, ESC, MDPS 처럼 사람이 읽어서 이해할 수 있도록 하는 것도 나쁘지 않을 것 같다.)
결론
- 진단 통신을 통해서 각 제어기들의 ECU ID를 읽을 수 있다.
- 제어기들의 ECU ID를 이용하여 실차의 형상을 확인할 수 있다.
- 확인된 실차의 형상과 기준 설계 형상을 비교하여 형상 관리를 할 수 있다.
'application' 카테고리의 다른 글
진단 응답 해석하기 (1) 2025.01.26 진단 요청과 응답 m_id 짝 찾기 (0) 2025.01.26 dbc에서 m_id들을 찾기 (0) 2025.01.26 차량 형상 점검 (0) 2025.01.19 주행 중인 도로의 경사도 구하기 1/t.b.d. (1) 2025.01.15