ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 진단 통신으로 제어기 확인하기
    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 메시지의 구조는 아래 그림과 같다.

    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를 정의한다.

    timer_diag_req의 정의. 500msec 마다 진단 메시지를 전송하도록 설정하였다.

    • 타이머가 트리거할 때마다 m_id를 +1 만큼 증가하고 Read ECU ID 요청을 전송하도록 On Timer에 diag_req 함수를 정의한다.

    On Timer에 timer_diag_req가 트리거할 때마다 실행할 함수인 on_timer_diag_req()를 정의한다. 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을 입력했을 때 수행할 함수의 이름을 입력한다. 그리고 코드 창에서 함수를 정의한다.

    키보드로 Ctrl-1을 입력하면 실행할 함수를 정의한다.
    키보드에서 Ctrl-1을 입력하면, req에 Read ECU ID을 저장하고, m_id에 시작값인 0x700을 저장하고, timer_diag_req를 시작한다. timer_diag_req 주기마다 m_id를 증가하면서 Read ECU ID 요청을 전송한다.

     

    • 미니프로그램의 전체 코드를 첨부한다.

    mobis_esc_ecu_id_rev_eng.py
    0.03MB

     

     

    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'

     

    Read ECU ID 응답 메시지 처리 과정. 응답이 여러 메시지들로 분리되어 전송되는 경우 First Frame에 대응하는 Flow Control 메시지를 전송해야 한다. 그리고 응답들을 조립해야 한다.

     

     

    진단 요청과 응답 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