-
claude.ai와 코드, 문서 분석 및 코드 작성application 2025. 7. 26. 15:07
CAN Flash Programmer Porting Project
Prompt:
내가 첨부할 파일들은 TI(Texas Instrument)사의 칩을 CAN 통신을 통해서 플래시 프로그래밍하는 도구인 can_flash_programmer의 코드 파일들입니다.
- can_flash_programmer.cpp
- f021_DownloadImage.h
- f021_DownloadImage.cpp
- PCANBasic.h
이 앱은 PEAK사의 CAN(Controller Area Network) 하드웨어를 이용하여 USB를 통해서 CAN 통신을 합니다.
나는 토선(Tosun)사의 CAN 하드웨어를 이용하는 이와 동일한 프로그램을 개발하려고 합니다.
이 프로그램의 이름은 ts_can_flash_programmer로 하겠습니다.
이 프로그램 개발을 위해서 나는 당신에게 두 가지 파일을 추가로 제공할 겁니다.- tc1013_can-fd_example.c : 토선 CAN 하드웨어를 사용하는 예제 코드입니다.
- libTSCAN_SDK_CPP_cn.pdf: 토선 CAN 하드웨어 api 문서입니다.
can_flash_programmer는 C++로 작성되었습니다. 나는 C를 읽고 이해하는 정도의 C 프로그램 능력을 갖고 있습니다. C++는 잘 읽지 못합니다. 그래서 프로그램은 C로 작성합니다.
우리가 요구 조건을 서로 잘 이해하고 (아마 당신은 쉽게 이해할 것이고, 나는 질문이 많을 겁니다.) 프로그램 개발에 합의한 후에 코딩을 해주세요.
함께 코딩을 할 수 있습니까?
Response:
네, 함께 코딩할 수 있습니다!
상황을 정리해보면:
- 현재 PEAK CAN 하드웨어를 사용하는
can_flash_programmer(C++)가 있음 - 이를 토선(Tosun) CAN 하드웨어용으로 포팅하여
ts_can_flash_programmer(C)로 개발 - TI 칩의 CAN 통신 기반 플래시 프로그래밍 도구
제가 도움드릴 수 있는 부분들:
- 기존 C++ 코드 분석 및 핵심 로직 파악
- PEAK CAN API와 토선 CAN API 간의 차이점 분석
- C++에서 C로의 포팅 작업
- CAN 통신 프로토콜 및 플래시 프로그래밍 로직 구현
먼저 파일들을 업로드해 주시면:
- 기존 코드의 구조와 동작 방식을 분석하겠습니다
- 토선 CAN API의 사용법을 파악하겠습니다
- 포팅 계획을 세우고 요구사항을 명확히 하겠습니다
파일들을 업로드해 주시고, 혹시 추가로 고려해야 할 특별한 요구사항이나 제약사항이 있다면 알려주세요.
Prompt:
File: PCANBasic.h
File: f021_DownloadImage.h
File: can_flash_programmer.cpp
File: f021_DownloadImage.cpp
내 짐작에 CAN 하드웨어를 연결하고, 하드웨어를 설정하고, 메시지를 전송하는, 하드웨어 연결을 해제하는 부분만 PEAK사의 함수를 토선사의 함수로 바꾸면 될 것 같습니다.
먼저, can_flash_programmer의 파일들 입니다.
Response:
네, 파일들을 분석해보니 당신의 짐작이 맞습니다. 기존 코드에서 PEAK CAN API 부분만 토선 CAN API로 교체하면 될 것 같습니다.
기존 코드 분석
현재 PEAK CAN API를 사용하는 주요 부분들:
- 초기화:
CAN_InitializeFD()- CAN-FD 초기화 - 메시지 전송:
CAN_WriteFD()- 64바이트 CAN-FD 메시지 전송 - 종료:
CAN_Uninitialize()(현재 코드에는 없지만 필요)
주요 동작 흐름
- CAN 초기화: 1Mbps nominal, 2Mbps data bitrate로 CAN-FD 설정
- 커널 전송: 64바이트씩 커널 파일을 CAN ID 0x1로 전송
- 5초 대기: 커널 부팅 대기
- 애플리케이션 전송: 64바이트씩 애플리케이션 파일을 전송 (전송 간격: f28003x=100ms, f28p65x=1ms)
토선 API 예제와 문서 필요
다음 단계로 진행하기 위해 다음 파일들을 업로드해 주세요:
tc1013_can-fd_example.c- 토선 CAN 하드웨어 예제 코드libTSCAN_SDK_CPP_cn.pdf- 토선 CAN API 문서
이 파일들을 보면:
- 토선 API의 초기화 함수
- CAN-FD 설정 방법
- 메시지 전송 함수
- 에러 처리 방식
을 파악해서 정확한 포팅 계획을 세울 수 있습니다.
파일들을 업로드해 주시면 구체적인 포팅 방법을 제안드리겠습니다.
Prompt:
중요한 발견이 있네요. 파일 전송 속도입니다. 이를 설정할 수 있도록 하고 싶습니다. -t 옵션을 만들어서 -t 10 하면 10msec로 전송하도록 할 수 있겠죠?
Response:
네, 좋은 아이디어입니다! 파일 전송 속도를 명령행 옵션으로 설정할 수 있도록 하는 것이 매우 유용할 것 같습니다.
현재 코드의 전송 속도 설정
현재 코드를 보면:
f28003x: 100ms 간격f28p65x: 1ms 간격sleep_time전역 변수로 관리
제안하는 수정사항
1. 명령행 파라미터 추가
// 전역 변수 추가 int g_iTransmitDelay = -1; // -1이면 기본값 사용, 아니면 사용자 지정값 // ParseCommandLine에 추가 case 't': g_iTransmitDelay = _wtoi(pcOptArg); if (g_iTransmitDelay < 0) { _tprintf(_T("Invalid transmit delay: %s\n"), pcOptArg); ExitApp(1); } iParm++; break;2. Help 메시지 업데이트
_tprintf(_T("-t <delay> - Transmit delay in milliseconds between CAN frames\n")); _tprintf(_T(" (default: f28003x=100ms, f28p65x=1ms)\n"));3. sleep_time 설정 로직 수정
// 기존 코드 대신 if (g_iTransmitDelay >= 0) { sleep_time = g_iTransmitDelay; // 사용자 지정값 사용 } else { // 기본값 사용 if (g_bf28003x) { sleep_time = 100; } else if (g_bf28p65x) { sleep_time = 1; } }이렇게 하면:
-t 10: 10ms 간격으로 전송-t 0: 지연 없이 최대 속도로 전송-t옵션 없음: 기존처럼 디바이스별 기본값 사용
토선 CAN 예제 파일들을 업로드해 주시면, 이 기능도 포함해서 전체 포팅 계획을 세워드리겠습니다.
Prompt:
File: libTSCAN_SDK_CPP_cn.pdf
File: tc1013_can-fd_example.c
좋은 제안입니다.
토선 관련 파일들 입니다.
Response:
훌륭합니다! 이제 토선 CAN API를 분석해보고 포팅 계획을 세워보겠습니다.
토선 API 분석
주요 함수들 비교:
기능 PEAK API 토선 API 초기화 CAN_InitializeFD()tscan_config_canfd_by_baudrate()메시지 전송 CAN_WriteFD()tscan_transmit_canfd_async()연결 채널 직접 사용 tscan_connect()라이브러리 초기화 없음 initialize_lib_tscan()데이터 구조 비교:
PEAK:
TPCANMsgFDID,MSGTYPE,DLC,DATA[64]
토선:
TLIBCANFDFIdentifier,FFDProperties,FDLC,FData[64]
포팅 계획
1. 명령행 옵션 추가 (-t 옵션)
// 전역 변수 추가 int g_iTransmitDelay = -1; // -1: 기본값, 아니면 사용자 지정값 // ParseCommandLine에 추가 case 't': g_iTransmitDelay = _wtoi(pcOptArg); if (g_iTransmitDelay < 0) { printf("Invalid transmit delay: %s\n", pcOptArg); exit(1); } iParm++; break;2. API 함수 매핑
// PEAK -> 토선 함수 매핑 CAN_InitializeFD() → tscan_config_canfd_by_baudrate() CAN_WriteFD() → tscan_transmit_canfd_async() TPCANMsgFD → TLIBCANFD3. 구조 변환
// PEAK 메시지를 토선 메시지로 변환하는 함수 void convert_peak_to_tosun(TPCANMsgFD* peak_msg, TLIBCANFD* tosun_msg) { tosun_msg->FIdxChn = 0; // 채널 0 tosun_msg->FIdentifier = peak_msg->ID; tosun_msg->FDLC = peak_msg->DLC; tosun_msg->FProperties.bits.istx = 1; // TX 메시지 tosun_msg->FFDProperties.bits.EDL = 1; // CAN-FD tosun_msg->FFDProperties.bits.BRS = 1; // BRS 활성화 memcpy(tosun_msg->FData, peak_msg->DATA, 64); }4. 주요 수정 포인트
- 라이브러리 초기화:
initialize_lib_tscan(1, 0, 0) - 디바이스 연결:
tscan_connect() - CAN-FD 설정: 1Mbps/2Mbps로 설정
- 전송 지연:
-t옵션으로 제어
이제 실제 C 코드로
ts_can_flash_programmer.c를 작성할까요?어떤 부분부터 시작하시겠습니까?
- main 함수와 명령행 파싱부터
- 토선 CAN 초기화 부분부터
- 파일 전송 로직부터
Prompt:
질문부터요. :-)
initialize_lib_tscan(1, 0, 0)에서 1, 0, 0이 각각 의미하는 바는 뭔가요?
Response:
좋은 질문입니다! 토선 API 문서를 보면
initialize_lib_tscan함수의 매개변수들은 다음과 같습니다:void initialize_lib_tscan(bool AEnableFIFO, bool AEnableErrorFrame, bool AUseHWTime)각 매개변수의 의미:
- AEnableFIFO (첫 번째 매개변수:
1)- 의미: FIFO 메커니즘 활성화 여부
true(1)권장: FIFO를 활성화해야tsfifo_receive_xx함수들로 메시지를 읽을 수 있음- 용도: 수신된 메시지들을 버퍼에 저장하여 나중에 읽을 수 있게 함
- AEnableErrorFrame (두 번째 매개변수:
0)- 의미: 에러 프레임 수신 여부
false(0)설정: 에러 프레임을 드라이버에서 직접 버림true(1)설정: 에러 프레임도 수신하여 처리
- AUseHWTime (세 번째 매개변수:
0)- 의미: 하드웨어 타임스탬프 사용 여부
false(0)권장: 문서에서 직접 false로 설정하라고 명시- 용도: 성능 모드 관련 설정
우리 프로젝트에서는:
initialize_lib_tscan(1, 0, 0);- FIFO 활성화: 메시지 수신이 필요하지 않지만 안전하게 활성화
- 에러 프레임 비활성화: 플래시 프로그래밍에서는 불필요
- 하드웨어 타임스탬프 비활성화: 문서 권장사항
이 설정이 적절한 것 같습니다!
Prompt:
위와 같이 설정하면 프로그램은 에러 프레임이 발생 사실을 모르겠군요. 따라서 에러 프레임에 대한 사용자 경고나 복구 조치를 할 수 없구요. 방법이 있다면 사용자 경고를, 가능하다면 복구도 하면 좋겠습니다. 단, 방법이 간단해야 합니다. 많이 복잡하면 이 상태로 두려고 합니다.
Response:
좋은 지적입니다! 에러 프레임 처리를 추가하는 것이 좋겠네요. 토선 API에서는 비교적 간단하게 구현할 수 있습니다.
간단한 에러 프레임 처리 방법
1. 초기화 시 에러 프레임 활성화
initialize_lib_tscan(1, 1, 0); // 두 번째 매개변수를 1로 변경2. 에러 카운터 및 간단한 복구 로직
// 전역 변수 int g_error_count = 0; int g_consecutive_errors = 0; const int MAX_CONSECUTIVE_ERRORS = 5; // 연속 에러 허용 한계 // 에러 프레임 감지 함수 (메시지 전송 후 체크) int check_can_status_and_recover(size_t deviceHandle) { // 토선 API에는 직접적인 상태 확인 함수가 없으므로 // 전송 실패 시 에러로 간주 return 0; // 정상 } // 메시지 전송 시 에러 처리 int send_canfd_message_with_error_handling(size_t deviceHandle, TLIBCANFD* message) { u32 result = tscan_transmit_canfd_async(deviceHandle, message); if (result != 0) { g_error_count++; g_consecutive_errors++; printf("Warning: CAN transmission error occurred (Error code: %u, Total errors: %d)\n", result, g_error_count); // 연속 에러가 많으면 복구 시도 if (g_consecutive_errors >= MAX_CONSECUTIVE_ERRORS) { printf("Warning: Too many consecutive errors (%d). Attempting recovery...\n", g_consecutive_errors); // 간단한 복구: 잠시 대기 후 재시도 Sleep(1000); // 1초 대기 // CAN-FD 채널 재설정 시도 if (tscan_config_canfd_by_baudrate(deviceHandle, CHN1, 500.0, 2000.0, lfdtISOCAN, lfdmNormal, 1) == 0) { printf("Recovery: CAN-FD channel reconfigured successfully\n"); g_consecutive_errors = 0; // 복구 성공 시 연속 에러 카운트 리셋 } else { printf("Error: Failed to reconfigure CAN-FD channel\n"); return -1; // 복구 실패 } } return result; // 에러 코드 반환 } else { g_consecutive_errors = 0; // 성공 시 연속 에러 카운트 리셋 return 0; } }3. f021_DownloadImage.c에서 사용
// 기존 코드를: gStatus = CAN_WriteFD(PCAN_USBBUS1, &MessageBuffer); // 이렇게 변경: gStatus = send_canfd_message_with_error_handling(deviceHandle, &MessageBuffer); if (gStatus != 0) { QUIETPRINT(_T("Error sending CAN frame: %u\n"), gStatus); // 심각한 에러면 중단 if (g_consecutive_errors >= MAX_CONSECUTIVE_ERRORS) { QUIETPRINT(_T("Too many errors. Aborting transfer.\n")); return -1; } }4. 프로그램 종료 시 에러 리포트
void print_error_summary() { if (g_error_count > 0) { printf("\n=== Transfer Summary ===\n"); printf("Total CAN errors encountered: %d\n", g_error_count); printf("Transfer completed with warnings.\n"); } else { printf("\nTransfer completed successfully with no errors.\n"); } }장점:
- 간단함: 기존 코드에 몇 줄만 추가
- 사용자 경고: 에러 발생 시 즉시 알림
- 자동 복구: 연속 에러 시 채널 재설정 시도
- 통계: 전체 에러 개수 추적
단점:
- 토선 API는 PEAK처럼 상세한 CAN 상태 정보를 제공하지 않음
- 주로 전송 실패 기반으로만 에러 감지 가능
이 정도 수준이면 복잡하지 않으면서도 기본적인 에러 처리가 가능할 것 같은데, 어떻게 생각하시나요?
Prompt:
복잡하지 않군요.
이렇게 결정했습니다.
- 우선 에러 경고나 복구 기능 없이 구현한다.
- 실험한다.
- 문제가 에러 경고나 복구 기능 필요 여부를 판정한다.
- 필요가 확인되면 구현한다.
이유는 우선 최소 필수 기능 구현에 집중하려고요.
다음 질문은 아래 코드 관련입니다.
// PEAK 메시지를 토선 메시지로 변환하는 함수 void convert_peak_to_tosun(TPCANMsgFD* peak_msg, TLIBCANFD* tosun_msg) { tosun_msg->FIdxChn = 0; // 채널 0 tosun_msg->FIdentifier = peak_msg->ID; tosun_msg->FDLC = peak_msg->DLC; tosun_msg->FProperties.bits.istx = 1; // TX 메시지 tosun_msg->FFDProperties.bits.EDL = 1; // CAN-FD tosun_msg->FFDProperties.bits.BRS = 1; // BRS 활성화 memcpy(tosun_msg->FData, peak_msg->DATA, 64); }convert_peak_to_tosun()은 파일에서 데이터를 읽어서 먼더 PEAK형식으로 CAN 메시지를 만들고, 이 메시지를 TOSUN 형식으로 바꾸는 것 같습니다. 파일에서 데이터를 읽어서 곧바로 TOSUN 형식으로 만들면 좋겠습니다. 코드에 PEAK 관련 내용이 하나도 없으면 좋겠거든요. 이렇게 하기에 제공된 정보가 충분한가요?
Response:
네, 당신이 맞습니다! 파일에서 직접 토선 형식으로 만드는 것이 훨씬 깔끔하겠네요. 제공된 정보로 충분히 가능합니다.
기존 PEAK 코드 분석
현재
f021_DownloadImage.cpp에서 메시지를 만드는 부분:// PEAK 방식 TPCANMsgFD MessageBuffer; MessageBuffer.MSGTYPE = PCAN_MESSAGE_STANDARD|PCAN_MESSAGE_FD|PCAN_MESSAGE_BRS; MessageBuffer.ID = 0x1; MessageBuffer.DLC = 0xF; //64 bytes // 파일에서 64바이트 읽어서 DATA에 저장 for (counter = 0; counter < 64; counter++) { MessageBuffer.DATA[counter] = sendData[counter]; } CAN_WriteFD(PCAN_USBBUS1, &MessageBuffer);토선 직접 방식으로 변환
// 토선 방식 - PEAK 없이 직접 생성 TLIBCANFD MessageBuffer; // 메시지 구조체 초기화 memset(&MessageBuffer, 0, sizeof(TLIBCANFD)); // 메시지 설정 MessageBuffer.FIdxChn = 0; // 채널 0 MessageBuffer.FIdentifier = 0x1; // CAN ID = 0x1 MessageBuffer.FDLC = 15; // DLC = 15 (64바이트) MessageBuffer.FProperties.bits.istx = 1; // TX 메시지 MessageBuffer.FProperties.bits.extframe = 0; // 표준 프레임 MessageBuffer.FProperties.bits.remoteframe = 0; // 데이터 프레임 MessageBuffer.FFDProperties.bits.EDL = 1; // CAN-FD 활성화 MessageBuffer.FFDProperties.bits.BRS = 1; // BRS 활성화 MessageBuffer.FFDProperties.bits.ESI = 0; // ESI 비활성화 // 파일에서 64바이트를 직접 토선 메시지에 저장 for (counter = 0; counter < 64; counter++) { MessageBuffer.FData[counter] = sendData[counter]; } // 토선 API로 전송 result = tscan_transmit_canfd_async(deviceHandle, &MessageBuffer);완전히 PEAK 제거한 함수
// 파일 데이터로 토선 CAN-FD 메시지 생성 및 전송 int create_and_send_tosun_message(size_t deviceHandle, unsigned char* data, int dataSize) { TLIBCANFD MessageBuffer; // 구조체 초기화 memset(&MessageBuffer, 0, sizeof(TLIBCANFD)); // 메시지 설정 MessageBuffer.FIdxChn = 0; // 채널 0 MessageBuffer.FIdentifier = 0x1; // CAN ID = 0x1 MessageBuffer.FDLC = 15; // DLC = 15 (64바이트) MessageBuffer.FProperties.bits.istx = 1; // TX 메시지 MessageBuffer.FProperties.bits.extframe = 0; // 표준 프레임 MessageBuffer.FProperties.bits.remoteframe = 0; // 데이터 프레임 MessageBuffer.FFDProperties.bits.EDL = 1; // CAN-FD 활성화 MessageBuffer.FFDProperties.bits.BRS = 1; // BRS 활성화 MessageBuffer.FFDProperties.bits.ESI = 0; // ESI 비활성화 // 데이터 복사 (최대 64바이트) int copySize = (dataSize > 64) ? 64 : dataSize; memcpy(MessageBuffer.FData, data, copySize); // 남은 공간은 0으로 패딩 if (copySize < 64) { memset(&MessageBuffer.FData[copySize], 0, 64 - copySize); } // 토선 API로 전송 return tscan_transmit_canfd_async(deviceHandle, &MessageBuffer); }네, 제공된 정보로 충분합니다! 이렇게 하면 PEAK 관련 코드를 완전히 제거하고 토선 API만 사용할 수 있습니다.
다음 단계로 실제
ts_can_flash_programmer.c파일을 작성해볼까요?Prompt:
함수 이름에 create_and_send_tosun_message() 처럼 tosun이 있는 경우, tosun을 제거해 주십시오. 이제 코드에 peak 관련 언급이 없으니, tosun을 따로 이야기 할 필요가 없을 것 같습니다.
빠진 요구 조건이 더 없나요? 현재까지 요구 조건들을 정리해봐 주시겠습니까? 그리고 코드 작성 시작을 정하죠.
Response:
네, 맞습니다! 이제 토선만 사용하니까 굳이 "tosun"을 명시할 필요가 없겠네요.
// 수정된 함수명 int create_and_send_canfd_message(size_t deviceHandle, unsigned char* data, int dataSize)현재까지 요구사항 정리
기본 요구사항
- 언어: C언어로 작성 (기존은 C++)
- 프로그램명:
ts_can_flash_programmer - 하드웨어: 토선(Tosun) CAN 하드웨어 사용 (기존은 PEAK)
- 기능: TI 칩의 CAN 통신 기반 플래시 프로그래밍
명령행 옵션
-d <device>: 디바이스 타입 (f28003x, f28p65x)-k <file>: 커널 파일 경로-a <file>: 애플리케이션 파일 경로-t <delay>: 새로 추가 - 전송 지연 시간(ms) 설정-v: verbose 모드-q: quiet 모드-w: 종료 전 키 입력 대기-? / -h: 도움말
동작 흐름
- CAN-FD 초기화 (1Mbps nominal, 2Mbps data)
- 커널 파일 전송 (64바이트씩, CAN ID 0x1)
- 5초 대기 (커널 부팅)
- 애플리케이션 파일 전송 (64바이트씩, 전송 간격 조절 가능)
전송 지연 설정
-t옵션 없음: 기본값 (f28003x=100ms, f28p65x=1ms)-t <숫자>: 사용자 지정 지연 시간-t 0: 지연 없이 최대 속도
에러 처리
- 1단계: 에러 처리 없이 최소 구현
- 2단계: 테스트 후 필요시 에러 경고/복구 기능 추가
빠진 요구사항 확인
혹시 이런 부분들은 어떻게 하실 건가요?
- 디바이스 선택: 토선 장치가 여러 개 연결된 경우 첫 번째 장치 사용?
- 파일 형식: SCI8 형식 그대로 유지?
- 출력 메시지: 기존처럼 전송된 바이트를 콘솔에 출력?
- 종료 코드: 성공/실패 시 반환값?
이 정도면 요구사항이 명확한 것 같습니다.
코드 작성을 시작할까요?
어느 순서로 진행하시겠습니까?
main함수와 명령행 파싱부터- 토선 CAN 초기화 함수부터
- 파일 전송 로직부터
Prompt:
- 디바이스 선택 관련해서요. 토선 장치가 여러 개인 경우, 사용자 입력을 받도록 합시다. 1. TC1012P, 2. TC1013, ... 이렇게 각 줄에 장치 하나를 표시하고, 사용자가 2를 입력하면 TC1013을 연결하는 방식으로요.
- 파일 형식은 SCI8을 그대로 유지합시다. 이걸 변경하면 안 될 것 같아요.
- 출력 메시지: can_flash_programmer의 단점이 이것이더군요. 출력이 지저분 합니다. 8 바이트씩 한 줄에 출력하면 좋겠습니다.
- 종료 코드는 ... 성공하면 0, 실패하면 오류 코드들이 있어야 하는데 ... can_flash_programmer에서 정한 오류 코드들이 있나요?
Response:
좋은 요구사항들이네요! 하나씩 확인해보겠습니다.
4. 종료 코드 분석
기존
can_flash_programmer.cpp를 보면:// 파일 열기 실패 return(10); // 일반적인 성공 return 0; // setDeviceName()에서 ExitApp(2); // 잘못된 디바이스 이름 ExitApp(7); // 디바이스 이름 인식 실패기존 오류 코드들:
0: 성공2: 잘못된 디바이스 이름7: 디바이스 이름 인식 실패10: 파일 열기 실패
추가로 필요한 오류 코드들:
3: CAN 초기화 실패4: 디바이스 연결 실패5: 파일 전송 실패6: 사용자 입력 취소
3. 출력 메시지 개선
기존 출력:
0x1 0x2 0x3 ...개선된 출력:
Kernel data: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 ... App data: 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20업데이트된 요구사항 정리
디바이스 선택
Found 3 TSCAN devices: 1. TC1012P (Serial: 123456) 2. TC1013 (Serial: 789012) 3. TC1016 (Serial: 345678) Select device (1-3): 2 Connecting to TC1013...출력 형식
- 8바이트씩 한 줄에 16진수로 표시
- 커널/앱 구분해서 레이블 표시
- verbose 모드에서만 상세 출력
종료 코드
0: 성공2: 잘못된 디바이스 이름3: CAN 초기화 실패4: 디바이스 연결 실패5: 파일 전송 실패6: 사용자 입력 취소7: 디바이스 인식 실패10: 파일 열기 실패
코드 작성 순서
1단계: main 함수와 명령행 파싱
- 명령행 옵션 처리 (-t 옵션 포함)
- 도움말 출력
- 기본 구조 설정
어떤가요? 이 요구사항들로 1단계부터 시작할까요?
Prompt:
"디바이스" 라는 단어가 여러 번 나옵니다. 어떤 때는 칩을 의미하고 어떤 때는 CAN 인터페이스 하드웨어를 의마하는 것 같기도 합니다. 사실 내가 지금 정확히 구분하지 못하고 있습니다. 이런 구분이 필요한 상황인가요? 필요하다면 칩을 "디바이스"로 CAN 인터페이스 하드웨어를 "CAN 인터페이스"로 표시해 주십시오.
Response:
네, 정확한 구분이 필요합니다! 혼동을 피하기 위해 명확히 정리하겠습니다.
용어 정리
디바이스 (Device)
- TI 칩 (f28003x, f28p65x 등)
- 플래시 메모리에 프로그램을 써넣을 대상
- CAN 통신으로 데이터를 받아서 자신의 플래시에 저장
CAN 인터페이스 (CAN Interface)
- 토선 하드웨어 (TC1012P, TC1013, TC1016 등)
- PC와 TI 칩 사이의 통신 도구
- USB로 PC에 연결되고, CAN으로 TI 칩과 통신
수정된 요구사항
CAN 인터페이스 선택
Found 3 CAN interfaces: 1. TC1012P (Serial: 123456) 2. TC1013 (Serial: 789012) 3. TC1016 (Serial: 345678) Select CAN interface (1-3): 2 Connecting to TC1013...명령행 옵션
-d <device>: 디바이스 타입 (f28003x, f28p65x) - TI 칩 종류-k <file>: 커널 파일 (디바이스에 전송할 파일)-a <file>: 애플리케이션 파일 (디바이스에 전송할 파일)
종료 코드
3: CAN 인터페이스 초기화 실패4: CAN 인터페이스 연결 실패7: 디바이스 이름 인식 실패 (TI 칩 종류)
출력 메시지
Initializing CAN interface... Connecting to CAN interface TC1013... Configuring CAN-FD for device f28003x... Sending kernel to device... Kernel data: 01 02 03 04 05 06 07 08 Sending application to device... App data: 11 12 13 14 15 16 17 18이렇게 구분하면 명확해지나요? 이제 코드 작성을 시작할까요?
Prompt:
넵 코드 작성을 부탁합니다.
Response:
좋습니다! 단계적으로 코드를 작성하겠습니다.
ts_can_flash_programmer.c
//########################################################################### // FILE: ts_can_flash_programmer.c // TITLE: CAN Flash Programmer for firmware upgrades through CAN-FD (host). // Using TOSUN CAN interface hardware. // //########################################################################### #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <windows.h> #include <conio.h> // TOSUN 라이브러리 헤더 (토선 예제에서 가져온 정의들) #define byte unsigned char #define TSAPI(ret) __declspec(dllimport) ret __stdcall typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef int32_t s32; typedef enum { CHN1, CHN2, CHN3, CHN4, CHN5, CHN6, CHN7, CHN8, CHN9, CHN10, CHN11, CHN12, CHN13, CHN14, CHN15, CHN16, CHN17, CHN18, CHN19, CHN20, CHN21, CHN22, CHN23, CHN24, CHN25, CHN26, CHN27, CHN28, CHN29, CHN30, CHN31, CHN32 } APP_CHANNEL; typedef enum { lfdtCAN = 0, lfdtISOCAN = 1, lfdtNonISOCAN = 2 } TLIBCANFDControllerType; typedef enum { lfdmNormal = 0, lfdmACKOff = 1, lfdmRestricted = 2 } TLIBCANFDControllerMode; typedef union { u8 value; struct { u8 istx : 1; u8 remoteframe : 1; u8 extframe : 1; u8 tbd : 4; u8 iserrorframe : 1; } bits; } TCANProperty; typedef union { u8 value; struct { u8 EDL : 1; u8 BRS : 1; u8 ESI : 1; u8 tbd : 5; } bits; } TCANFDProperty; typedef struct _TLIBCANFD { u8 FIdxChn; TCANProperty FProperties; u8 FDLC; TCANFDProperty FFDProperties; s32 FIdentifier; uint64_t FTimeUS; u8 FData[64]; } TLIBCANFD, *PLibCANFD; // TOSUN API 함수 선언 TSAPI(void) initialize_lib_tscan(int AEnableFIFO, int AEnableErrorFrame, int AUseHWTime); TSAPI(void) finalize_lib_tscan(void); TSAPI(u32) tscan_scan_devices(uint32_t* ADeviceCount); TSAPI(u32) tscan_get_device_info(const s32 ADeviceIndex, char** AFManufacturer, char** AFProduct, char** AFSerial); TSAPI(u32) tscan_connect(const char* ADeviceSerial, size_t* AHandle); TSAPI(u32) tscan_disconnect_by_handle(const size_t ADeviceHandle); TSAPI(u32) tscan_config_canfd_by_baudrate(const size_t ADeviceHandle, const APP_CHANNEL AChnIdx, const double AArbRateKbps, const double ADataRateKbps, const TLIBCANFDControllerType AControllerType, const TLIBCANFDControllerMode AControllerMode, const u32 A120OhmConnected); TSAPI(u32) tscan_transmit_canfd_async(const size_t ADeviceHandle, const TLIBCANFD* ACANFD); //***************************************************************************** // 종료 코드 정의 //***************************************************************************** #define EXIT_SUCCESS 0 // 성공 #define EXIT_INVALID_DEVICE_NAME 2 // 잘못된 디바이스 이름 #define EXIT_CAN_INIT_FAILED 3 // CAN 인터페이스 초기화 실패 #define EXIT_CAN_CONNECT_FAILED 4 // CAN 인터페이스 연결 실패 #define EXIT_FILE_TRANSFER_FAILED 5 // 파일 전송 실패 #define EXIT_USER_CANCELLED 6 // 사용자 취소 #define EXIT_DEVICE_NOT_RECOGNIZED 7 // 디바이스 인식 실패 #define EXIT_FILE_OPEN_FAILED 10 // 파일 열기 실패 //***************************************************************************** // 글로벌 변수 //***************************************************************************** int g_bVerbose = 0; int g_bQuiet = 0; int g_bWaitOnExit = 0; int g_iTransmitDelay = -1; // -1이면 기본값 사용 // 디바이스 타입 int g_bf28003x = 0; int g_bf28p65x = 0; int g_bf021 = 0; // 파일 경로 char* g_pszAppFile = NULL; char* g_pszKernelFile = NULL; char* g_pszDeviceName = NULL; // CAN 인터페이스 핸들 size_t g_deviceHandle = 0; //***************************************************************************** // 함수 프로토타입 //***************************************************************************** void ExitApp(int iRetcode); void PrintWelcome(void); void ShowHelp(void); int ParseCommandLine(int argc, char* argv[]); int SetDeviceName(void); int ScanAndSelectCANInterface(void); int InitializeCANInterface(void); int TransferFiles(void); //***************************************************************************** // 출력 매크로 //***************************************************************************** #define VERBOSEPRINT(fmt, ...) if(g_bVerbose) { printf(fmt, ##__VA_ARGS__); } #define QUIETPRINT(fmt, ...) if(!g_bQuiet) { printf(fmt, ##__VA_ARGS__); } //***************************************************************************** // 메인 함수 //***************************************************************************** int main(int argc, char* argv[]) { int result = 0; // 명령행 파싱 if (ParseCommandLine(argc, argv) != 0) { return EXIT_INVALID_DEVICE_NAME; } // 웰컴 메시지 출력 PrintWelcome(); // TOSUN 라이브러리 초기화 VERBOSEPRINT("Initializing TSCAN library...\n"); initialize_lib_tscan(1, 0, 0); // FIFO 활성화, 에러프레임 비활성화, HW 타이머 비활성화 do { // CAN 인터페이스 검색 및 선택 result = ScanAndSelectCANInterface(); if (result != 0) break; // CAN 인터페이스 초기화 result = InitializeCANInterface(); if (result != 0) break; // 파일 전송 result = TransferFiles(); if (result != 0) break; } while (0); // 정리 if (g_deviceHandle != 0) { VERBOSEPRINT("Disconnecting from CAN interface...\n"); tscan_disconnect_by_handle(g_deviceHandle); } VERBOSEPRINT("Finalizing TSCAN library...\n"); finalize_lib_tscan(); // 결과 출력 if (result == 0) { QUIETPRINT("Flash programming completed successfully.\n"); } else { QUIETPRINT("Flash programming failed with error code %d.\n", result); } ExitApp(result); return result; } //***************************************************************************** // 프로그램 종료 //***************************************************************************** void ExitApp(int iRetcode) { if (g_bWaitOnExit) { printf("\nPress any key to exit...\n"); while (!_kbhit()) { // 키 입력 대기 } } exit(iRetcode); } //***************************************************************************** // 웰컴 배너 //***************************************************************************** void PrintWelcome(void) { if (g_bQuiet) { return; } printf("\nC2000 CAN Firmware Upgrader (TOSUN Version)\n"); printf("Copyright (c) 2024 Texas Instruments Incorporated. All rights reserved.\n\n"); } //***************************************************************************** // 도움말 출력 //***************************************************************************** void ShowHelp(void) { if (g_bQuiet) { return; } printf("This application may be used to download images to a Texas Instruments\n"); printf("C2000 microcontroller in the CAN boot mode using TOSUN CAN interface.\n\n"); printf("Supported parameters are:\n\n"); printf("-d <device> - The name of the device to load to:\n"); printf(" f28003x, f28p65x\n"); printf("-k <file> - The file name for flash kernel.\n"); printf(" This file must be in the sci8 boot format.\n"); printf("-a <file> - The file name for application download.\n"); printf(" This file must be in the sci8 boot format.\n"); printf("-t <delay> - Transmit delay in milliseconds between CAN frames\n"); printf(" (default: f28003x=100ms, f28p65x=1ms)\n"); printf("-? or -h - Show this help.\n"); printf("-q - Quiet mode. Disable output to stdout.\n"); printf("-w - Wait for a key press before exiting.\n"); printf("-v - Enable verbose output\n\n"); printf("-d is a mandatory parameter.\n\n"); printf("Application and kernel files must be in the SCI8 ascii boot format.\n"); printf("These can be generated using the hex2000 utility. An example of how to do\n"); printf("this follows:\nhex2000 application.out -boot -sci8 -a -o application.txt\n\n"); } //***************************************************************************** // 명령행 파싱 //***************************************************************************** int ParseCommandLine(int argc, char* argv[]) { int iParm; int bShowHelp = 0; char* pcOptArg; // 각 파라미터 처리 for (iParm = 1; iParm < argc; iParm++) { // 스위치 유효성 검사 if (!argv || ((argv[iParm][0] != '-') && (argv[iParm][0] != '/')) || (argv[iParm][1] == '\0')) { printf("Unrecognized or invalid argument: %s\n", argv[iParm]); return -1; } // 다음 인수 가져오기 pcOptArg = ((iParm + 1) < argc) ? argv[iParm + 1] : NULL; switch (argv[iParm][1]) { case 'd': // 디바이스 이름 g_pszDeviceName = pcOptArg; iParm++; if (SetDeviceName() != 0) { return -1; } break; case 'k': // 커널 파일 g_pszKernelFile = pcOptArg; iParm++; break; case 'a': // 애플리케이션 파일 g_pszAppFile = pcOptArg; iParm++; break; case 't': // 전송 지연 if (pcOptArg == NULL) { printf("Missing delay value for -t option\n"); return -1; } g_iTransmitDelay = atoi(pcOptArg); if (g_iTransmitDelay < 0) { printf("Invalid transmit delay: %s\n", pcOptArg); return -1; } iParm++; break; case 'v': // Verbose g_bVerbose = 1; break; case 'q': // Quiet g_bQuiet = 1; break; case 'w': // Wait on exit g_bWaitOnExit = 1; break; case '?': case 'h': // Help bShowHelp = 1; break; default: printf("Unrecognized argument: %s\n\n", argv[iParm]); ShowHelp(); return -1; } } // 도움말 출력 if (bShowHelp) { ShowHelp(); ExitApp(EXIT_SUCCESS); } // 필수 파라미터 검사 if (!g_pszDeviceName) { printf("Error: Device name (-d) is required.\n\n"); ShowHelp(); return -1; } return 0; } //***************************************************************************** // 디바이스 이름 설정 //***************************************************************************** int SetDeviceName(void) { if (strcmp(g_pszDeviceName, "f28003x") == 0) { g_bf021 = 1; g_bf28003x = 1; } else if (strcmp(g_pszDeviceName, "f28p65x") == 0) { g_bf021 = 1; g_bf28p65x = 1; } else { printf("\nUnrecognized device name: %s\n\n", g_pszDeviceName); ShowHelp(); return EXIT_DEVICE_NOT_RECOGNIZED; } // 전송 지연 기본값 설정 if (g_iTransmitDelay == -1) { if (g_bf28003x) { g_iTransmitDelay = 100; // f28003x: 100ms } else if (g_bf28p65x) { g_iTransmitDelay = 1; // f28p65x: 1ms } } return 0; } //***************************************************************************** // CAN 인터페이스 검색 및 선택 //***************************************************************************** int ScanAndSelectCANInterface(void) { uint32_t deviceCount = 0; u32 result; int selection; char* manufacturer = NULL; char* product = NULL; char* serial = NULL; // 디바이스 검색 QUIETPRINT("Scanning for CAN interfaces...\n"); result = tscan_scan_devices(&deviceCount); if (result != 0) { QUIETPRINT("Error: Failed to scan CAN interfaces. Error code: %u\n", result); return EXIT_CAN_CONNECT_FAILED; } if (deviceCount == 0) { QUIETPRINT("Error: No CAN interfaces found.\n"); return EXIT_CAN_CONNECT_FAILED; } QUIETPRINT("Found %u CAN interface(s):\n", deviceCount); // 디바이스 목록 출력 for (int i = 0; i < (int)deviceCount; i++) { result = tscan_get_device_info(i, &manufacturer, &product, &serial); if (result == 0) { QUIETPRINT("%d. %s %s (Serial: %s)\n", i + 1, manufacturer, product, serial); } else { QUIETPRINT("%d. Unknown device (Index: %d)\n", i + 1, i); } } // 사용자 선택 if (deviceCount == 1) { selection = 1; QUIETPRINT("Automatically selecting the only available CAN interface.\n"); } else { printf("Select CAN interface (1-%u): ", deviceCount); if (scanf("%d", &selection) != 1 || selection < 1 || selection > (int)deviceCount) { QUIETPRINT("Error: Invalid selection.\n"); return EXIT_USER_CANCELLED; } } // 선택된 디바이스 연결 result = tscan_get_device_info(selection - 1, &manufacturer, &product, &serial); if (result == 0) { QUIETPRINT("Connecting to %s %s (Serial: %s)...\n", manufacturer, product, serial); result = tscan_connect(serial, &g_deviceHandle); } else { QUIETPRINT("Connecting to CAN interface %d...\n", selection); result = tscan_connect("", &g_deviceHandle); // 빈 문자열로 기본 연결 시도 } if (result != 0) { QUIETPRINT("Error: Failed to connect to CAN interface. Error code: %u\n", result); return EXIT_CAN_CONNECT_FAILED; } QUIETPRINT("Successfully connected to CAN interface. Handle: %zu\n", g_deviceHandle); return 0; } //***************************************************************************** // CAN 인터페이스 초기화 //***************************************************************************** int InitializeCANInterface(void) { u32 result; VERBOSEPRINT("Configuring CAN-FD for device %s...\n", g_pszDeviceName); VERBOSEPRINT("Nominal bit rate: 1000 kbps, Data bit rate: 2000 kbps\n"); // CAN-FD 설정: 1Mbps nominal, 2Mbps data result = tscan_config_canfd_by_baudrate( g_deviceHandle, // 디바이스 핸들 CHN1, // 채널 1 1000.0, // 1000 kbps nominal 2000.0, // 2000 kbps data lfdtISOCAN, // ISO CAN-FD lfdmNormal, // Normal mode 1 // 120 ohm 터미네이션 활성화 ); if (result != 0) { QUIETPRINT("Error: Failed to configure CAN-FD. Error code: %u\n", result); return EXIT_CAN_INIT_FAILED; } VERBOSEPRINT("CAN-FD configuration completed successfully.\n"); return 0; } //***************************************************************************** // 파일 전송 (플레이스홀더 - 다음 단계에서 구현) //***************************************************************************** int TransferFiles(void) { QUIETPRINT("File transfer function not implemented yet.\n"); return 0; }1단계 코드를 완성했습니다! 주요 특징:
구현된 기능들
✅ 명령행 파싱
-d,-k,-a,-t,-v,-q,-w,-h옵션 모두 지원-t옵션으로 전송 지연 시간 설정 가능- 기본값: f28003x=100ms, f28p65x=1ms
✅ CAN 인터페이스 선택
- 여러 장치 발견 시 사용자가 선택
- 장치 정보 표시 (제조사, 제품명, 시리얼)
- 1개만 있으면 자동 선택
✅ 용어 정리
- "디바이스" = TI 칩 (f28003x, f28p65x)
- "CAN 인터페이스" = 토선 하드웨어 (TC1013 등)
✅ 종료 코드 체계
- 0: 성공, 2: 잘못된 디바이스 이름, 3: CAN 초기화 실패 등
다음 단계
지금
TransferFiles()함수가 플레이스홀더 상태입니다.2단계로 이 함수를 구현할까요?
이 함수에서 해야 할 일:
- 커널 파일 읽기 및 전송
- 5초 대기
- 애플리케이션 파일 읽기 및 전송
- 개선된 출력 형식 (8바이트씩 한 줄)
계속 진행하시겠습니까?
Prompt:
예 부탁합니다.
Response:
ts_can_flash_programmer.c ∙ Version 3
//########################################################################### // FILE: ts_can_flash_programmer.c // TITLE: CAN Flash Programmer for firmware upgrades through CAN-FD (host). // Using TOSUN CAN interface hardware. // //########################################################################### #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <windows.h> #include <conio.h> // TOSUN 라이브러리 헤더 (토선 예제에서 가져온 정의들) #define byte unsigned char #define TSAPI(ret) __declspec(dllimport) ret __stdcall typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef int32_t s32; typedef enum { CHN1, CHN2, CHN3, CHN4, CHN5, CHN6, CHN7, CHN8, CHN9, CHN10, CHN11, CHN12, CHN13, CHN14, CHN15, CHN16, CHN17, CHN18, CHN19, CHN20, CHN21, CHN22, CHN23, CHN24, CHN25, CHN26, CHN27, CHN28, CHN29, CHN30, CHN31, CHN32 } APP_CHANNEL; typedef enum { lfdtCAN = 0, lfdtISOCAN = 1, lfdtNonISOCAN = 2 } TLIBCANFDControllerType; typedef enum { lfdmNormal = 0, lfdmACKOff = 1, lfdmRestricted = 2 } TLIBCANFDControllerMode; typedef union { u8 value; struct { u8 istx : 1; u8 remoteframe : 1; u8 extframe : 1; u8 tbd : 4; u8 iserrorframe : 1; } bits; } TCANProperty; typedef union { u8 value; struct { u8 EDL : 1; u8 BRS : 1; u8 ESI : 1; u8 tbd : 5; } bits; } TCANFDProperty; typedef struct _TLIBCANFD { u8 FIdxChn; TCANProperty FProperties; u8 FDLC; TCANFDProperty FFDProperties; s32 FIdentifier; uint64_t FTimeUS; u8 FData[64]; } TLIBCANFD, *PLibCANFD; // TOSUN API 함수 선언 TSAPI(void) initialize_lib_tscan(int AEnableFIFO, int AEnableErrorFrame, int AUseHWTime); TSAPI(void) finalize_lib_tscan(void); TSAPI(u32) tscan_scan_devices(uint32_t* ADeviceCount); TSAPI(u32) tscan_get_device_info(const s32 ADeviceIndex, char** AFManufacturer, char** AFProduct, char** AFSerial); TSAPI(u32) tscan_connect(const char* ADeviceSerial, size_t* AHandle); TSAPI(u32) tscan_disconnect_by_handle(const size_t ADeviceHandle); TSAPI(u32) tscan_config_canfd_by_baudrate(const size_t ADeviceHandle, const APP_CHANNEL AChnIdx, const double AArbRateKbps, const double ADataRateKbps, const TLIBCANFDControllerType AControllerType, const TLIBCANFDControllerMode AControllerMode, const u32 A120OhmConnected); TSAPI(u32) tscan_transmit_canfd_async(const size_t ADeviceHandle, const TLIBCANFD* ACANFD); //***************************************************************************** // 종료 코드 정의 //***************************************************************************** #define EXIT_SUCCESS 0 // 성공 #define EXIT_INVALID_DEVICE_NAME 2 // 잘못된 디바이스 이름 #define EXIT_CAN_INIT_FAILED 3 // CAN 인터페이스 초기화 실패 #define EXIT_CAN_CONNECT_FAILED 4 // CAN 인터페이스 연결 실패 #define EXIT_FILE_TRANSFER_FAILED 5 // 파일 전송 실패 #define EXIT_USER_CANCELLED 6 // 사용자 취소 #define EXIT_DEVICE_NOT_RECOGNIZED 7 // 디바이스 인식 실패 #define EXIT_FILE_OPEN_FAILED 10 // 파일 열기 실패 //***************************************************************************** // 글로벌 변수 //***************************************************************************** int g_bVerbose = 0; int g_bQuiet = 0; int g_bWaitOnExit = 0; int g_iTransmitDelay = -1; // -1이면 기본값 사용 // 디바이스 타입 int g_bf28003x = 0; int g_bf28p65x = 0; int g_bf021 = 0; // 파일 경로 char* g_pszAppFile = NULL; char* g_pszKernelFile = NULL; char* g_pszDeviceName = NULL; // CAN 인터페이스 핸들 size_t g_deviceHandle = 0; //***************************************************************************** // 함수 프로토타입 //***************************************************************************** void ExitApp(int iRetcode); void PrintWelcome(void); void ShowHelp(void); int ParseCommandLine(int argc, char* argv[]); int SetDeviceName(void); int ScanAndSelectCANInterface(void); int InitializeCANInterface(void); int TransferFiles(void); int CreateAndSendCANFDMessage(unsigned char* data, int dataSize); void PrintDataBytes(const char* label, unsigned char* data, int size); int ReadFileChunk(FILE* file, unsigned char* buffer, int* bytesRead); int TransferSingleFile(const char* filename, const char* fileType); //***************************************************************************** // 출력 매크로 //***************************************************************************** #define VERBOSEPRINT(fmt, ...) if(g_bVerbose) { printf(fmt, ##__VA_ARGS__); } #define QUIETPRINT(fmt, ...) if(!g_bQuiet) { printf(fmt, ##__VA_ARGS__); } //***************************************************************************** // 메인 함수 //***************************************************************************** int main(int argc, char* argv[]) { int result = 0; // 명령행 파싱 if (ParseCommandLine(argc, argv) != 0) { return EXIT_INVALID_DEVICE_NAME; } // 웰컴 메시지 출력 PrintWelcome(); // TOSUN 라이브러리 초기화 VERBOSEPRINT("Initializing TSCAN library...\n"); initialize_lib_tscan(1, 0, 0); // FIFO 활성화, 에러프레임 비활성화, HW 타이머 비활성화 do { // CAN 인터페이스 검색 및 선택 result = ScanAndSelectCANInterface(); if (result != 0) break; // CAN 인터페이스 초기화 result = InitializeCANInterface(); if (result != 0) break; // 파일 전송 result = TransferFiles(); if (result != 0) break; } while (0); // 정리 if (g_deviceHandle != 0) { VERBOSEPRINT("Disconnecting from CAN interface...\n"); tscan_disconnect_by_handle(g_deviceHandle); } VERBOSEPRINT("Finalizing TSCAN library...\n"); finalize_lib_tscan(); // 결과 출력 if (result == 0) { QUIETPRINT("Flash programming completed successfully.\n"); } else { QUIETPRINT("Flash programming failed with error code %d.\n", result); } ExitApp(result); return result; } //***************************************************************************** // 프로그램 종료 //***************************************************************************** void ExitApp(int iRetcode) { if (g_bWaitOnExit) { printf("\nPress any key to exit...\n"); while (!_kbhit()) { // 키 입력 대기 } } exit(iRetcode); } //***************************************************************************** // 웰컴 배너 //***************************************************************************** void PrintWelcome(void) { if (g_bQuiet) { return; } printf("\nC2000 CAN Firmware Upgrader (TOSUN Version)\n"); printf("Copyright (c) 2024 Texas Instruments Incorporated. All rights reserved.\n\n"); } //***************************************************************************** // 도움말 출력 //***************************************************************************** void ShowHelp(void) { if (g_bQuiet) { return; } printf("This application may be used to download images to a Texas Instruments\n"); printf("C2000 microcontroller in the CAN boot mode using TOSUN CAN interface.\n\n"); printf("Supported parameters are:\n\n"); printf("-d <device> - The name of the device to load to:\n"); printf(" f28003x, f28p65x\n"); printf("-k <file> - The file name for flash kernel.\n"); printf(" This file must be in the sci8 boot format.\n"); printf("-a <file> - The file name for application download.\n"); printf(" This file must be in the sci8 boot format.\n"); printf("-t <delay> - Transmit delay in milliseconds between CAN frames\n"); printf(" (default: f28003x=100ms, f28p65x=1ms)\n"); printf("-? or -h - Show this help.\n"); printf("-q - Quiet mode. Disable output to stdout.\n"); printf("-w - Wait for a key press before exiting.\n"); printf("-v - Enable verbose output\n\n"); printf("-d is a mandatory parameter.\n\n"); printf("Application and kernel files must be in the SCI8 ascii boot format.\n"); printf("These can be generated using the hex2000 utility. An example of how to do\n"); printf("this follows:\nhex2000 application.out -boot -sci8 -a -o application.txt\n\n"); } //***************************************************************************** // 명령행 파싱 //***************************************************************************** int ParseCommandLine(int argc, char* argv[]) { int iParm; int bShowHelp = 0; char* pcOptArg; // 각 파라미터 처리 for (iParm = 1; iParm < argc; iParm++) { // 스위치 유효성 검사 if (!argv || ((argv[iParm][0] != '-') && (argv[iParm][0] != '/')) || (argv[iParm][1] == '\0')) { printf("Unrecognized or invalid argument: %s\n", argv[iParm]); return -1; } // 다음 인수 가져오기 pcOptArg = ((iParm + 1) < argc) ? argv[iParm + 1] : NULL; switch (argv[iParm][1]) { case 'd': // 디바이스 이름 g_pszDeviceName = pcOptArg; iParm++; if (SetDeviceName() != 0) { return -1; } break; case 'k': // 커널 파일 g_pszKernelFile = pcOptArg; iParm++; break; case 'a': // 애플리케이션 파일 g_pszAppFile = pcOptArg; iParm++; break; case 't': // 전송 지연 if (pcOptArg == NULL) { printf("Missing delay value for -t option\n"); return -1; } g_iTransmitDelay = atoi(pcOptArg); if (g_iTransmitDelay < 0) { printf("Invalid transmit delay: %s\n", pcOptArg); return -1; } iParm++; break; case 'v': // Verbose g_bVerbose = 1; break; case 'q': // Quiet g_bQuiet = 1; break; case 'w': // Wait on exit g_bWaitOnExit = 1; break; case '?': case 'h': // Help bShowHelp = 1; break; default: printf("Unrecognized argument: %s\n\n", argv[iParm]); ShowHelp(); return -1; } } // 도움말 출력 if (bShowHelp) { ShowHelp(); ExitApp(EXIT_SUCCESS); } // 필수 파라미터 검사 if (!g_pszDeviceName) { printf("Error: Device name (-d) is required.\n\n"); ShowHelp(); return -1; } return 0; } //***************************************************************************** // 디바이스 이름 설정 //***************************************************************************** int SetDeviceName(void) { if (strcmp(g_pszDeviceName, "f28003x") == 0) { g_bf021 = 1; g_bf28003x = 1; } else if (strcmp(g_pszDeviceName, "f28p65x") == 0) { g_bf021 = 1; g_bf28p65x = 1; } else { printf("\nUnrecognized device name: %s\n\n", g_pszDeviceName); ShowHelp(); return EXIT_DEVICE_NOT_RECOGNIZED; } // 전송 지연 기본값 설정 if (g_iTransmitDelay == -1) { if (g_bf28003x) { g_iTransmitDelay = 100; // f28003x: 100ms } else if (g_bf28p65x) { g_iTransmitDelay = 1; // f28p65x: 1ms } } return 0; } //***************************************************************************** // CAN 인터페이스 검색 및 선택 //***************************************************************************** int ScanAndSelectCANInterface(void) { uint32_t deviceCount = 0; u32 result; int selection; char* manufacturer = NULL; char* product = NULL; char* serial = NULL; // 디바이스 검색 QUIETPRINT("Scanning for CAN interfaces...\n"); result = tscan_scan_devices(&deviceCount); if (result != 0) { QUIETPRINT("Error: Failed to scan CAN interfaces. Error code: %u\n", result); return EXIT_CAN_CONNECT_FAILED; } if (deviceCount == 0) { QUIETPRINT("Error: No CAN interfaces found.\n"); return EXIT_CAN_CONNECT_FAILED; } QUIETPRINT("Found %u CAN interface(s):\n", deviceCount); // 디바이스 목록 출력 for (int i = 0; i < (int)deviceCount; i++) { result = tscan_get_device_info(i, &manufacturer, &product, &serial); if (result == 0) { QUIETPRINT("%d. %s %s (Serial: %s)\n", i + 1, manufacturer, product, serial); } else { QUIETPRINT("%d. Unknown device (Index: %d)\n", i + 1, i); } } // 사용자 선택 if (deviceCount == 1) { selection = 1; QUIETPRINT("Automatically selecting the only available CAN interface.\n"); } else { printf("Select CAN interface (1-%u): ", deviceCount); if (scanf("%d", &selection) != 1 || selection < 1 || selection > (int)deviceCount) { QUIETPRINT("Error: Invalid selection.\n"); return EXIT_USER_CANCELLED; } } // 선택된 디바이스 연결 result = tscan_get_device_info(selection - 1, &manufacturer, &product, &serial); if (result == 0) { QUIETPRINT("Connecting to %s %s (Serial: %s)...\n", manufacturer, product, serial); result = tscan_connect(serial, &g_deviceHandle); } else { QUIETPRINT("Connecting to CAN interface %d...\n", selection); result = tscan_connect("", &g_deviceHandle); // 빈 문자열로 기본 연결 시도 } if (result != 0) { QUIETPRINT("Error: Failed to connect to CAN interface. Error code: %u\n", result); return EXIT_CAN_CONNECT_FAILED; } QUIETPRINT("Successfully connected to CAN interface. Handle: %zu\n", g_deviceHandle); return 0; } //***************************************************************************** // CAN 인터페이스 초기화 //***************************************************************************** int InitializeCANInterface(void) { u32 result; VERBOSEPRINT("Configuring CAN-FD for device %s...\n", g_pszDeviceName); VERBOSEPRINT("Nominal bit rate: 1000 kbps, Data bit rate: 2000 kbps\n"); // CAN-FD 설정: 1Mbps nominal, 2Mbps data result = tscan_config_canfd_by_baudrate( g_deviceHandle, // 디바이스 핸들 CHN1, // 채널 1 1000.0, // 1000 kbps nominal 2000.0, // 2000 kbps data lfdtISOCAN, // ISO CAN-FD lfdmNormal, // Normal mode 1 // 120 ohm 터미네이션 활성화 ); if (result != 0) { QUIETPRINT("Error: Failed to configure CAN-FD. Error code: %u\n", result); return EXIT_CAN_INIT_FAILED; } VERBOSEPRINT("CAN-FD configuration completed successfully.\n"); return 0; } //***************************************************************************** // CAN-FD 메시지 생성 및 전송 //***************************************************************************** int CreateAndSendCANFDMessage(unsigned char* data, int dataSize) { TLIBCANFD MessageBuffer; u32 result; // 구조체 초기화 memset(&MessageBuffer, 0, sizeof(TLIBCANFD)); // 메시지 설정 MessageBuffer.FIdxChn = 0; // 채널 0 MessageBuffer.FIdentifier = 0x1; // CAN ID = 0x1 MessageBuffer.FDLC = 15; // DLC = 15 (64바이트) MessageBuffer.FProperties.bits.istx = 1; // TX 메시지 MessageBuffer.FProperties.bits.extframe = 0; // 표준 프레임 MessageBuffer.FProperties.bits.remoteframe = 0; // 데이터 프레임 MessageBuffer.FFDProperties.bits.EDL = 1; // CAN-FD 활성화 MessageBuffer.FFDProperties.bits.BRS = 1; // BRS 활성화 MessageBuffer.FFDProperties.bits.ESI = 0; // ESI 비활성화 // 데이터 복사 (최대 64바이트) int copySize = (dataSize > 64) ? 64 : dataSize; memcpy(MessageBuffer.FData, data, copySize); // 남은 공간은 0으로 패딩 if (copySize < 64) { memset(&MessageBuffer.FData[copySize], 0, 64 - copySize); } // 토선 API로 전송 result = tscan_transmit_canfd_async(g_deviceHandle, &MessageBuffer); return (result == 0) ? 0 : EXIT_FILE_TRANSFER_FAILED; } //***************************************************************************** // 데이터 출력 (8바이트씩 한 줄에 표시) //***************************************************************************** void PrintDataBytes(const char* label, unsigned char* data, int size) { if (g_bQuiet) return; for (int i = 0; i < size; i++) { if (i % 8 == 0) { if (i == 0) { printf("%-12s", label); // 첫 번째 줄에 레이블 } else { printf(" "); // 다음 줄들은 공백으로 정렬 } } printf("%02X ", data[i]); if ((i + 1) % 8 == 0 || i == size - 1) { printf("\n"); } } } //***************************************************************************** // 파일에서 64바이트 읽기 //***************************************************************************** int ReadFileChunk(FILE* file, unsigned char* buffer, int* bytesRead) { unsigned int value; int count = 0; int result; *bytesRead = 0; // 64바이트 또는 파일 끝까지 읽기 for (count = 0; count < 64; count++) { result = fscanf(file, "%x", &value); if (result != 1) { break; // 파일 끝 또는 읽기 오류 } buffer[count] = (unsigned char)(value & 0xFF); } *bytesRead = count; return (result == 1 || count > 0) ? 0 : -1; // 성공 또는 부분 성공 } //***************************************************************************** // 단일 파일 전송 //***************************************************************************** int TransferSingleFile(const char* filename, const char* fileType) { FILE* file = NULL; unsigned char sendData[64]; int bytesRead; int result; int frameCount = 0; int totalBytes = 0; // 파일 열기 if (fopen_s(&file, filename, "rb") != 0 || !file) { QUIETPRINT("Error: Unable to open %s file %s. Does it exist?\n", fileType, filename); return EXIT_FILE_OPEN_FAILED; } QUIETPRINT("Sending %s to device...\n", fileType); // SCI8 형식 헤더 건너뛰기 (처음 3바이트) fgetc(file); fgetc(file); fgetc(file); // 첫 번째 청크 읽기 result = ReadFileChunk(file, sendData, &bytesRead); while (result == 0 && bytesRead > 0) { frameCount++; totalBytes += bytesRead; // 데이터 출력 (verbose 모드일 때만) if (g_bVerbose) { char label[20]; sprintf(label, "%s #%d:", fileType, frameCount); PrintDataBytes(label, sendData, bytesRead); } // CAN-FD 메시지 전송 result = CreateAndSendCANFDMessage(sendData, bytesRead); if (result != 0) { QUIETPRINT("Error: Failed to send CAN frame #%d\n", frameCount); fclose(file); return result; } // 전송 지연 if (g_iTransmitDelay > 0) { Sleep(g_iTransmitDelay); } // 다음 청크 읽기 result = ReadFileChunk(file, sendData, &bytesRead); } fclose(file); QUIETPRINT("%s transfer completed: %d frames, %d bytes total\n", fileType, frameCount, totalBytes); return 0; } //***************************************************************************** // 파일 전송 메인 함수 //***************************************************************************** int TransferFiles(void) { int result = 0; // 필수 파일 존재 확인 if (!g_pszKernelFile || !g_pszAppFile) { QUIETPRINT("Error: Both kernel (-k) and application (-a) files are required.\n"); return EXIT_FILE_OPEN_FAILED; } VERBOSEPRINT("Transfer delay set to %d ms\n", g_iTransmitDelay); // 1. 커널 파일 전송 VERBOSEPRINT("\n=== Kernel Transfer Phase ===\n"); result = TransferSingleFile(g_pszKernelFile, "Kernel"); if (result != 0) { return result; } // 2. 커널 부팅 대기 (5초) QUIETPRINT("Waiting for kernel to boot (5 seconds)...\n"); Sleep(5000); VERBOSEPRINT("Kernel boot wait completed.\n"); // 3. 애플리케이션 파일 전송 VERBOSEPRINT("\n=== Application Transfer Phase ===\n"); result = TransferSingleFile(g_pszAppFile, "Application"); if (result != 0) { return result; } QUIETPRINT("All file transfers completed successfully.\n"); return 0; }완성했습니다! 2단계에서 파일 전송 기능을 구현했습니다.
새로 구현된 기능들
✅ 파일 전송 로직
- SCI8 형식 처리: 파일 시작 3바이트 건너뛰기
- 64바이트 청크: 파일을 64바이트씩 읽어서 전송
- 두 단계 전송: 커널 → 5초 대기 → 애플리케이션
✅ 개선된 출력 형식
기존 (지저분한 출력):
0x1 0x2 0x3 ...새로운 (깔끔한 출력):
Kernel #1: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 Application #1: 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20✅ 전송 제어
- 지연 시간:
-t옵션으로 설정된 시간만큼 대기 - 진행 상황: 전송된 프레임 수와 총 바이트 수 표시
- 에러 처리: 파일 열기 실패, 전송 실패 시 적절한 종료 코드
✅ Verbose 모드
-v옵션: 각 프레임의 데이터를 8바이트씩 표시- 일반 모드: 요약 정보만 표시
사용 예시
# 기본 사용법 ts_can_flash_programmer -d f28003x -k kernel.txt -a app.txt # 전송 속도 조절 (10ms 간격) ts_can_flash_programmer -d f28003x -k kernel.txt -a app.txt -t 10 # 상세 출력으로 데이터 확인 ts_can_flash_programmer -d f28003x -k kernel.txt -a app.txt -v # 최대 속도 전송 (지연 없음) ts_can_flash_programmer -d f28003x -k kernel.txt -a app.txt -t 0이제 코드가 완성되었습니다! 테스트해보시고 문제가 있으면 알려주세요. 혹시 추가로 필요한 기능이나 수정사항이 있나요?
이후에 ...
- 여기까지 개발된 코드를 vscode에서 코파일럿과 함께 추가 개선한다.
- 최종적으로 개발된 코드를 실행했다. 잘 된다.

'application' 카테고리의 다른 글
UDS 기반 제어기 프로그래머 개발 - libTSCAN (5) 2025.08.11 ts_can_flash_programmer 개발 - libTSCAN (3) 2025.07.26 XCP - 설정 (0) 2025.04.13 SecOC 구현 (0) 2025.02.11 SecOC (Secure On-board Communication) (0) 2025.02.09