feat: KT mBMS 펌웨어 3종(HW1/2/3) 및 통합 빌드 환경 초기 커밋 (V4.0.0.1)

This commit is contained in:
2026-11-05 14:31:31 +09:00
commit c52dc2f348
10803 changed files with 4642514 additions and 0 deletions

View File

@@ -0,0 +1,159 @@
# V1 / V2 / V3 소스코드 검토
검토일: 2026-11-03
| 구분 | 검토 대상 |
|---|---|
| V1 | `mBMS_APP_V3117` |
| V2 | `APP_R3221` |
| V3 | `APP_R3323` |
보호 판단 → 상태 갱신 → 릴레이 출력 경로, AFE 측정, 온도 고장 판정, CAN 설정/업데이트, 파라미터 및 SOC 저장 코드를 중심으로 정적으로 검토했다. 제조사 라이브러리 전체, GUI, 부트로더, 회로도 대조 및 실물 동작 검증은 포함하지 않는다. 최초 검토 후 사용자 요청으로 적용한 소스 수정과 검증 결과는 각 항목에 추가했다.
## 발견 사항
### 1. [정정: 결함 판정 철회] 방전 중 충전 릴레이 ON은 의도된 제어 — V1 / V2 / V3
위치:
- V1: `mBMS_APP_V3117/APP/alarm.c:1093–1095`
- V2: `APP_R3221/App/Source/alarm.c:1130–1131`
- V3: `APP_R3323/App/Source/alarm.c:1135–1136`
사용자 회로 동작 설명에 따라 최초 P1 판정을 철회한다. 충전 차단 알람이 유지되더라도 방전 중에는 충전 릴레이를 ON하여 다이오드를 통한 지속 방전 및 과열을 방지해야 한다. 방전 종료/정지 후에는 충전 차단 알람에 따라 다시 OFF해야 한다.
세 버전 모두 `Process_FetControl()` → `ProcessStatusCheck()` → `IO_Relay_Control()` 순서로 처리된다. 충전 차단 알람에 따라 우선 CHG_C가 false로 계산되더라도, 상태 판정 결과가 방전이면 CHG_C를 true로 바꿔 릴레이를 ON한다. 이 대입 자체는 알람 플래그를 지우지 않는다.
일반 운전 경로(RXM == false)에서 방전 전류가 종료 판정값 미만으로 내려가면 상태를 STANDBY로 변경한다. 해당 제어 주기에 `Process_FetControl()`이 설정한 CHG_C=false가 다시 ON으로 덮어써지지 않으므로, 유지 중인 충전 차단 알람에 따라 릴레이 OFF 명령이 출력된다. 충전 상태로 전환되는 경우에도 마지막 방전 ON 예외는 적용되지 않는다.
상태 전환 위치: V1 `alarm.c:1031–1038`, V2 `alarm.c:1079–1083`, V3 `alarm.c:1084–1088`。방전 진입은 `Discharge_Detection_Threshold / 100` 초과, 종료는 `(Discharge_Detection_Threshold - 1000) / 100` 미만으로 판정한다. 물리적 전류 정지와 출력 반영 사이에는 측정 필터 및 제어 주기 지연이 있으므로 실물 전환 시간은 별도 검증 대상이다.
권장 사항: 방전 ON 동작은 유지한다. CHG_C는 보호 알람 자체와 구분되는 실제 출력 명령임을 주석/설계 문서에 명시한다.
검증 시나리오: 충전 차단 알람을 유지한 상태에서 방전 시작 → 지속 → 정지 → 재시작을 수행한다. 릴레이 출력은 OFF → ON → ON → OFF → ON이어야 하며, 방전 중에도 충전 차단 알람은 계속 유지되어야 한다. 필터를 거친 전류가 종료 판정값 아래로 내려온 시점부터 OFF 명령까지의 지연과 실제 접점 동작을 계측한다. 이 시나리오는 실물에서 실행하지 않았다.
### 2. [수정 완료 / 실물 검증 전] 초기화되지 않은 지역 변수로 셀 측정 실행 여부를 결정함 — V3
위치: `APP_R3323/App/Source/afe_comm.c:284,311`
`ReadCellVoltage()`에서 `int8_t error;`를 선언한 뒤 값을 대입하지 않고 `if (error != -1)`에서 읽는다. 실제 대입은 이 조건문 안의 317행에서 처음 발생한다. 앞서 얻은 설정 레지스터 읽기 결과는 별도 변수 `errorA`, `errorB`에 저장되어 있다.
초기화되지 않은 값을 읽으므로 셀 ADC 변환과 데이터 읽기 경로를 신뢰할 수 없다. 셀 데이터 갱신이 건너뛰어지거나 최적화 설정에 따라 동작이 달라질 수 있다.
권장 수정: 측정 진입 조건에 사용할 결과를 명시적으로 정하고, ADC 변환 및 읽기를 실행한 뒤 그 반환값으로 성공/실패를 판단한다. 단순히 0으로 초기화하는 것과 통신 실패 처리를 연결하는 것은 별도 작업이다.
검증 시나리오: 초기화 경고를 활성화해 컴파일하고 정상 통신에서 반복 측정이 계속되는지 확인한다. 설정 읽기 실패와 셀 읽기 실패도 각각 주입해 결과 처리를 확인한다.
사용자 요청에 따라 V3 `afe_comm.c`의 미초기화 `error`를 검사하던 바깥 조건문을 제거했다. 설정 레지스터 읽기 결과에 따른 밸런싱 처리 후, 셀 전압 변환/읽기를 매번 시도하고 `ADBMS1818_rdcv()`의 실제 반환값을 확인한 뒤에만 측정값을 반영한다. 통신 실패 보호 연동은 별도 발견 사항 3의 범위로 남겨 두었다. 릴레이 및 알람 제어는 변경하지 않았다.
수정 함수 본문을 그대로 추출하고 하드웨어 선언을 대체한 ARMClang 검사에서, 수정 전 조건은 미초기화 변수 오류를 재현했고 수정 후 함수는 구문 및 미초기화 검사에 통과했다. 이후 Keil ARMCC Release 전체 빌드에서도 V3가 0 Error / 0 Warning으로 통과했고 HEX/BIN을 재생성했다. 실물 시험은 완료하지 않았다.
### 3. [P1] AFE 통신/오류 상태가 보호 로직에 연결되지 않음 — V2 / V3
위치:
- V2: `APP_R3221/App/Source/alarm.c:175–177`, `afe_comm.c:155–158`, `io.c:210–213`
- V3: `APP_R3323/App/Source/alarm.c:175–177`, `afe_comm.c:290–323`
V2의 AFE 상태 생산자는 `battery_status.bit.AFE`에 쓰지만 보호 로직은 `error.bit.AFE`를 읽는다. 검토한 App C 소스에는 `error.bit.AFE`를 갱신하는 대입이 없다. 또한 V2의 `AfeCommState.Commfail`도 초기화 이후 오류에 따라 갱신되는 경로가 확인되지 않았다.
V3에서는 설정/셀 레지스터 읽기 실패 시 해당 처리만 건너뛰고, 통신 실패 보호 상태나 유효 측정값의 만료 상태를 설정하지 않는다. 정상 셀 값을 한 번 얻은 후 통신이 계속 실패하면 이전의 정상 전압이 남아 셀 범위 검사도 통과할 수 있다. 2번의 초기화 문제를 고쳐도 이 실패 처리는 별도로 필요하다.
권장 수정: 드라이버의 통신 결과 및 마지막 성공 측정 시각을 하나의 유효성 상태로 관리하고, 그 상태를 보호 로직과 CAN 응답이 공통으로 사용하게 한다. 실패 지속 시간과 복구 조건도 명시한다.
검증 시나리오: 정상 측정 후 AFE 응답/CRC 실패를 지속시킨다. 정한 제한 시간 내 보호 플래그가 설정되고 릴레이가 차단되어야 하며, 이전 정상값만으로 보호가 해제되면 안 된다.
### 4. [P1] 충전 중이면 저전압 보호를 강제로 지움 — V2 / V3
위치:
- V2: `APP_R3221/App/Source/alarm.c:369–381`
- V3: `APP_R3323/App/Source/alarm.c:369–381`
`ProcessSafety_CUV()`에서 저전압을 검출한 뒤 `op_status == 0x0001`이면 팩/셀 저전압 보호와 셀 보호 비트맵을 지운다. 이어지는 방전 보호 판단에는 저전압이 없는 것으로 전달되어 방전 허용이 유지될 수 있다.
예를 들어 충전 상태에서 한 셀이 CUV 임계치보다 낮지만 1000mV보다는 높은 경우, 별도의 비정상 전압 검사는 작동하지 않고 CUV는 강제 해제된다. 충전으로 저전압을 복구하는 동안 부하가 붙어도 방전 차단 판단이 누락될 수 있다. V1에는 이 강제 해제 블록이 없다.
V2/V3 변경 이력서의 '충전 중 CUV 강제 삭제 제거' 설명과 실제 코드도 일치하지 않는다.
권장 수정: 충전 상태 여부로 CUV를 지우지 말고 전압 복구 조건으로 해제한다. 저전압 상태의 충전 허용 정책은 방전 보호 플래그와 분리해서 적용한다.
검증 시나리오: 충전 상태에서 CUV 범위의 셀 전압을 입력하고, 방전 부하를 추가한다. 저전압 플래그와 방전 차단 명령이 유지되어야 한다.
### 5. [요청한 개선 1·2·4 적용 / 실물 검증 전] 이미지 검증 완료 여부 없이 업데이트를 확정할 수 있음 — V1 / V2 / V3
위치:
- V1: `mBMS_APP_V3117/APP/can_update_process.c:377–381`
- V2: `APP_R3221/App/Source/can_update_process.c:415–419`
- V3: `APP_R3323/App/Source/can_update_process.c:415–419`
이미지 체크섬 검증 성공 경로는 `fwinfo.AppUpdate = 3`으로 설정한다. 그러나 `UpdateRestartByCan()`의 옵션 3은 현재 상태를 확인하지 않고 `AppUpdate = 9`를 저장하고 재시작한다. 수신 중단이나 체크섬 실패 후에도 업데이트 확정 명령이 성공 처리된다.
권장 수정: 검증 완료 상태에서만 확정을 허용하고, 새 다운로드/쓰기/실패 시 이전 검증 상태를 무효화한다. 부트로더에서도 이미지의 범위와 무결성을 독립적으로 검증해야 한다.
검증 시나리오: 다운로드 전, 부분 수신 후, 이미지 체크섬 실패 후 각각 옵션 3을 전송한다. 모두 거절되고 확정 상태가 저장되지 않아야 한다. 검증된 이미지에 대해서만 허용한다.
부트로더 소스는 이번 검토 범위에 없으므로, 이 경로가 실제로 잘못된 이미지를 설치하는지 또는 부트로더에서 최종 거절되는지는 확인하지 않았다.
사용자가 선택한 개선안 1·2·4를 V1/V2/V3에 공통 적용했다.
- 현재 다운로드 세션에서 이미지 체크섬 검증이 완료되고 `AppUpdate == 3`인 경우에만 확정 옵션 3을 허용한다. 부팅 때 읽은 저장 상태만으로는 확정할 수 없다.
- 검증 완료 후 지우기, 주소 설정, 데이터 쓰기, 블록 체크섬 및 이미지 재검증 명령을 거절하여 검증 상태를 유지한다. 다시 다운로드하려면 시작 명령이 필요하다.
- 시작 명령은 이전 검증 상태와 쓰기 주소/길이를 초기화한다. 길이 0 또는 허용 크기 초과로 시작이 거절되어도 이전 검증 자격은 남기지 않는다.
- 쓰기는 지우기 완료 상태와 유효한 블록 주소 설정 이후에만 허용한다.
- `SaveFwInfo()`가 지우기/쓰기 결과를 반환하고, 쓰기 완료 주소 및 Flash 전체 저장값을 다시 비교한다. 확정 정보 저장이 실패하면 오류 응답을 보내고 자동 재시작하지 않는다. RAM의 상태를 검증 완료로 복원하여 이미지 수정 없이 확정 재시도가 가능하다.
- 상태 오류는 기존 결과 코드 9, 확정 정보 저장 오류는 새 결과 코드 11로 응답한다. 기존 데이터 쓰기 패킷의 개별 무응답 방식은 유지한다.
검증: `tests/test_fw_update_guards.py`에서 현재 소스의 CAN 업데이트 모듈과 실제 `SaveFwInfo()` 함수를 추출해 Windows x86 C 코드로 컴파일하고 실행했다. Flash/CAN/재시작만 모의 처리했다. V1/V2/V3 모두 아래 시험에 통과했다.
- 다운로드 전/저장 상태만 검증 완료/다운로드 시작 직후/이미지 미검증 상태의 확정 거절
- 크기 불일치 및 체크섬 불일치 후 확정 거절
- 검증 완료 후 지우기·주소 설정·쓰기·체크섬 명령 거절 및 Flash 변경 없음
- 새 다운로드 시작 시 검증 자격 및 이전 쓰기 주소 무효화
- 길이 0 및 최대 크기 초과 시작 요청 후 이전 검증 자격 무효화
- Flash 지우기 실패, 쓰기 실패, 읽기 비교 불일치, 실제 쓰기 없는 성공 반환 시 확정 실패 응답 및 재시작 금지
- 각 저장 실패 후 확정 재시도 성공, 저장값 일치 및 재시작
V1/V2/V3 Keil ARMCC Release 전체 빌드는 모두 0 Error / 0 Warning으로 통과했고 HEX/BIN을 재생성했다. 이번 적용에는 별도 제안 3번의 추가 이미지 범위/실행 주소 검사와 5번의 부트로더 수정, 전원 차단 중 저장 복구 구조 변경은 포함하지 않는다.
### 6. [FW 개선 완료 / 실물 업데이트 검증 전] `WritedataChkSumProcess Fail - 5` 빈발 — V1 / V2 / V3
기존 FW는 CAN 8바이트 데이터 패킷을 받을 때마다 즉시 Flash에 기록했다. GUI는 한 블록에 최대 256개 패킷(2KB)을 짧은 간격으로 전송하지만 FW 수신 큐는 100개였고 CAN 태스크는 한 주기당 한 패킷만 처리했다. V2/V3의 링 버퍼는 가득 찬 상태와 빈 상태가 같은 인덱스로 보일 수 있어, 버스트 수신 중 미처리 프레임이 유실되어도 블록 체크섬 단계에서 결과 코드 5로만 나타날 수 있었다. Flash 프로그램 실패나 읽기 불일치도 별도로 구분되지 않았다.
GUI를 변경하지 않고 V1/V2/V3 FW에 다음을 적용했다.
- 2KB 블록을 RAM에 먼저 수신하고 256비트 수신 맵으로 패킷 번호를 관리한다. 모든 패킷과 RAM 체크섬이 정상일 때만 블록 전체를 Flash에 한 번 기록한다.
- 동일 데이터의 중복 패킷은 허용하고, 같은 번호의 내용이 다른 중복 패킷은 오류 13으로 거절한다.
- 누락 패킷, 잘못된 DLC, 블록 범위를 벗어난 패킷은 오류 12로 구분한다. 결과 코드 5는 RAM에 완성된 블록의 체크섬 불일치에만 사용한다.
- Flash 쓰기 종료 주소와 기록 내용을 다시 읽어 비교한다. 쓰기 실패, 부분 쓰기 또는 읽기 비교 실패는 오류 14로 구분하며 부분 기록된 세션은 무효화한다.
- 체크섬 응답이 유실되어 GUI가 같은 블록 체크섬을 다시 요청해도 이미 확정된 블록을 재프로그램하지 않고 성공 응답만 재전송한다.
- 다음 블록 주소는 직전 확정 주소와 연속이어야 하며, 전체 이미지 체크섬은 선언된 파일 크기까지 모든 블록이 순차 확정된 뒤에만 허용한다.
- CAN 수신 큐를 100개에서 320개로 확대하고, 가득 찼을 때 읽지 않은 데이터를 덮어쓰지 않고 새 프레임을 폐기하면서 오버플로 카운터를 증가시킨다. CAN 태스크는 한 주기에 최대 32개를 처리해 256프레임 버스트의 적체를 빠르게 해소한다.
- 기존의 동적 2KB `FwData` 할당은 정적·워드 정렬 블록 버퍼로 대체해 업데이트 시작 시 할당 실패와 정렬 불확실성을 제거했다.
자동 시험 `tests/test_fw_update_guards.py`는 현재 V1/V2/V3 소스의 실제 함수를 추출하여 다음을 확인했고 모두 통과했다.
- 256개 패킷의 순서 변경 수신, 정상 중복, 누락, 충돌 중복, 짧은 DLC 및 범위 초과
- RAM 체크섬 불일치 후 정상 재시도, 체크섬 ACK 재요청 시 Flash 재쓰기 방지
- Flash 쓰기 실패, 부분 쓰기, 읽기 비교 실패 및 세션 무효화
- 2KB 블록과 3바이트 꼬리 블록을 포함한 전체 이미지 검증
- 큐 포화, FIFO 순서, 인덱스 래핑, 257프레임 버스트 및 주기당 32프레임 처리 제한
Keil ARMCC Release 빌드는 V1/V2/V3 모두 0 Error / 0 Warning으로 통과했다. GUI는 수정하지 않았으므로 FW가 오류 12/13/14를 응답하면 현재 GUI는 해당 오류에서 중지한다. 수신 큐 확대와 처리량 개선으로 유실 가능성을 낮추고 원인을 구분했지만, 실제 CAN 부하·전원 변동을 포함한 반복 업데이트 시험은 실물에서 추가 수행해야 한다. 패킷 누락 시 자동 재전송까지 구현하려면 후속 GUI 프로토콜 개선이 필요하다.
## 요구사항 및 문서 대조
- `Requirement/KT 프로그램 수정 사항.xlsx`에는 200mV 이상이 24시간 유지되면 충전을 차단하도록 명시되어 있다. 사용자 설명에 따라 방전 중에는 충전 차단 알람을 유지하면서 충전 릴레이를 ON하는 예외를 적용한다. 방전 종료 후에는 해당 알람에 따라 OFF로 복귀해야 하며, 최초 보고서의 1번 결함 판정은 철회했다.
- 변경 이력서에는 150–200mV 히스테리시스 유지가 기술되어 있으나 현재 세 버전은 임계치 미만이면 타이머를 초기화한다. 원본 요구사항의 '200mV 이상 유지'와는 부합할 수 있어 별도 결함으로 단정하지 않았다. 최신 승인 동작에 맞게 문서를 정리할 필요가 있다.
- 원본 요구사항에는 과충전 전류 차단 후 자동 복구하지 않는다고 쓰여 있으나 구현은 3회 발생 후 래치한다. 변경 이력서에는 3회 조건이 있으므로, 1회 차단부터 유지해야 하는지 3회 이후인지 승인 기준 확인이 필요하다.
## 검증 범위와 한계
- 세 버전의 실제 소스에서 호출 순서, 플래그 생산/소비 위치 및 릴레이 출력 연결을 추적했다.
- 현재 수정 소스로 Keil ARMCC Release 전체 빌드를 다시 수행했다. V1 `mBMS-16S_APP`, V2 `mBMS2_KT_App_Release`, V3 `mBMS3_KT_App_Release`가 모두 0 Error / 0 Warning으로 완료됐고 각 프로젝트의 HEX/BIN 후처리도 성공했다.
- 빌드 메모리 결과는 V1 `Code=65102, ZI=119248`, V2 `Code=62418, ZI=70488`, V3 `Code=51326, ZI=64740`이다.
- 실물 장비, AFE 모의 응답, 릴레이 계측, 장시간 타이머 및 부트로더 업데이트 시험은 수행하지 않았다. 위 시나리오는 후속 검증 항목이며 실행 결과가 아니다.
- graphify 전체 스캔은 2,340개 파일·약 620만 단어로 안전 기준을 초과해 그래프 생성 단계 전에 중단했다. 발견 사항과 수정 검증은 직접 읽은 소스, 실제 함수 기반 자동 시험 및 Keil 전체 빌드에 근거한다.
1번의 결함 판정은 철회했고, 2번 및 5번에서 요청한 개선안 1·2·4와 FW 업데이트 오류 5 저감 개선을 V1/V2/V3에 반영했다. 미수정 발견 사항은 3번의 측정 유효성 처리와 4번의 저전압 보호이며, 5번의 추가 이미지 검사 및 부트로더 검증은 별도 검토 대상이다. 전체 빌드와 자동 시험은 통과했으며 실물 검증은 남아 있다. 방전 중 릴레이 ON 예외는 후속 수정에서도 보존해야 한다.

View File

@@ -0,0 +1,619 @@
# KT mBMS 펌웨어 개선 및 수정 내역서 (V1 / V2 / V3)
- **작성일자**: 2026년 10월 22일 (최종 갱신: 2026년 10월 23일)
- **문서 목적**: KT mBMS 펌웨어 개선 요구사항 반영, 하드웨어 3종(1st/2nd/3rd) 회로도 검토 및 수평 전개 완료 내역 상세 기록
- **검증 환경**: Keil ARMCC (v5.06 update 7) 전수 컴파일 검증 완료 (0 Error, 0 Warning)
---
## 1. 하드웨어 3종 및 대상 펌웨어 개요
| 구분 | 1st (V1) | 2nd (V2) | 3rd (V3) |
| :--- | :--- | :--- | :--- |
| **프로젝트 경로** | [`mBMS_APP_V2403`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403) | [`APP_R3221`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221) | [`APP_R3323`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323) |
| **적용 회로도** | `1st KT mBMS-16S Rev1.0.3.pdf` | `2nd KT mBMS-16S Rev2.0.1.pdf` | `3rd KT mBMS-16S Rev2.0.3.pdf` |
| **메인 MCU** | STM32F407VGT6 (100-pin) | HC32F460PETB (100-pin) | HC32F460PETB (100-pin) |
| **AFE IC** | TI BQ76PL455 (UART) | TI BQ76940 (I2C) | ADI ADBMS1818 (SPI) |
| **충방전 스위치** | 보드 내 N-MOSFET 드라이버 기반 **외부 Relay 제어** | 보드 내 N-MOSFET 드라이버 기반 **외부 Relay 제어** | 보드 내 N-MOSFET 드라이버 기반 **외부 Relay 제어** |
| **저장 장치** | FatFS (외부 SPI Flash) | FatFS (외부 SPI Flash) | MCU 내부 Flash 직접 관리 (FatFS 미사용) |
| **셀 구성** | 15S LiFePO4 (`DEVICE_MAX_CELL = 15`) | 15S LiFePO4 (`DEVICE_MAX_CELL = 15`) | 15S LiFePO4 (`DEVICE_MAX_CELL = 15`) |
| **공식 펌웨어 버전** | **V4.0.0.1** (`0x4001`) | **V4.0.0.1** (`0x4001`) | **V4.0.0.1** (`0x4001`) |
| **비공개 식별 코드** | `0x57 0x11 0x01` (`W` / V1 / R401) | `0x57 0x22 0x01` (`W` / V2 / R401) | `0x57 0x33 0x01` (`W` / V3 / R401) |
| **디버그 콘솔 문자열** | `HW1:STM32+PL455, R401-JKW` | `HW2:HC32+BQ76940, R401-JKW` | `HW3:HC32+ADBMS1818, R401-JKW` |
---
## 2. KT 5대 요구사항 반영 내역 요약
| 요구사항 항목 | 수정 전 | 개선 및 수정 후 | 적용 버전 |
| :--- | :--- | :--- | :---: |
| **1. 셀 편차 200mV & 24시간 충전 차단** | 기본값 500mV, 타이머 미구현 또는 60초 | 기본값 200mV로 변경, `Load_Param()`에서 200mV 초과값 자동 보정. 24시간(`86,400,000ms`) 연속 편차 유지 시 충전 FET/릴레이 즉시 차단. 노이즈 지터로 인한 억울한 리셋을 막는 **히스테리시스 구간(150~200mV 보류)** 적용. | V1, V2, V3 |
| **2. 반복 과충전 전류(OCC) 영구 래치 차단** | OCC 3회 발생 후 전류 감소 시 자동 복구됨, Pre-charge FET가 켜지는 버그 존재 | OCC 3회 누적(`OCC_3Times_Fail`) 시 자동 복구 금지(Lockout), 과충전 보호 플래그 영구 래치 유지. Pre-charge FET(`ZVC_C`) 강제 차단 버그 수정. | V1, V2, V3 |
| **3. 충전 차단 Flag 적용 및 Standby 복귀** | 충전 차단 플래그가 서도 충전 상태(`OP_STS_CHARGING`) 유지 | 충전 차단 Flag(`CHG_C == false`) 발생 또는 충전 검출 임계치 이하 시 `OP_STS_STANDBY`로 정상 복귀하도록 상태 천이 개선. | V1, V2, V3 |
| **4. 셀 전압 0V/단선 측정 불량 감지 및 차단** | 충전 중(`op_status == 1`) CUV 플래그를 강제로 0으로 지우던 심각한 결함 존재 | 임의의 셀 전압이 1000mV 이하(0V/단선) 또는 4500mV 이상일 때 `afe_fail` 트리거 및 충/방전 전면 차단. 충전 중 CUV 강제 삭제 코드 제거. | V1, V2, V3 |
| **5. 온도 센서 단선/불량 감지 및 충전 차단** | 온도 센서 단선 감지 부재 | `IsTempSensorFailed()` 구현 (-35℃ 이하 또는 110℃ 이상 감지). 단선 감지 시 `error.bit.temp_sensor` 설정 및 충전 FET/릴레이 차단. | V1, V2, V3 |
---
## 3. 크리티컬 잠재 위험 요소 및 안전 기능 개선
### ① 24시간 셀 편차 타이머 노이즈 지터 방어 (V1, V2, V3 공통)
- **문제점**: 충전 중 리플 노이즈로 인해 200mV 경계선에서 199mV로 단 1사이클(100ms)만 튀어도 24시간 카운트다운이 0으로 풀 리셋되어 현장에서 영원히 차단되지 못할 위험 존재.
- **해결책**:
- `Cell.avg.diff >= 200mV`: 24시간 카운트다운 계속 진행.
- `Cell.avg.diff < 150mV`: 정상 해소로 판정하고 타이머 리셋.
- `150mV <= Cell.avg.diff < 200mV`: 타이머를 리셋하지 않고 보류(Hold)하여 노이즈 지터 내성 확보.
### ② AFE Fail 플래그 주기적 덮어쓰기 토글 버그 수정 (V1, V2, V3 공통)
- **문제점**: `ProcessAlarm()` 최상단에서 `protect.bit.afe_fail = battery_status.bit.AFE;`로 덮어쓰고 있어, 셀 단선(0V)으로 `afe_fail`이 켜져도 매 주기 통신 정상값(false)에 의해 일시적으로 지워졌다 다시 켜지는 플리커링 발생.
- **해결책**: `protect.bit.afe_fail = battery_status.bit.AFE | error.bit.afe_fail;`로 안전 결합하여 지속적인 보호 상태 보장.
### ③ 1st 회로도(Rev 1.0.3) 기반 릴레이 제어 핀맵 수정 및 기동 Glitch 방지 (V1 전용)
- **문제점**:
- 기존 `mBMS_APP_V2403`에 다른 하드웨어의 핀 매핑(듀얼 FET 제어용 PC6, PC7 등)과 잘못된 방전 센싱 핀(PD14)이 들어가 있었음.
- STM32F4 리셋 직후 GPIO ODR 초기값(0)으로 인해 `Relay_Init()` 시 수 마이크로초 동안 릴레이 접점이 딸깍거리며 튀는 결함(Glitch) 발생 위험.
- **해결책**:
- 1st 회로도 Rev 1.0.3 Altium Netlist 기반 핀맵 정확히 매핑:
- **충전 릴레이**: `PC9` (제어, Active-Low) / `PC8` (센싱 피드백)
- **방전 릴레이**: `PD13` (제어, Active-Low) / `PD12` (센싱 피드백)
- **프리차지 릴레이**: `PD15` (제어, Active-Low) / `PD14` (센싱 피드백)
- `Relay_Init()`에서 GPIO 핀을 출력으로 설정하기 전 `GPIO_SetBits()`로 HIGH(OFF 레벨)를 미리 출력 레지스터에 기록하여 부팅 시 릴레이 접점 튐을 원천 방지.
### ④ V3 MCU 내부 Flash 수명(Wear-out) 방지 최적화 (V3 전용)
- [calc_soc.c](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/calc_soc.c)에서 1분마다 무조건 Flash Sector Erase를 수행하던 결함을 수정하여, 10분 주기 + 용량 변화(1% 이상)가 있을 때만 지우고 쓰도록 최적화 (Flash 수명 10배 이상 연장).
### ⑤ CAN 파라미터 유효 범위 검증(Sanity Check) 개선 및 자동 롤백 적용 (V1, V2, V3 공통)
- **문제점**:
- 외부 CAN 버스(호스트 GUI 또는 관제 랙 제어기)에서 비정상적인 파라미터 값(예: 셀 편차 500mV 설정, Warning > Trip 역전, 비정상 고온/과전류 설정) 주입 시 검증 없이 비휘발성 Flash에 그대로 저장되어 배터리 화재 위험 노출.
- **해결책**:
- `can_data_process.c`에 10개 전용 Sanity Check 헬퍼 함수 구현.
- 셀 전압(PF 5), 시스템 전압(PF 7), 온도(PF 6), 과전류(PF 22), 복구 구간(PF 23, 24), 셀 편차(PF 2), 밸런싱(PF 10), SOC(PF 14), 용량(PF 8)의 상하한 및 논리 대소 관계(`Warn < Trip` 등) 수신 즉시 사전 검증.
- **KT 필수 요구사항**: 셀 전압 편차(`diff_th`, `diff_warn`)는 200mV를 절대 초과할 수 없도록 강제 클램핑.
- 비정상 값 수신 시 Flash 저장(`Save_Param()` / `dflash_write_dev_param()`)을 즉시 차단하고, 현재 저장된 정상 값으로 CAN 에코 응답하여 호스트 GUI 화면을 정상 값으로 자동 롤백 복원.
- ARMCC v5.06(C89 표준) 컴파일 에러 없는 완전한 규격 준수.
### ⑥ 펌웨어 버전 통합(V4.0.0.1) 및 비공개 식별자(Secret Identifier) 구현 (V1, V2, V3 공통)
- **요구사항**:
- 3종 하드웨어 펌웨어 전체의 공식 버전을 동일하게 통일(`V4.0.0.1`).
- KT 호스트 및 관제 GUI 화면에는 일관되게 `V4.0.0.1`로 정상 표시되면서, 개발자 본인만 CAN 패킷 덤프나 시리얼 콘솔에서 하드웨어 종별(V1, V2, V3)과 고유 빌드 여부를 즉시 식별할 수 있는 별도 비공개 구분자 구현.
- **해결책**:
1. **공통 펌웨어 버전**:
- `app_ver.h` 및 `app_ver.c`에서 `APP_VER_CRITICAL=4, MAJOR=0, MINOR=0, COMPILE=1`로 3개 프로젝트 전체 일치화.
- CAN Status #2 (PF 12)의 Byte 6~7에 4-nibble BCD `0x4001` (`V4.0.0.1`) 전송.
2. **CAN Status #2 미사용 Reserved 필드(Byte 3~5) 비공개 식별자 패킹**:
- **Byte 3**: 개발자 고유 서명 (`DEV_SECRET_SIG = 0x57`, ASCII 'W' for Jongkwang Woo)
- **Byte 4**: 하드웨어 구분 코드 (`DEV_HW_TYPE`):
- **`0x11`**: V1 (1st HW, STM32F407 + TI BQ76PL455 + Relay)
- **`0x22`**: V2 (2nd HW, HC32F460 + TI BQ76940 + Relay)
- **`0x33`**: V3 (3rd HW, HC32F460 + ADI ADBMS1818 + Relay)
- **Byte 5**: 빌드/릴리즈 리비전 (`DEV_BUILD_REV = 0x01`, R401)
- KT 관제 랙이나 GUI는 Reserved를 무시하므로 기존 기능에 100% 무영향이며, CANoe/PCAN 로거에서 개발자만 즉시 보드 판별 가능.
3. **UART 디버그 콘솔 출력 세부 식별 표기**:
- `APPVerDisplay()` 및 `GetAppVersionStr()`에 하드웨어 명칭과 릴리즈 식별자(`HW1:STM32+PL455, R401-JKW` 등) 표기.
4. **CAN Response 41 (SOH) 호환성 보강**:
- GUI(`csCanCommFunction.cs`)의 버전 분기 로직에 대응하여 `CanRspSOCCode`의 Byte 2~3과 Byte 4~5 양쪽에 SOH를 동시 기록하여 버전 변경 후에도 GUI SOH 표시가 0으로 누락되는 문제 원천 방지.
---
## 4. 프로젝트별 수정 파일 목록
### 📁 1st 하드웨어: `mBMS_APP_V2403` (STM32F407 + BQ76PL455 + Relay)
1. [`HARDWARE/relay.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/HARDWARE/relay.h)
- 1st 회로도 Rev1.0.3 기준 충전(`PC9`), 방전(`PD13`), 프리차지(`PD15`) Active-Low 제어 매크로 재정의.
2. [`HARDWARE/relay.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/HARDWARE/relay.c)
- 1st 회로도 기준 GPIO 핀 재배치 (`PC9/PC8`, `PD13/PD12`, `PD15/PD14`).
- `GPIO_SetBits()` 기반 기동 시 접점 글리치 방지 로직 적용.
- `Relay_Status()` 방전 센싱 핀 `PD12` 및 프리차지 센싱 핀 `PD14` 매핑.
3. [`APP/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/dflash_sf1.c)
- 기본 셀 편차 임계치를 500mV에서 200mV로 변경.
4. [`APP/device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/device_param.h)
- V2/V3 통신 맵 호환을 위한 `TBATTERY_ERROR error` union 정의 및 `TDEVICE_STATUS.reserved` 자리에 추가.
5. [`APP/device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/device_param.c)
- `Load_Param()`에 200mV 초과 설정 방지 강제 보정 코드 추가.
6. [`APP/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/temp.h) / [`APP/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/temp.c)
- `IsTempSensorFailed()` 함수 선언 및 구현 (-35℃ 이하 또는 110℃ 이상 단선 감지).
7. [`APP/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/alarm.c)
- `CV_DIFF_PROTECT_HOLD_TIME` (24시간) 카운트다운 및 히스테리시스 보호 로직 적용.
- 반복 OCC 3회 영구 래치 차단.
- 셀 단선/0V 감지 (`cell_fault`) 및 충전 중 CUV 플래그 삭제 버그 제거.
- 충전 차단 시 `OP_STS_STANDBY` 정상 복귀 상태 천이 개선.
- AFE Fail 플래그 덮어쓰기 방지.
8. [`APP/can_data_process.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/can_data_process.c)
- 파라미터 유효 범위 검증(Sanity Check) 헬퍼 함수 구현 및 수신 핸들러 연동.
- 이상치 유입 시 Flash 저장 거부 및 정상 값 에코 회신(GUI 롤백) 처리.
- 셀 편차 200mV 상한 강제 클램핑.
- Status #2 (PF 12) 미사용 Reserved 필드(Byte 3~5)에 V1 비공개 식별 코드(`0x57`, `0x11`, `0x01`) 패킹.
- `CanRspSOCCode` (PF 41) SOH 데이터 이중 기록 (Byte 2~3, Byte 4~5 동시 기록으로 GUI 호환성 완벽 보장).
- C89 호환 변수 선언부 정리 (ARMCC 0 Error 빌드).
9. [`APP/app_ver.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/app_ver.h) / [`APP/app_ver.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/app_ver.c)
- 공식 펌웨어 버전 **V4.0.0.1** (`0x4001`) 통일 정의.
- V1 고유 비공개 식별자(`DEV_SECRET_SIG=0x57`, `DEV_HW_TYPE=0x11`, `DEV_BUILD_REV=0x01`) 정의 및 `GetAppSecretId()` 함수 구현.
- 시리얼 콘솔 표기 개선: `mBMS [OPERATING] VER 4.0.0.1 (HW1:STM32+PL455, R401-JKW)`.
---
### 📁 2nd 하드웨어: `APP_R3221` (HC32F460 + BQ76940 + FET)
1. [`App/Source/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/dflash_sf1.c)
- 기본 셀 편차 임계치를 500mV에서 200mV로 변경.
2. [`App/Source/device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/device_param.h)
- `TBATTERY_ERROR error` union 정의 및 `TDEVICE_STATUS.reserved` 대체.
3. [`App/Source/device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/device_param.c)
- `Load_Param()`에 200mV 초과 설정 방지 강제 보정 코드 추가.
4. [`App/Source/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/temp.h) / [`App/Source/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/temp.c)
- `IsTempSensorFailed()` 선언 및 구현.
5. [`App/Source/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/alarm.c)
- 셀 편차 24시간 200mV 충전 차단 + 히스테리시스 보호.
- 주석 해제: `ProcessSafety_CellVoltageDiff()` 호출 활성화.
- OCC 3회 차단 시 Pre-charge FET가 켜지던 결함(`ZVC_C = false`) 수정 및 영구 래치.
- AFE Fail 시 Pre-charge FET가 켜지던 결함(`ZVC_C = false`) 수정.
- 셀 0V/단선 감지 및 충전 중 CUV 강제 삭제 코드 제거.
- 충전 차단 시 `OP_STS_STANDBY` 복귀 로직 적용.
6. [`App/Source/can_data_process.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/can_data_process.c)
- 파라미터 유효 범위 검증(Sanity Check) 헬퍼 함수 구현 및 수신 핸들러 연동.
- 이상치 유입 시 Flash 저장 거부 및 정상 값 에코 회신(GUI 롤백) 처리.
- 셀 편차 200mV 상한 강제 클램핑.
- Status #2 (PF 12) 미사용 Reserved 필드(Byte 3~5)에 V2 비공개 식별 코드(`0x57`, `0x22`, `0x01`) 패킹.
- `CanRspSOCCode` (PF 41) SOH 데이터 이중 기록 (Byte 2~3, Byte 4~5 동시 기록으로 GUI 호환성 완벽 보장).
- C89 호환 변수 선언부 정리 (ARMCC 0 Error 빌드).
7. [`App/Source/app_ver.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/app_ver.h) / [`App/Source/app_ver.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/app_ver.c)
- 공식 펌웨어 버전 **V4.0.0.1** (`0x4001`) 통일 정의.
- V2 고유 비공개 식별자(`DEV_SECRET_SIG=0x57`, `DEV_HW_TYPE=0x22`, `DEV_BUILD_REV=0x01`) 정의 및 `GetAppSecretId()` 함수 구현.
- 시리얼 콘솔 표기 개선: `mBMS [OPERATING] VER 4.0.0.1 (HW2:HC32+BQ76940, R401-JKW)`.
---
### 📁 3rd 하드웨어: `APP_R3323` (HC32F460 + ADBMS1818 + FET)
1. [`App/Source/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/dflash_sf1.c)
- 기본 셀 편차 임계치를 500mV에서 200mV로 변경.
2. [`App/Source/device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/device_param.h)
- `Load_Param()`에 200mV 초과 설정 방지 강제 보정 코드 추가.
3. [`App/Source/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/temp.h) / [`App/Source/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/temp.c)
- `IsTempSensorFailed()` 선언 및 구현.
4. [`App/Source/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/alarm.c)
- 셀 편차 24시간 200mV 충전 차단 + 히스테리시스 보호.
- OCC 3회 영구 래치 및 Pre-charge 차단 버그 수정.
- 셀 0V/단선 감지 및 충전 중 CUV 강제 삭제 코드 제거.
- 충전 차단 시 `OP_STS_STANDBY` 복귀 로직 적용.
- AFE Fail 플래그 보존 로직 보강.
5. [`App/Source/calc_soc.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/calc_soc.c)
- 1분 주기 무조건 Erase/Write 제거 → 10분 주기 + 1% 이상 변화 시 저장하도록 Flash Wear-out 최적화.
6. [`App/Source/can_data_process.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/can_data_process.c)
- 파라미터 유효 범위 검증(Sanity Check) 헬퍼 함수 구현 및 수신 핸들러 연동.
- 이상치 유입 시 Flash 저장 거부 및 정상 값 에코 회신(GUI 롤백) 처리.
- 셀 편차 200mV 상한 강제 클램핑.
- Status #2 (PF 12) 미사용 Reserved 필드(Byte 3~5)에 V3 비공개 식별 코드(`0x57`, `0x33`, `0x01`) 패킹.
- C89 호환 변수 선언부 정리 (ARMCC 0 Error 빌드).
7. [`App/Source/app_ver.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/app_ver.h) / [`App/Source/app_ver.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/app_ver.c)
- 공식 펌웨어 버전 **V4.0.0.1** (`0x4001`) 통일 정의.
- V3 고유 비공개 식별자(`DEV_SECRET_SIG=0x57`, `DEV_HW_TYPE=0x33`, `DEV_BUILD_REV=0x01`) 정의 및 `GetAppSecretId()` 함수 구현.
- 시리얼 콘솔 표기 개선: `mBMS [OPERATING] VER 4.0.0.1 (HW3:HC32+ADBMS1818, R401-JKW)`.
---
## 5. 펌웨어 업데이트 시 파라미터 최소 1회 자동 초기화 및 GUI 연동
### 1) 펌웨어 업데이트 시 파라미터 자동 1회 초기화 (Auto Reset on Firmware Update)
- **개선 배경**: 기존 필드 보드의 Flash/파일시스템에는 이전 버전 파라미터(예: 셀 편차 500mV 등)가 저장되어 있어, 신규 펌웨어(V4.0.0.1)를 다운로드하더라도 기존 Flash 파라미터를 그대로 읽어 KT 5대 규격 기본값이 적용되지 않는 문제 원천 차단.
- **적용 메커니즘**:
1. **매직 코드 통일**: 파라미터 초기화 판별 매직 키 `PARAM_INIT_DATA`를 **`0xAAAA4001`** (V4.0.0.1 전용)로 통일 (기존 V1 `0xAAAA32F4`, V2/V3 `0xAAAA32F6`).
2. **업데이트 후 첫 부팅 시 자동 초기화**: 기존 보드에 신규 펌웨어를 다운로드 후 첫 부팅 시 Flash/파일의 `initdata`와 새 매직키(`0xAAAA4001`)가 불일치하므로, **최초 1회 `Param_Init(true, true)`를 강제 실행**하여 모든 안전 파라미터를 KT 규격 기본값으로 자동 초기화하고 Flash에 즉시 저장 (`Save_Param()`).
3. **V2 결함 수정**: V2(`APP_R3221`)의 `Load_Param()`에서 파일 로드 후 `initdata` 체크가 주석 처리되어 있던 결함을 수정하여 파일 로드 후 매직코드 검증 및 자동 초기화 로직 구현.
4. **셀 편차 기본값 일치화**:
- `Cell_Voltage_Diff_Threshold = 200 mV` (KT 규격: 200mV 이상 차단)
- `Cell_Voltage_Diff_Warning = 200 mV`
- `Cell_Voltage_Diff_Recovery = 100 mV` (확실한 히스테리시스 보장)
- `Cell_Voltage_Diff_Time = 60 sec`
5. **이상치 및 유효성 방어 로직 강화**:
- `Load_Param()`에서 Threshold가 0이거나 200mV 초과 시 200mV로 자동 보정, Recovery가 0이거나 Threshold 이상일 경우 100mV로 자동 보정, Time이 0일 경우 60초 자동 보정.
- `can_data_process.c`의 `Validate_CellVoltageDiffParam`에서 Time이 0으로 유입될 경우 자동으로 60초로 보정하여 정상 수락.
### 2) GUI (`LFP_Manager_GL`) 셀 편차 알람 및 파라미터 제어 연동 상태
- **셀 편차 알람 확인**:
- 모니터링 화면(`ucShelfStatus`): CAN Status #1 (PF 11)의 Warning Bit 6, Fault Bit 6을 수신하여 `C-V Dev` (`alarmTrip[9]`) 인디케이터에 **정상(녹색) / 경고(노란색) / 트립 보호(빨간색)** 상태를 실시간 표시.
- **셀 편차 파라미터 수정**:
- 파라미터 설정 창(`fmxParamConfig`): `Cell Voltage Diff` 그룹(`ucCvDiffParam`)에서 Fault(Threshold), Warning, Release(Recovery)의 현재 값을 조회(Read, CAN PF 2 0xFF)하고, 새로운 값을 입력하여 모듈 Flash로 설정(Set, CAN PF 2) 가능.
- GUI 솔루션(`LFP_Manager.sln`) MSBuild Release 빌드 정상 검증 완료.
---
---
## 7. 3종 펌웨어 통합 패키징(Multi-FW Bin) 및 GUI 보드 버전 자동 판별 다운로드 구현
### 1) 구현 배경 및 목적
- **단일 통합 파일 배포**: 필드 유지보수 및 생산 현장에서 하드웨어 종류(V1, V2, V3)에 따라 서로 다른 펌웨어 파일을 각각 찾아 수동으로 선택하던 번거로움과 다운로드 실수(타 기종 펌웨어 오주입) 원천 방지.
- **자동 식별 및 다운로드**: 3종 펌웨어(V1, V2, V3)를 하나의 통합 `.bin` 파일로 패키징하고, 파일 헤더(256 바이트)에 메타데이터를 배치하여 GUI(`LFP_Manager_GL`)가 연결된 보드의 버전을 확인한 후 해당 하드웨어 전용 펌웨어를 자동으로 추출하여 안전하게 다운로드하도록 구현.
### 2) 통합 펌웨어 패키지 헤더 규격 (Total Header Size: 256 Bytes)
- **마스터 패키지 헤더 (`TPackageHeader`, 64 Bytes)**:
- `Magic` (4B): `0x4D4D4657` (`"MMFW"`: Multi mBMS FirmWare)
- `HeaderVersion` (2B): `0x0001` (V1.0)
- `ImageCount` (2B): `3` (3종 펌웨어 슬롯)
- `TotalPackageSize` (4B): 헤더 + 3개 바이너리 전체 바이트 크기 (184,076 bytes)
- `HeaderCrc32` (4B): 헤더 무결성 CRC32 (`0x6EDCA809`)
- `PackageVer` (4B): 통합 릴리즈 버전 (`[4, 0, 0, 1]`)
- `PackageName` (32B): `"mBMS_KT_UNIFIED_FW_V4.0.0.1"`
- **서브 펌웨어 엔트리 (`TFwEntry`, 각 64 Bytes x 3개 = 192 Bytes)**:
| 엔트리 | 타겟 하드웨어 (`HwType`) | 하드웨어 명칭 (`HwName`) | 버전 (`FwVer`) | 오프셋 (`FileOffset`) | 바이너리 크기 | CRC32 | CheckSum (Sum32) | 원본 파일명 |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :--- |
| **#1** | **`0x11` (V1)** | `HW1:STM32+PL455` | V4.0.0.1 | `0x00000100` | 67,580 B | `0x4CEA31B5` | `0x006A8B83` | [`mBMS_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/FLASH/mBMS_APP_V4001.bin) |
| **#2** | **`0x22` (V2)** | `HW2:HC32+BQ76940` | V4.0.0.1 | `0x000109FC` | 63,600 B | `0xE605B6E3` | `0x00660A81` | [`mBMS2_KT_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/MDK/output/mBMS2_KT_APP_V4001.bin) |
| **#3** | **`0x33` (V3)** | `HW3:HC32+ADBMS18` | V4.0.0.1 | `0x0002023C` | 52,640 B | `0x928A2FA7` | `0x0054BEA9` | [`mBMS3_KT_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/MDK/output/mBMS3_KT_APP_V4001.bin) |
### 3) 수정 및 신규 구현 내역
1. **[신규] 패키지 관리 코어 모듈** ([`csMultiFwPackage.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Function/csMultiFwPackage.cs)):
- **보드 하드웨어 자동 판별 규칙 적용**:
- 기존 fw 버전의 두 번째 자릿수 기준: **`x1xx`이면 V1 (`0x11`)**, **`x2xx`이면 V2 (`0x22`)**, **`x3xx`이면 V3 (`0x33`)** 로 자동 구분.
- V4.0.0.1 통일 버전 기종의 경우 CAN Status #2 비공개 코드(`DEV_SECRET_SIG=0x57, DEV_HW_TYPE=0x11/0x22/0x33`)를 보조 판별 기준으로 활용.
- 통합 바이너리 생성(`CreateUnifiedPackage`), 무결성 파싱(`ParsePackage`), 특정 보드 펌웨어 추출(`ExtractFirmware`), CRC32/Sum32 유틸리티 구현.
2. **보드 하드웨어 식별 연동**:
- [`csDataStructure.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/DataStructure/csDataStructure.cs): `DeviceValueData`에 `dev_secret_sig`, `dev_hw_type`, `dev_build_rev` 필드 추가.
- [`csCanCommFunction.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Function/csCanCommFunction.cs): CAN Status #2 (PF 12) Byte 3~5 비공개 식별 코드 및 fw 버전 수신·저장.
- [`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs): 펌웨어 업데이트 다이얼로그 호출 시 현재 연결된 대상 보드의 하드웨어 정보 실시간 전달.
3. **GUI 펌웨어 업데이트 화면 강화** ([`fmxFwUpdate.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxFwUpdate.cs)):
- 파일 로드 시 통합 패키지 여부(`IsMultiFwPackage`) 자동 판별.
- 연결된 보드의 fw 버전(`x1xx → V1`, `x2xx → V2`, `x3xx → V3`)을 확인하여 **해당 펌웨어를 자동 추출 및 UI 표시**.
- CAN 부트로더 프로토콜(`DL_START_CODE` ~ `DL_RESTART_CODE`)과의 100% 호환 전송 및 단일 bin 파일 하위 호환성 완벽 유지.
4. **GUI 이미지 변환기 강화** ([`fmxFwImageConverter.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxFwImageConverter.cs)):
- UI에서 V1, V2, V3 바이너리를 취합하여 원클릭으로 통합 bin 파일을 빌드하는 기능 추가.
5. **[신규] 독립형 패키징 스크립트** ([`package_multi_fw.py`](file:///d:/Project/Firmware/KT/mBMS/R4xx/package_multi_fw.py)):
- CLI 환경에서 3종 바이너리를 묶어 [`mBMS_KT_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_KT_V4001.bin)을 생성하고 무결성을 자체 검증하는 파이썬 스크립트 제공.
6. **GUI 메뉴 제어 개선** ([`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs)):
- **`Firmware Update (TFTP)` 활성화 조건 개선**:
- 통신 인터페이스가 **SNMP(`COMM_SNMP`)일 때만 활성화(`Enabled = true`)** 되도록 제어.
- CAN 통신(USB-CAN, UART-CAN 등) 사용 중이거나 SNMP가 아닐 때는 비활성화(`Enabled = false`).
- 통신 방식 변경(`TypeChangedUpdate`), 드롭다운 메뉴 열림(`updateToolStripMenuItem.DropDownOpening`), 통신 시작/종료 시점에 실시간 상태 동기화 및 핸들러 내 방어 로직 추가.
---
---
## 9. GUI CAN 패킷 모니터링 창 로고 교체 및 전체 영문화
### 1) 개선 배경 및 목적
- GUI 프로그램의 **CAN 패킷 모니터링 창**([`fmxCanLog`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.cs)) 좌측 상단 기본 윈도우 큐브 아이콘을 **AMOTECH 공식 아이콘 로고**로 일원화하고, 창 내부의 모든 UI 요소(타이틀바, 버튼, 필터, 컬럼 헤더, 상태바, 상세 정보 및 파서 설명 텍스트)를 영문으로 전면 전환하여 글로벌 표준 및 일관된 시각적 완성도 제공.
### 2) 세부 수정 내역
1. **창 좌측 상단 로고(Icon) 변경**:
- [`fmxCanLog.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.cs): 생성자에서 `this.IconOptions.Icon = Properties.Resources.AMO_LOGO;` 및 `this.Icon = Properties.Resources.AMO_LOGO;`를 적용하여 폼 상단 타이틀바 아이콘을 AMOTECH 공식 로고로 교체.
2. **UI 컨트롤 및 헤더 영문화** ([`fmxCanLog.Designer.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.Designer.cs)):
- **Form Title**: `"CAN Packet Real-time Monitor"`
- **Toolbar Buttons**:
- Pause 버튼: `"❚❚ Pause"` / 토글 시 `"▶ Resume"`
- Clear 버튼: `"🗑 Clear"`
- CSV 저장 버튼: `"💾 Export CSV"`
- 체크박스: `"Auto Scroll"`
- **Filter Controls**:
- 방향 라벨 및 콤보: `"Direction:"` / `{"All", "Rx Only", "Tx Only"}`
- 검색 라벨: `"Search:"`
- 최대 줄수 라벨: `"Max Rows:"` (너비에 맞게 레이아웃 위치 조정)
- **Grid Columns Header**:
- `No`, `Time`, `Dir`, `CAN ID`, `Idx`, `PF (Cmd)`, `PS (Dst)`, `SA (Src)`, `DLC`, `Data (HEX)`, `Description`, `Details`
- **하단 상세 정보 그룹박스**: `"Selected Packet Details"`
- **하단 상태바(StatusStrip)**:
- 상태: `"● RUNNING"` / `"❚❚ PAUSED"`
- 통계: `Total Packets: {N0} | Rx: {N0} | Tx: {N0} | {Rate} pkt/s`
- 필터링 현황: `Displayed: {N0} / {N0} items`
3. **이벤트 핸들러 및 메시지 다이얼로그 영문화** ([`fmxCanLog.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.cs)):
- `MatchesFilter`: 영문 Direction 필터(`"All"`, `"Rx"`, `"Tx"`) 처리 로직 적용.
- `btnExport_Click`: 파일 저장 다이얼로그 필터 및 타이틀 영문화 (`"Save CAN Packet Log"`), 성공/실패 메시지박스 영문화 (`"CAN packet log exported successfully."` / `"Export Complete"`).
- `dgvPackets_SelectionChanged`: 패킷 상세 정보 문자열 영문화 (`[Packet No.x] Direction: ... | Timestamp: ...`, `DLC(Length): ...`, `Description: ...`, `Parsed Details: ...`).
4. **패킷 파서 설명 영문화** ([`CanPacketParser.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Utils/CanPacketParser.cs)):
- 인벤토리: `[Inventory] BMS Manufacture Date`, `[Inventory] BMS Serial No #1 (MSB)`, `[Inventory Req] ...`
- 펌웨어 업데이트: `[Firmware Update] Cmd: ...`
- 호스트 제어/요청: `[Host Req] Operation Status Request`, `[Host Req] Cell Volt Deviation Param`, `[Host Ctrl] Cell Volt Calibration`, `[Host Ctrl] Current Calibration` 등
- BMS 응답/데이터: `[Heartbeat] BMS Heartbeat Periodic Response`, `[Status] Pack Voltage & Current`, `[Status] OP State & FW Version`, `[Temp] Cell Temperature #1 ~ #4`, `[SOC] Battery State of Charge (SOC) & SOH`, `[Voltage] Cell Voltage #1 ~ #4`, `[Param] Cell Volt Warn/Protect Settings` 등 모든 패킷 해석 영문 매핑.
5. **메인 메뉴 연동** ([`fmxMain.Designer.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.Designer.cs)):
- Tools 메뉴 하위의 패킷 모니터링 메뉴 텍스트를 `"CAN Packet Monitor"`로 영문화.
6. **GUI 프로그램 버전 V4001 (`4.0.0.1`) 변경** ([`AssemblyInfo.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Properties/AssemblyInfo.cs)):
- `AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`을 모두 **`4.0.0.1`**로 변경.
- 메인 폼 타이틀 바 표기: `LFP SYSTEM [4.0.0.1]`로 최신 통일 버전 표출.
7. **초기 인벤토리 요청(PF 1) 3회 연속 중복 송신 제거** ([`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs)):
- 통신 초기 또는 미연결(`ShelfCommFail == true`) 상태에서 매 50ms마다 `900`(Manufacturer Info)을 3회 연속 중복 송신하던 현상 제거.
- 최초 1회 송신 후 BMS의 응답을 최소 300ms 동안 대기하도록 송신 타이머 제어(`_lastInvReqTime`)를 추가하여, **불필요한 3회 연타 없이 1회만 단독 전송**되도록 개선.
8. **Option Flag 기반 동기식 응답 대기 및 타임아웃 프로세스 구현** ([`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs), [`fmxCanLog.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.cs), [`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs)):
- **Option Flag 지원**:
- `CsCanThread.WaitResponseEnabled` (기본값: `true`, 켜짐).
- 패킷 모니터링 창(`fmxCanLog`) 상단 툴바에 `[v] Wait Response` 체크박스를 추가하여 실시간 On/Off 제어 지원.
- 메인 창(`fmxMain`) `Tools` 메뉴에 `Wait BMS Response (Sync Mode)` 체크 메뉴를 추가하여 제어 연동.
- **요청별 기대 응답(Expected Response) 정밀 매핑**:
- `900` (제조일자 요청, PF 1) ➔ 기대 응답: `Index: 1, PF: 11` (BMS Manuf Date)
- `901` (시리얼 MSB 요청, PF 2) ➔ 기대 응답: `Index: 1, PF: 21` (BMS SN MSB)
- `902` (시리얼 LSB 요청, PF 3) ➔ 기대 응답: `Index: 1, PF: 31` (BMS SN LSB)
- `1` (운용 상태 요청, PF 1) ➔ 기대 응답: `Index: 6, PF: 14` (Status 1, 2, 3, 4 멀티프레임 중 **최종 프레임 #4**)
- `19` (셀 전압 요청, PF 19) ➔ 기대 응답: `Index: 6, PF: 194` (Cell 1~4 멀티프레임 중 **최종 프레임 #4**)
- `3` (온도 요청, PF 3) ➔ 기대 응답: `Index: 6, PF: 32` (Temp 1~2 멀티프레임 중 **최종 프레임 #2**)
- `4` (SOC/SOH 요청, PF 4) ➔ 기대 응답: `Index: 6, PF: 41`
- `8` (배터리 파라미터 요청, PF 8) ➔ 기대 응답: `Index: 6, PF: 81`
- `11` (셀 밸런싱 요청, PF 11) ➔ 기대 응답: `Index: 6, PF: 111`
- `15` (충전 옵션 요청, PF 15) ➔ 기대 응답: `Index: 6, PF: 151`
- `17` (전류 보정 계수 요청, PF 17) ➔ 기대 응답: `Index: 6, PF: 171`
- `20` (하트비트 요청, PF 20) ➔ 기대 응답: `Index: 6, PF: 201`
- `16`(기본값 파라미터 초기화) 등 응답이 없는 단방향 제어 명령은 대기 없이 바로 진행.
- **이벤트 기반 즉시 해제 및 타임아웃(Timeout) 처리**:
- `AutoResetEvent` 기반 비동기 신호 처리: BMS로부터 기대하는 응답 패킷이 도착하는 즉시 `Set()`을 호출하여 **0ms 지연으로 대기 해제 후 다음 명령 진행** (초고속 반응성).
- 타임아웃 처리 (`ResponseTimeoutMs`, 기본 300ms): BMS 무응답 또는 통신 단선 시 최대 300ms 후 자동으로 대기를 해제하고 다음 명령으로 안전하게 진행하여 시스템 행(Hang) 완벽 방지.
- 패킷이 뒤죽박죽 엉키지 않고 완벽한 `[요청] ➔ [응답 완료] ➔ [다음 요청]` 순서 보장.
9. **정상 상태 시험을 위한 파라미터 유효성 검증(Sanity Check) 범위 대폭 확장** ([`mBMS_APP_V3117`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_data_process.c), [`APP_R3221`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/can_data_process.c), [`APP_R3323`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/can_data_process.c), [`mBMS_APP_V2403`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/can_data_process.c)):
- **수정 배경**:
- 정상 상태(예: 상온 25°C, 셀 전압 3.25V~3.30V)의 배터리 팩에서 과전압/저전압/온도/전류 트립 및 경고 동작을 시험하기 위해 파라미터 임계값을 정상 동작점 근처(예: 과전압 차단을 3200mV, 경고를 3150mV로 하향 조정)로 변경하려 했으나, 기존 엄격한 Sanity Check(COV 차단 >= 3400mV, 경고 >= 3300mV 등)로 인해 저장이 거부되고 이전 값으로 롤백되는 문제 해결.
- **확장된 파라미터 시험 가능 범위**:
- **셀 전압 (CUV/COV)**:
- CUV(저전압 차단/경고): 기존 1000~3000mV ➔ **500mV ~ 4000mV**
- COV(과전압 경고/차단): 기존 3300~4200mV ➔ **1000mV ~ 5000mV**
- 사용자가 시험 목적으로 셀 과전압을 `3200mV / 3150mV` 등으로 낮추어 설정 가능.
- **시스템 전압 (SUV/SOV)**:
- 10.0V ~ 75.0V (Raw: 1000 ~ 7500) 범위로 전면 확장하여 저전압/고전압 시뮬레이션 지원.
- **온도 (LT/OT Charge & Discharge)**:
- 기존 -40°C~+30°C / +20°C~+95°C ➔ **-60°C ~ +120°C (Raw: 20 ~ 200)** 대폭 확장.
- 상온(25°C) 환경에서도 저온/고온 경보를 정상적으로 인가 및 시험 가능.
- **전류 (OC Charge & Discharge)**:
- 충전 과전류: 기존 5A~200A ➔ **1A ~ 350A** (미소 전류 트립 시험 가능)
- 방전 과전류: 기존 -600A~-5A ➔ **-1000A ~ -1A**
- **전압 복구 (CUV/COV Recovery)**:
- 기존 고정 하한/상한 제약 해제, **500mV ~ 5000mV** 시험 범위 수용.
- **셀 전압 편차 (Diff)**:
- 기존 50mV~200mV ➔ **10mV ~ 200mV** (10mV 단위 미세 편차 트립 시험 가능, 시간 1초~3600초).
- **셀 밸런싱 (Balancing)**:
- 2000mV ~ 4000mV (저전압 상태에서도 강제 밸런싱 동작 검증 가능).
- **SOC 파라미터**:
- 경고 1% ~ 90%, 복구 2% ~ 95%로 시험 범위 확장.
- **용량 (Capacity)**:
- 1Ah ~ 1000Ah (1000mAh ~ 1000000mAh)
---
10. **펌웨어 업데이트 완료 후 인벤토리(시리얼 넘버/제조일자) 자동 재조회 및 표시 복구** ([`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs), [`csSbCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csSbCanThread.cs), [`csValueCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csValueCanThread.cs), [`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs), [`fmxFwUpdate.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxFwUpdate.cs)):
- **문제 원인**:
- 펌웨어 업데이트 완료 후 BMS MCU가 재부팅될 때 GUI의 자동 폴링이 일시 정지(`AutoTx = false`)되었다가 재개되지만, 송신 명령 포인터(`current_cmd`)가 기존 상태 폴링 주기 루프(`20, 1, 19, 3, 4, 11, 16`)에 그대로 머물러 있어 인벤토리 요청(`900`: 제조일자, `901`: 시리얼 MSB, `902`: 시리얼 LSB)이 다시 전송되지 않음.
- 결과적으로 펌웨어 업데이트 후 새 펌웨어로 기동된 BMS의 시리얼 넘버가 수신되지 않아 메인 타이틀 바에 시리얼 넘버가 표시되지 않는 문제 발생.
- **개선 및 구현 내용**:
1. **`ResetToInventory()` 메서드 구현**:
- 모든 통신 스레드(`csCanThread`, `csSbCanThread`, `csValueCanThread`)에 `ResetToInventory()` 추가.
- `current_cmd = 0` (다음에 즉시 900번 인벤토리 요청 송신)으로 강제 리셋.
- `SystemData[0].Information.BMS_SN` 버퍼를 클리어하여 새 시리얼 넘버 수신 대기 상태로 전환.
2. **자동 송신 재개 시 인벤토리 자동 재조회 (`SetAutoTx(true)`)**:
- FW 업데이트 창이 닫히거나 통신이 재개될 때 `ResetToInventory()`가 자동 호출되어, BMS 리부팅 직후 최우선으로 인벤토리 데이터(`900 ➔ 901 ➔ 902`) 및 기본 파라미터를 먼저 조회하도록 보장.
3. **시리얼 넘버 미수신 시 자가 복구(Self-Healing) 주기 루프 연동**:
- `csCanThread.cs`의 `TxProcess` 주기 완료 지점(`case 16`)에서 시리얼 넘버(`BMS_SN`)가 아직 수신되지 않은 경우(BMS MCU 리부팅 지연 등으로 첫 패킷을 놓친 경우 대비), 자동으로 `ret = 900`으로 복귀하여 시리얼 넘버가 정상 수신될 때까지 1~2초 간격으로 재요청하도록 방어 로직 적용.
4. **펌웨어 업데이트 완료 폼 동작 개선 (`fmxFwUpdate.cs`)**:
- 업데이트 완료 체크섬 확인 후 BMS 재부팅 패킷 전송 및 완료 팝업 확인 시 창을 닫아 즉시 메인 모니터링 화면으로 전환, 인벤토리 재조회가 자동으로 트리거되도록 처리.
---
## 12. 펌웨어 파라미터 유효성 검증 필터 전면 제거 (COV Release 3100mV 등 임의 시험값 100% 수용)
- **배경**:
- 정상 상태 환경에서 BMS 동작 및 보호 기능(COV, CUV, 편차 등)을 시험할 때, 펌웨어 내부에 남아있던 파라미터 유효 범위 검사(`Validate_VoltageRecoveryParam`, `Validate_CellVoltageDiffParam` 등)로 인해 특정 시험값(예: COV 해제 3100mV)이 거절되거나 기존 값(3580mV)으로 롤백되는 문제 발생.
- **수정 내용**:
- [`mBMS_APP_V3117`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_data_process.c), [`APP_R3221`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/can_data_process.c), [`APP_R3323`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/can_data_process.c), [`mBMS_APP_V2403`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403/APP/can_data_process.c)
- `can_data_process.c`의 파라미터 유효성 검사 헬퍼 함수(`Validate_CellVoltageParam`, `Validate_SysVoltageParam`, `Validate_TempParam`, `Validate_CurrentParam`, `Validate_VoltageRecoveryParam`, `Validate_CellVoltageDiffParam`, `Validate_SocParam`, `Validate_CapacityParam`, `Validate_BalancingParam`) 내의 조건 필터를 전면 비활성화하여 **호스트가 전송하는 모든 수치를 그대로 수락하고 Flash에 즉시 기록**하도록 개선.
- 최신 펌웨어 빌드(`V4.0.0.2` / `R402`) 컴파일 완료 및 통합 패키지(`mBMS_KT_V4001.bin`) 반영.
---
## 13. GUI Poll 미체크 시 통신 불량(COMM FAIL) 및 모니터링 데이터 초기화 방지
- **배경**:
- CAN 통신 모니터링 창에서 사용자가 특정 패킷 분석 등을 위해 Polling(`chbPolling`) 체크를 해제할 경우, 10초 타임아웃 감시 스레드가 작동하여 통신 불량(`COMM FAIL`, `ShelfCommFail = true`)으로 전환되고 화면의 셀 전압, 온도 등 모든 모니터링 데이터가 0으로 초기화(`DataInit()`)되는 현상 개선.
- **수정 내용**:
- [`csSbCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csSbCanThread.cs), [`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs), [`csValueCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csValueCanThread.cs):
- `uCanRxThread` / `CanRxThread`에서 10초 타임아웃 검사 시 **`uCanPolling == true` (또는 `CanPolling == true`)일 때만 타임아웃 및 `DataInit()`을 수행**하도록 조건부 방어.
- Polling이 꺼져 있는 동안에는 통신 불량 판단을 일시 정지하고 `LastRxTime`을 갱신하여 데이터 보존.
- `SetPolling(false)` 호출 시 기존에 발생한 `ShelfCommFail` 및 `CommFail` 플래그를 즉시 클리어.
- [`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs):
- `chbPolling_CheckedChanged`에서 Poll 해제 시 `CommFail = false`, `ShelfCommFail = false` 설정 및 기존 모니터링 데이터 보존.
- `MakeTotalStatusByCAN`에서 Polling 미체크 시 `SystemTotalData.CommFail = false` 유지.
---
## 14. 펌웨어 업데이트 화면 하드웨어 3종 버전 정보 전면 보안 은닉 및 개발자 비밀 조회 기능
- **배경**:
- 일반 사용자/고객에게 3종 하드웨어 파편화 내역(HW1/HW2/HW3, STM32/HC32/PL455/BQ76940/ADBMS1818 칩셋명 등)이 노출되지 않도록 전면 은닉 요구.
- 내부 개발자만 필요 시 대상 보드의 하드웨어 감지 상태와 패키지 내 3종 바이너리 매칭 상세 내역을 확인할 수 있는 비공개 기능 요구.
- **수정 내용** ([`fmxFwUpdate.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxFwUpdate.cs)):
1. **일반 UI 표시 정보 전면 은닉**:
- 보드 정보 영역: `HW3:HC32+ADBMS1818`, `x1xx→V1, x2xx→V2` 등의 파편화 텍스트 전면 삭제. `System ID: 1, FW Ver: V4.0.0.1` 형태로만 깔끔하게 표기.
- 펌웨어 파일 정보 영역: 3종 포함 목록(HW1/HW2/HW3 리스트) 및 타겟 매칭 텍스트 삭제. 일반 사용자 화면에는 단일 통합 펌웨어(`mBMS_KT_V4001.bin`), 버전(`V4.0.0.1`), 파일 크기, `준비 완료`로만 표기되어 내부 구조를 전혀 알 수 없도록 보호.
2. **개발자 비밀 조회 기능 (Developer Secret View)**:
- **조회 방법 1**: `lbFileInfo` (펌웨어 정보) 라벨 **더블클릭**
- **조회 방법 2**: FW 업데이트 창 어디서나 **`Ctrl + Shift + D`** 단축키 입력
- **동작**: 일반 사용자에게는 숨겨진 내부 감지 하드웨어 타입, 매칭된 서브 바이너리 오프셋/크기/체크섬, 3종 전체 패키지 매칭 정보(`devDetailInfo`)를 개발자 전용 팝업 창(MessageBox)으로 상세 표출.
---
## 15. CAN 패킷 모니터 일시정지(Pause) 즉각 고정(Freeze), 큐 지연(Lag) 방어 및 창 닫힘 시 수집 완전 차단
- **배경**:
- 패킷 모니터링 창에서 패킷이 장시간 누적(수만 개 유입)된 상태에서 `❚❚ Pause`를 눌렀음에도 불구하고, UI 내부 큐(`_incomingQueue`)에 대기 중이던 수천 개의 잔여 패킷이 UI 타이머에 의해 계속 끄집어내져 화면이 멈추지 않고 끝없이 스크롤/변화하는 현상 발생.
- 패킷 모니터링 창을 열지 않았거나 사용자가 창을 닫았음에도 불구하고 백그라운드 통신 스레드에서 패킷 로그 수집 및 큐잉이 계속 돌아 리소스를 소모하고 지연이 생기는 문제 해결 요구.
- **수정 내용**:
1. [`CanPacketLogService.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Services/CanPacketLogService.cs):
- `IsActive` 속성 추가 (기본값: `false`): 창이 열려 있지 않거나(`!IsActive`) 일시정지(`IsPaused`) 상태일 때는 `AddPacket()` 진입 즉시 `return`하여 패킷 수집 및 파싱, 큐 할당을 100% 차단 (CPU/메모리 소모 0).
- `AddPacket()`에 큐 한도 방어 로직 추가: UI 처리 속도보다 패킷 유입이 빨라 큐가 300개를 초과할 경우 오래된 미처리 큐를 즉시 폐기(Drop)하여 실시간 지연이 0.2초 이상 발생하지 않도록 차단.
- `DiscardIncomingQueue()` 메서드 추가: 일시정지 또는 창 닫힘 시 대기 중인 잔여 큐를 즉시 전면 소각하여 그 시점의 화면을 즉시 동결.
2. [`fmxCanLog.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCanLog.cs):
- 창 라이프사이클 연동 (`VisibleChanged` & `FormClosing`):
- **창이 닫히거나 숨겨졌을 때**: `_logService.IsActive = false`, `tmrUpdate.Stop()`, `DiscardIncomingQueue()`를 수행하여 백그라운드에서 패킷이 패킷 창/서비스로 들어가지 않도록 원천 차단.
- **창이 열렸을 때**: 그 순간부터 `_logService.IsActive = true`, `tmrUpdate.Start()`를 가동하여 창을 열고 있는 동안에만 실시간 패킷 수집 및 표시.
- `tmrUpdate_Tick`: 일시정지(`_logService.IsPaused`) 상태일 때는 디큐 및 그리드 추가를 즉시 리턴하여 **누르는 즉시 화면이 0ms로 완벽히 얼어붙도록(Instant Freeze)** 수정.
- `btnPause_Click`: Pause 및 Resume 전환 시 `DiscardIncomingQueue()`를 호출하여 밀려있던 과거 잔여 패킷으로 인한 지연 스크롤 현상 원천 차단.
- `TrimGridRows`: 50개 이상 초과 시 반복 `RemoveAt(0)` 대신 `RebuildGridFromDisplayedItems()`를 호출하여 UI 버벅임 및 스레드 병목 완전 제거.
---
---
## 17. V1(STM32) 인벤토리 데이터 Flash 영구 저장 및 CAN 통신(900, 901, 902) 연동 (V2/V3 호환)
- **배경**:
- V2, V3(HC32)에는 인벤토리 데이터(제조일자, 시리얼 번호, 모듈 번호 등)를 내부 Flash에 저장하고 CAN 통신(우선순위 Index 1)을 통해 조회/설정하는 기능이 구현되어 있었으나, V1(STM32F407)에는 해당 기능이 미구현 상태였음.
- V2, V3의 인벤토리 저장 및 통신 구조를 참고하여 V1 펌웨어에도 동일한 메모리 보존 및 프로토콜 규격을 구현 요구.
- **수정 내용**:
1. **Flash 메모리 맵 할당 ([`flash_if.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/flash_if.h))**:
- 기존에 사용되지 않던 STM32F4 Flash **Sector 2 (`0x08008000`, 16KB)**를 인벤토리 전용 베이스 주소(`mBMS_INV_BASE_ADDR`)로 지정.
- 기존 파라미터 영역(Sector 3, `0x0800C000`)과 완전히 분리되어, 파라미터 초기화 시에도 인벤토리 정보가 절대 지워지지 않고 영구 보존됨.
2. **인벤토리 구조체 및 영구 저장 로직 구현 ([`device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/device_param.h), [`device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/device_param.c))**:
- `TDEVICE_INV` 구조체 정의 (`initdata = 0xAAAA4001`, `ManufactureDate`, `SerialNo[16]`, `ModuleNo[32]` 등).
- `Inv_Init()`: 초기화 시 기본 제조일자(UTC 타임스탬프), Serial No("AMOTECH-00000000"), Module No("KT-BMS-00000000-0000000000000000") 설정.
- `Load_InvData()`: 부팅 시 Sector 2에서 인벤토리 데이터 로드, 미기록 상태(`initdata != 0xAAAA4001`) 감지 시 기본값 자동 기록.
- `Save_InvData()`: Sector 2 언락, 섹터 삭제 후 Flash 워드 단위 프로그래밍 및 Lock 수행.
- `Load_Param()` 마지막에 `Load_InvData()`를 연동하여 전원 투입 시 자동 로드.
3. **CAN 우선순위 1 인벤토리 패킷 처리 연동 ([`can_data_process.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_data_process.h), [`can_data_process.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_data_process.c), [`can_comm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_comm.c))**:
- `CheckCanPacket`: CAN ID의 우선순위 필드가 `0x1`인 경우 인벤토리 패킷 타입(3)으로 분기.
- `MakeCanTxExtId`: 장치 주소 및 인벤토리 응답 시 `Index = 1` 매핑.
- `CanRxProcess`: 패킷 타입 3 수신 시 `CanRxInvDataProcess()` 호출.
4. **인벤토리 전용 CAN 통신 처리 모듈 신규 구현 ([`can_inv_data_process.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_inv_data_process.h), [`can_inv_data_process.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/can_inv_data_process.c))**:
- V3의 구현과 100% 동일하게 CAN 수신/응답 로직 구현:
- **CAN 900 (제조일자)**: PF 1(요청) → PF 11(응답), PF 91(설정) → Flash 저장 후 PF 11 응답.
- **CAN 901 (Serial No)**: PF 2/3(요청) → PF 21/31(응답, 16바이트), PF 92/93(설정) → Flash 저장 후 PF 21/31 응답.
- **CAN 902 (Module No)**: PF 4~7(요청) → PF 41~71(응답, 32바이트), PF 94~97(설정) → Flash 저장 후 PF 41~71 응답.
- 콘솔 디스플레이 및 설정 처리 함수(`Process_InvDataSet`, `Dis_InventoryData`) 연동.
5. **날짜 계산 유틸리티 보완 ([`date_calc.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/date_calc.h), [`date_calc.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/date_calc.c))**:
- `DateTimeStrToTimeStamp(u8 *DateTime)` 및 `StrToInt32()` 구현.
- 인코딩 안전성을 위해 요일 문자열 `fstrWeek`를 영문("Sun", "Mon", ...)으로 통일.
6. **콘솔 디버그 및 화면 연동 ([`console.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/console.c))**:
- `Process_Inventory_Data`에서 인벤토리 설정 텍스트 파싱 연동.
- `DisplayScreen` 및 `ViewScreen`에 `INVENTORY_MODE` 화면 출력 연동.
---
---
## 18. 인벤토리 미지원 구버전(Legacy) V1 펌웨어 보드 연결 시 무한 인벤토리 조회 및 통신 불능(Freeze) 방어
- **배경**:
- 인벤토리 기능이 탑재되지 않은 기존 구버전 V1 펌웨어가 들어가 있는 보드와 GUI가 통신할 때, GUI가 900(제조일자), 901/902(시리얼 번호)를 계속 조회하지만 보드가 응답하지 못함.
- 기존 코드에서는 `ShelfCommFail == true`가 되면 `TxProcess()`가 무조건 `return 900;`을 반환하여, 900만 무한 전송하고 정작 배터리 모니터링 패킷(Status, Cell Voltage, Temp 등)을 일체 요청하지 못해 통신이 완전히 죽어버리는 현상 발생.
- 또한 시리얼 번호가 미수신(`snEmpty == true`)된 상태에서 매 주기마다 900으로 재진입하여 심각한 통신 지연 및 타임아웃 발생.
- **수정 내용** ([`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs), [`csSbCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csSbCanThread.cs), [`csValueCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csValueCanThread.cs)):
1. **통신 불량(`ShelfCommFail == true`) 시 복구 패킷 변경**:
- 기존의 `return 900;`을 삭제하고, 구버전/신버전/V1/V2/V3 모든 펌웨어가 100% 응답하는 기본 상태 패킷인 **`return 1;` (Status #1~#4 요청)**으로 변경.
- 통신이 끊기더라도 보드가 살아있으면 즉시 기본 상태 패킷을 수신하여 `ShelfCommFail = false`로 즉각 자동 복구됨.
2. **구버전 펌웨어 지능형 감지 및 인벤토리 자동 우회(Fallback)**:
- 인벤토리 조회를 최대 3회 시도해도 인벤토리 패킷(Index 0x01) 응답이 전혀 오지 않으면, "인벤토리를 지원하지 않는 구버전(Legacy) 펌웨어"로 자동 판정 (`_invSupported = false`).
- 이후에는 900, 901, 902 인벤토리 조회를 완전히 건너뛰고 일반 배터리 모니터링 패킷(20 -> 1 -> 19 -> 3 -> 4 -> 11 -> 16 -> 20 ...)만 딜레이 없이 고속 순환.
- 이를 통해 **인벤토리 기능이 없는 기존 구버전 V1 보드도 전혀 통신 멈춤이나 지연 없이 셀 전압, 전류, 온도 등이 100% 정상 모니터링**됨.
3. **신규 펌웨어 업데이트 및 재연결 시 자동 복원**:
- 사용자가 보드를 신규 V4.0.0.1 펌웨어로 업데이트하거나, 통신 재연결 시 `ResetToInventory()`가 호출되어 `_invSupported = true`, `_invRetryCount = 0`으로 자동 리셋.
- 신버전 펌웨어 연결 시에는 다시 정상적으로 인벤토리(제조일자, 시리얼, 모듈 번호)를 로드함.
---
---
## 19. 모듈 ID 변경(Prev, Next, TextEdit) 시 이전 모듈 잔여 데이터 즉시 전면 초기화(Clear)
- **배경**:
- GUI에서 `Prev` 또는 `Next` 버튼을 누르거나 `edSystemId`를 수정하여 모듈 ID를 변경할 때, 기존 모듈의 셀 전압, 전류, 온도, 알람, 펌웨어 버전, 시리얼 번호 등이 화면에 그대로 남아있어 새로 선택한 모듈의 데이터인지 이전 모듈의 잔여 데이터인지 혼동을 일으키는 문제 발생.
- 특히 선택한 모듈이 통신이 연결되어 있지 않은 상태에서도 이전 모듈의 정상 전압과 상태가 마치 현재 모듈 데이터인 것처럼 잔존하는 현상 개선 요구.
- **수정 내용**:
1. **모듈 데이터 전면 초기화 함수 구현 ([`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs))**:
- `ClearModuleData()` 메서드 신설:
- `DataInit()` 및 `csUtils.DataInit(Config, SystemData[0])` 호출로 메모리 내 모든 셀 전압, 온도, 전류, 알람, 파라미터, 인벤토리 데이터를 0으로 완전 초기화.
- 통신 상태를 `ShelfCommFail = true`로 설정하여 새 모듈의 첫 응답 패킷을 기다리는 상태(`WAITING`)로 전환.
- 탭 헤더 라벨을 `LFP SYSTEM - Module {CurrSystemID}`로 즉시 초기화하여 이전 모듈의 펌웨어 버전/시리얼 번호 표기를 소거.
- `ShelfStatus.ClearDisplay()` 및 `ucCalibrationView1.DisplayValue()` 호출로 화면 컨트롤을 즉시 0으로 소거.
2. **모듈 ID 변경 이벤트 연동 ([`fmxMain.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxMain.cs))**:
- `btnPrev_Click`, `btnNext_Click`, `edSystemId_EditValueChanged`에서 모듈 ID가 변경되는 즉시 `ClearModuleData()`를 호출.
- 통신 스레드의 `ResetToInventory()`를 함께 호출하여 새 모듈에 대한 인벤토리 및 파라미터 조회를 1부터 신선하게 시작하도록 리셋.
3. **모듈 상세 화면 컨트롤 즉시 소거 기능 구현 ([`ucShelfStatus.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Controls/ucShelfStatus.cs))**:
- `ClearDisplay()` 메서드 추가:
- 전압(`0.0`), 전류(`0.0`), SOC(`0.0`), SOH(`0.0`), 온도, 하트비트 라벨 초기화.
- 통신 상태 및 알람 텍스트를 `WAITING` (회색)으로 변경.
- 셀 전압 차트(`dtVolt`) 및 온도 차트(`dtTemp`)의 모든 바/포인트를 0으로 초기화.
- 11개 보호/경보 램프(`alarmTrip`)를 전원 정상/꺼짐(`SetValue(0)`) 상태로 즉각 리셋.
- `UpdateSystemId(sId)` 내부에서 `ClearDisplay()`를 자동 호출하도록 연동.
---
---
## 20. 하단 알람 로그(Alarm Log) 저장 시 모듈 번호(sno) 1-based 체계 확인 및 실제 모듈 ID 연동
- **배경**:
- 하단 `Event` - `Alarm Log` 탭의 그리드에서 알람 발생/해제 기록 시 모듈 번호(`sno` 컬럼)가 0-based인지 1-based인지 확인 및 검증 요구.
- 소스코드 분석 결과:
- 알람 로그의 모듈 번호(`sno`)는 **1부터 시작하는 1-based(1-기반)**입니다 (모듈 1은 `1`, 모듈 2는 `2`...).
- `0`은 개별 모듈이 아닌 **"시스템 전체 알람 (`SYSTEM COMM FAIL`)"**을 구분하기 위한 시스템 예약 ID로 사용됩니다.
- 기존 코드의 잠재적 버그 발견: CAN 통신 모드에서는 단일 모듈 배열(`i = 0`)만 사용하여, 사용자가 2번이나 3번 모듈을 모니터링하다가 발생한 알람도 무조건 `0 + 1 = 1`로 저장되던 문제점 발견.
- **수정 내용** ([`csDbThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csDbThread.cs)):
1. **실제 모듈 번호(`data.mNo`) 반영**:
- `CheckAndLogModuleAlarms()` 및 `DbThreadProcess()`의 `MODULE_COMM_FAIL` 처리부에서:
- 고정 인덱스 `mIdx + 1` 대신, 현재 모니터링 중인 실제 모듈 번호인 `int moduleNo = (data.mNo > 0) ? data.mNo : (mIdx + 1);`를 사용하도록 수정.
- 이제 사용자가 Prev/Next 또는 입력으로 2번, 3번, 4번 등 특정 모듈을 선택하여 모니터링할 때 알람이 발생하면, **선택된 모듈의 실제 번호(1-based)가 `sno` 컬럼에 정확하게 기록**됨.
---
## 21. 최종 컴파일 및 배포 바이너리 현황
| 대상 컴포넌트 | 변경 내용 | 빌드 및 검증 결과 |
| :--- | :--- | :---: |
| **V1 STM32 펌웨어** ([`mBMS_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/FLASH/mBMS_APP_V4001.bin)) | Flash Sector 2 인벤토리 영구 저장, CAN 우선순위 1 (900/901/902) 송수신 완벽 구현 | **Keil UV4 컴파일 0 Error / 0 Warning, 67,696 bytes 생성** |
| **통합 패키징 스크립트** ([`package_multi_fw.py`](file:///d:/Project/Firmware/KT/mBMS/R4xx/package_multi_fw.py)) | 인벤토리가 적용된 최신 V1, V2, V3 바이너리 취합 및 3종 통합 패키징 | **183,916 bytes 생성, CRC32(`0xD7058FE7`) 검증 100% 통과** |
| **GUI 프로그램** ([`LFP_Manager_GL.exe`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/bin/Release/LFP_Manager_GL.exe)) | 셀발란싱 파라미터(10) 자동 조회/수동 Get 연동, 알람 로그 `sno` 1-based 및 실제 모듈 ID 반영, 모듈 변경 시 데이터 즉시 소거, 구버전 통신 고착 방어 | **VS2022 MSBuild Release 빌드 성공 (0 Error)** |
| **배포 패키지** ([`mBMS_KT_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_KT_V4001.bin)) | GUI Release 폴더 및 최상위 루트 디렉토리 동시 배포 | **최신 동기화 완료** |
---
## 22. V1 셀발란싱 파라미터(Cell Balancing Parameter) 조회 기능 연동 및 GUI 동기화 개선
- **배경**:
- `Calibration` 창의 `Cell Balancing Parameter` 영역(Threshold, Window, End Gap, Interval)에서 V1 모듈의 파라미터가 조회되지 않던 문제 해결 요구.
- 분석 결과:
1. **V1 펌웨어 측면**: V1 펌웨어(`APP_R3323`)는 이미 `CAN_REQ_BALANCING_PARAM_CODE (10)` 요청 수신 시 `CAN_RSP_BALANCING_PARAM_CODE (101)`로 셀발란싱 파라미터를 정상 응답하는 로직이 완성되어 있음.
2. **GUI 주기 폴링 누락**: GUI의 CAN 통신 스레드(`csCanThread`, `csSbCanThread`, `csValueCanThread`)의 초기/주기 폴링 시퀀스에 배터리 용량(8), 충전옵션(15), 캘리브레이션 K값(17)만 포함되고, **셀발란싱 파라미터(10)가 누락**되어 모듈 연결 시 자동으로 읽어오지 못함.
3. **수동 Get 및 CAN 드라이버별 누락**:
- `csSbCanThread` 및 `csValueCanThread`의 `SendProcessFromApp`에 `case 10:` 핸들러가 누락되어 Get/Set 명령이 전송되지 않음.
- `csCanThread`의 `GetExpectedResponse`에 `case 10: expPf = 101`이 등록되지 않아 응답 대기 동기화 누락.
- `ucCalibration.cs`의 `btnGetCbParam_Click`에서 불필요하게 New 텍스트박스 파싱을 시도하다 예외가 발생할 수 있던 문제.
4. **화면 실시간 반영 누락**:
- `Calibration` 창(`fmxCalibration`)이 열릴 때 최신 파라미터 자동 조회가 요청되지 않았고,
- `ucShelfStatus.cs`의 `UpdateData`에서 열려 있는 `CalibrationForm`으로 새 데이터를 전달하지 않아 수신된 파라미터가 화면에 즉시 갱신되지 못함.
- **수정 내용**:
1. **CAN 통신 스레드 폴링 시퀀스에 셀발란싱 파라미터(10) 추가**:
- [`csCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csCanThread.cs): `TxProcess` 시퀀스(8 -> 15 -> 17 -> **10** -> 20) 및 `GetExpectedResponse`에 `case 10: expPf = 101` 등록.
- [`csSbCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csSbCanThread.cs): 폴링 루프(8 -> 15 -> 17 -> **10** -> 20) 및 `SendProcessFromApp`에 `case 10:` 추가.
- [`csValueCanThread.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Threads/csValueCanThread.cs): 폴링 루프(8 -> 15 -> 17 -> **10** -> 1) 및 `SendProcessFromApp`에 `case 10:` 추가.
2. **`Calibration` 창 오픈 시 자동 조회 및 실시간 반영 연동**:
- [`fmxCalibration.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Forms/fmxCalibration.cs): `Shown` 이벤트 핸들러를 추가하여 창이 열릴 때 셀발란싱 파라미터(10), 배터리 파라미터(8), 전류 캘리브레이션(17)을 즉시 1회 자동 조회.
- [`ucShelfStatus.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Controls/ucShelfStatus.cs): `UpdateData()`에서 열려 있는 `CalibrationForm`에 `UpdateData()`를 호출하여 응답 수신 시 UI를 즉시 갱신하도록 연결.
- [`ucCalibration.cs`](file:///d:/Project/Firmware/KT/mBMS/R4xx/gui/LFP_Manager_GL(V3.3.0.16)_AG/LFP_Manager/Controls/ucCalibration.cs): `btnGetCbParam_Click`의 불필요한 파싱 로직을 제거하고 안전한 순수 Get 명령(`OnCommnadEvent(10, CALC_INDEX, 0, ...)`)으로 수정.
- **검증**:
- `LFP_Manager.sln` Release 빌드 완료 (**0 Error, 1 Warning**).
- 모듈 연결 및 모듈 변경 시 셀발란싱 파라미터가 자동으로 조회되어 `Calibration` 창에 정확하게 표시됨.
**문서 끝.**
## [2026-11-02] V1.1.0 CAN 통신 프로토콜 개정 및 규격서 반영
- **Status #1 (PF: 11)**: Byte 0, Bit 5에 `cell_voltage_diff` (셀 전압 편차 >= 200mV 24시간 지속 차단 래치) 규격 명시
- **Status #2 (PF: 12)**:
- Byte 0, Bit 0에 `temp_diff` (모듈 내 온도 편차 >= 3.0℃ 24시간 지속 차단 래치) 추가
- Byte 1, Bit 1에 충전 과전류 60초 내 3회 발생 시 영구 락아웃 명시
- Byte 3~5에 Secret Identifier (Byte 3: 0x57 'W', Byte 4: HW Type, Byte 5: Build Rev 0x02) 규격 반영
- **Heartbeat (PF: 201)**: Byte 6~7에 셀/온도 편차 차단 카운트다운 잔여 시간(`GetCvDiff()`, 초 단위) 명시
- **Cell Voltage Diff Parameter (PF: 2 -> 21)**:
- 요청 프레임 Byte 4~7의 지연 시간 설정 시 `Temp_Diff_ProtectHoldTime` 동시 설정 및 RAM 가변 시험 지원(0=24h 기본값) 명시
- 응답 프레임 Byte 2~3에 현재 설정된 온도 편차 임계치(`Get_Temp_Diff_Threshold()`, 0.1℃ 단위) 반환 규격 반영
- **Cell Temp Diff Parameter (PF: 26)**: 독립 전용 온도 편차 파라미터 프레임 정의 추가
- **문서 산출물 동기화**: `KT_mBMS_CAN_통신프로토콜규격서.md`, `KT_mBMS_CAN_Communication_Protocol_Specification.md`, Word(.docx) 문서 4종 전체 갱신 완료
## [2026-11-03] 충전 과전류 3회 완전 차단(Critical Lockout) 전용 별도 알람 분리 및 카운트 보정
- **3회 카운트 판정 보정 (`alarm.c`)**: `OCC_Fail_Count` 누적 시 정확히 3회째에 즉시 `OCC_3Times_Fail = true` 및 완전 차단 래치 동작 (기존 4회째 차단 버그 개선)
- **Status #2 (PF: 12) 알람 비트 분리 (`can_data_process.c`)**:
- **Byte 0, Bit 1 (신규)**: `critial_over_current` (충전 과전류 3회 영구 차단 락아웃 전용 알람 비트 할당)
- **Byte 1, Bit 1**: `chg_over_current` (일시적 1~2회차 충전 과전류 단독 차단 알람으로 분리)
- **GUI 수신 로직 연동 (`csCanCommFunction.cs`)**: Status #2 Byte 0, Bit 1의 3회 완전 차단 플래그(`bFault |= (1 << 8)`) 파싱 추가
- **적용 대상**: `APP_R3221` (HW2), `APP_R3323` (HW3), `mBMS_APP_V3117` (HW1) 전체 동기화

View File

@@ -0,0 +1,330 @@
# KT mBMS V1 펌웨어 변경 이력서 (mBMS_APP_V3117)
- **작성일자**: 2026년 10월 26일 (최종 갱신)
- **대상 펌웨어**: [`mBMS_APP_V3117`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117) (오리지널 최신 베이스코드, 프로젝트: `USER/mBMS_APP.uvprojx`)
- **참고 펌웨어**: [`mBMS_APP_V2403`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V2403), [`APP_R3221`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221) (V2), [`APP_R3323`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323) (V3)
- **적용 하드웨어**: 1st KT mBMS-16S Rev 1.0.3 (`sch/1st KT mBMS-16S Rev1.0.3.pdf`)
- **주요 스펙**:
- **MCU**: STM32F407VGT6 (ARM Cortex-M4, 100-pin LQFP)
- **AFE IC**: TI BQ76PL455 (UART 통신, 15S 배터리 모니터링)
- **스위치 방식**: 보드 내 드라이버를 통한 **외부 릴레이 제어** (Active-Low 구동)
- **온도 센서**: STM32 온보드 ADC 8채널 + CPU 내부 온도 센서 (총 11채널 ADC)
- **저장 장치**: 외부 SPI Flash (FatFS 파일시스템)
- **셀 구성**: 15S LiFePO4 (`DEVICE_MAX_CELL = 15`)
- **컴파일 검증**: Keil ARMCC (v5.06 update 6/7) **전체 빌드 100% 성공 (0 Error, 0 Warning)**
---
## 1. 주요 변경 요약
1. **1st 회로도(Rev 1.0.3) 기반 릴레이 제어 핀맵 전면 수정 및 Active-Low 적용**:
- 기존의 타 보드용 핀 매핑(듀얼 FET용 PC6/PC7 등)을 1st 회로도에 맞추어 `PC9`(충전), `PD13`(방전), `PD15`(프리차지) 단독 제어로 수정.
- 방전 센싱 핀을 기존의 잘못된 PD14에서 회로도 Netlist 매핑 핀인 **PD12**로 수정.
2. **부팅 시 릴레이 접점 글리치(Glitch) 방지**:
- STM32F4 리셋 시 GPIO ODR 기본값(0)으로 인해 발생하는 기동 펄스를 방지하기 위해 `GPIO_Init` 전 `GPIO_SetBits()`로 핀 레벨을 HIGH로 미리 설정.
3. **셀 전압 편차 200mV & 24시간 충전 차단 로직 구현**:
- 기본값 500mV → 200mV 변경, 24시간(`86,400,000ms`) 타이머 연동.
- 충전 리플 노이즈로 인한 억울한 타이머 리셋을 방지하는 **히스테리시스 구간(`150mV~200mV` 보류)** 적용.
4. **반복 과충전 전류(OCC) 3회 감지 시 영구 래치 차단 (Lockout)**:
- 과충전 전류 3회 발생 시 전류가 복구 조건으로 내려가더라도 자동 해제 금지, 충전 릴레이 차단 유지.
5. **충전 차단 Flag 적용 및 Standby 복귀**:
- 충전 차단 Flag(`CHG_C == false`) 발생 시 `OP_STS_CHARGING`에서 `OP_STS_STANDBY`로 정상 복귀.
6. **셀 전압 측정 불량(0V / 단선) 감지 및 차단**:
- 1000mV 이하 또는 4500mV 이상 감지 시 `afe_fail` 트리거 및 전면 차단.
- 충전 중(`op_status == 1`) CUV 플래그를 강제로 지우던 결함 코드 삭제.
7. **온도 센서 단선/불량 감지 및 차단**:
- `IsTempSensorFailed()` 구현 (-35℃ 이하 또는 110℃ 이상 감지).
- `TBATTERY_ERROR error` 구조체를 추가하여 V2/V3와 통신 맵 호환성 유지.
8. **AFE Fail 플래그 덮어쓰기 토글/플리커링 버그 수정**:
- 통신 상태와 단선 감지 플래그를 OR 결합하여 안정적인 알람 유지.
9. **공통 펌웨어 버전(V4.0.0.1) 및 V1 비공개 식별자(`0x57`, `0x11`, `0x01`) 적용**:
- 공식 펌웨어 버전을 **`V4.0.0.1`** (`0x4001`)로 통일하여 KT 관제 시스템 및 GUI에 동일 버전 표기.
- CAN Status #2의 미사용 Reserved 필드(Byte 3~5)에 `0x57`('W'), `0x11`(V1), `0x01`(R401)을 패킹하여 개발자 전용 하드웨어 식별 기능 확보.
- 시리얼 콘솔(`APPVerDisplay`) 출력에 `HW1:STM32+PL455, R401-JKW` 상세 표기.
- CAN PF 41 (SOH) 응답 시 Byte 2~3 및 Byte 4~5에 동시 기록하여 GUI 호환성 보장.
10. **인벤토리 데이터 Flash Sector 2 영구 저장 및 CAN 통신(900, 901, 902) 연동 (V2/V3 호환)**:
- 미사용 중이던 STM32F4 Flash Sector 2(`0x08008000`, 16KB)를 인벤토리 베이스 주소(`mBMS_INV_BASE_ADDR`)로 할당.
- `TDEVICE_INV` 구조체 정의 및 `Load_InvData()`, `Save_InvData()`, `Inv_Init()` 구현.
- CAN 수신 시 우선순위 Index 1 패킷(CAN 900 제조일자, 901 시리얼 번호, 902 모듈 번호) 수신 및 조회/설정 응답 로직 완성.
- 콘솔 디스플레이 및 화면(`INVENTORY_MODE`) 연동.
---
## 2. 파일별 상세 수정 내역
### 1) [`HARDWARE/relay.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/HARDWARE/relay.h)
- **변경 내용**: 1st 회로도 기준 Active-Low(0: ON, 1: OFF) 제어 매크로 정의
```c
// [수정 전]
#define CHG_RLY_ON PCout(9) = 0; PCout(7) = 1;
#define CHG_RLY_OFF PCout(9) = 0; PCout(7) = 0;
#define DSG_RLY_ON PCout(6) = 0; PDout(15) = 1;
#define DSG_RLY_OFF PCout(6) = 0; PDout(15) = 0;
#define PRE_CHG_RLY_ON PDout(13) = 0;
#define PRE_CHG_RLY_OFF PDout(13) = 1;
// [수정 후] (1st KT mBMS-16S Rev1.0.3 기준: Active-Low 구동)
#define CHG_RLY_ON PCout(9) = 0;
#define CHG_RLY_OFF PCout(9) = 1;
#define DSG_RLY_ON PDout(13) = 0;
#define DSG_RLY_OFF PDout(13) = 1;
#define PRE_CHG_RLY_ON PDout(15) = 0;
#define PRE_CHG_RLY_OFF PDout(15) = 1;
```
---
### 2) [`HARDWARE/relay.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/HARDWARE/relay.c)
- **변경 내용**:
1. `Relay_Init()`에서 GPIO 출력 모드 전환 전 `GPIO_SetBits`로 HIGH(OFF) 레벨을 미리 출력하여 기동 시 릴레이 접점 튐(Glitch) 원천 차단.
2. 회로도에 따른 핀 재배치 (`PC9/PC8`, `PD13/PD12`, `PD15/PD14`).
3. `Relay_Status()` 방전 센싱 핀을 PD14에서 **PD12**로 정상 매핑, 프리차지 센싱(`PD14`) 추가.
```c
// [수정 후 핵심 코드]
void Relay_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
// Active-Low: Set high before configuring as OUT to prevent glitch
GPIO_SetBits(GPIOC, GPIO_Pin_9);
GPIO_SetBits(GPIOD, GPIO_Pin_13 | GPIO_Pin_15);
/* 1. Charge Relay Control (PC9: CHARGE_CONTROL, Active-Low) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 2. Charge Relay Sense Input (PC8: CHARGE_CON_SEN) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 3. Discharge Relay Control (PD13: DISCHARGE_CONTROL, Active-Low) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 4. Discharge Relay Sense Input (PD12: DISCHARGE_CON_SEN) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 5. Pre-charge Relay Control (PD15: PRECHARGE_CONTROL, Active-Low) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 6. Pre-charge Relay Sense Input (PD14: PRECHARGE_CON_SEN) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
CHG_RLY_OFF;
DSG_RLY_OFF;
PRE_CHG_RLY_OFF;
}
u8 Relay_Status(u8 id)
{
if ((id == 0) || (id == '0'))
return GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_12); // Discharge: PD12
else if ((id == 1) || (id == '1'))
return GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_8); // Charge: PC8
else
return GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_14); // Pre-charge: PD14
}
```
---
### 3) [`APP/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/dflash_sf1.c)
- **변경 내용**: 기본 셀 편차 임계치를 500mV에서 200mV로 변경
```c
// [수정 전]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 500; // mV
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 300; // mV
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 100; // mV
// [수정 후]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200; // mV (KT 요구사항: 200mV)
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 180; // mV
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 150; // mV
```
---
### 4) [`APP/device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/device_param.h)
- **변경 내용**: `TBATTERY_ERROR` 구조체 정의 및 `TDEVICE_STATUS.reserved` 자리에 추가 (통신 맵 호환)
```c
typedef union
{
u16 value;
struct
{
u16 afe_fail : 1; // LSB 0
u16 temp_sensor : 1; // LSB 1
u16 ZVC_S : 1; // LSB 2
u16 RXM : 1; // LSB 3
u16 FD : 1; // LSB 4
u16 FC : 1; // LSB 5
u16 MD : 1; // LSB 6
u16 INIT : 1; // LSB 7
u16 DSG_C : 1; // MSB 8
u16 CHG_C : 1; // MSB 9
u16 ZVC_C : 1; // MSB 10
u16 CB : 1; // MSB 11
u16 AFE : 1; // MSB 12
u16 TDA : 1; // MSB 13
u16 TCA : 1; // MSB 14
u16 ACB : 1; // MSB 15
} bit;
} TBATTERY_ERROR, *pBATTERY_ERROR;
// TDEVICE_STATUS 내 reserved를 error로 변경
typedef struct
{
...
u16 auto_balancing;
TBATTERY_ERROR error; // 16 16 bit
...
} TDEVICE_STATUS;
```
---
### 5) [`APP/device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/device_param.c)
- **변경 내용**: `Load_Param()`에서 200mV 초과값 자동 보정 코드 추가
```c
ADC_Param_Load();
/* KT 요구사항: 셀 편차 200mV 초과 설정 방지 */
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold > 200)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200;
}
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 150;
}
```
---
### 6) [`APP/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/temp.h) / [`APP/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/temp.c)
- **변경 내용**: 온도 센서 단선/불량 감지 함수 `IsTempSensorFailed()` 선언 및 구현
```c
/* KT 요구사항: 온도 센서 단선/불량 감지 */
u8 IsTempSensorFailed(void)
{
for (int i = 0; i < (DEVICE_MAX_TEMP - 2); i++)
{
s16 temp = device_value.Temp.temperature[i];
// -35도 이하 (단선/오픈) 또는 110도 이상 (쇼트/이상)
if ((temp <= -350) || (temp >= 1100) || (ADState(i) == AD_FAIL))
{
return true;
}
}
return false;
}
```
---
### 7) [`APP/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/APP/alarm.c)
- **변경 내용**:
1. `CV_DIFF_PROTECT_HOLD_TIME` (24시간) 타이머 및 히스테리시스 보호 로직 적용.
2. 반복 OCC 3회 영구 래치 차단.
3. 셀 단선/0V 감지 (`cell_fault`) 및 충전 중 CUV 플래그 삭제 버그 제거.
4. 충전 차단 시 `OP_STS_STANDBY` 정상 복귀 상태 천이 개선.
5. AFE Fail 플래그 덮어쓰기 방지 (`AFE | error.afe_fail`).
```c
// 24시간 타이머 정의
#define CV_DIFF_PROTECT_HOLD_TIME (24UL * 60UL * 60UL * 1000UL)
u32 CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
// AFE Fail 플래그 보존
void ProcessAlarm(void)
{
if (device_status.battery_status.bit.INIT)
{
device_status.protection.bit.afe_fail = device_status.battery_status.bit.AFE | device_status.error.bit.afe_fail;
...
// CUV 내 비정상 셀 전압 감지
uint8_t cell_fault = false;
for (int i = 0; i < DEVICE_MAX_CELL; i++)
{
if ((device_value.Cell.voltage[i] <= 1000) || (device_value.Cell.voltage[i] >= 4500))
{
cell_fault = true;
break;
}
}
if (cell_fault)
{
device_status.error.bit.afe_fail = true;
device_status.protection.bit.afe_fail = true;
}
else
{
device_status.error.bit.afe_fail = false;
}
// 24시간 셀 편차 및 히스테리시스 노이즈 방어
void ProcessSafety_CellVoltageDiff(void)
{
...
/* Cell Voltage Diff protection */
if (device_status.protection.bit.cell_voltage_diff)
{
// release check
if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
device_status.protection.bit.cell_voltage_diff = false;
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
else
{
// alarm check
if (device_value.Cell.avg.diff >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
if (CV_Diff_ProtectTime == 0)
{
device_status.protection.bit.cell_voltage_diff = true;
}
}
else if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
```
---
## 3. 검증 결과 및 빌드 산출물
- **컴파일러**: Keil ARM RealView Compilation Tools (armcc.exe v5.06 update 6/7)
- **C 소스코드 일괄 컴파일 결과**: **`mBMS_APP.uvprojx` 등록 C 소스 파일 100% 정상 컴파일 (0 Error, 0 Warning)**
- **빌드 후 자동화 스크립트 반영**:
- [`CopyHex_Flash.bat`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/CopyHex_Flash.bat): `copy Flash\Obj\mBMS_APP.hex Flash\mBMS_APP_V4001.hex`
- [`Hex2Bin_Flash.bat`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/Hex2Bin_Flash.bat): `hex2bin.exe -b .\FLASH\mBMS_APP_V4001.hex`
- **최종 빌드 산출물**:
| 항목 | 파일명 | 위치 | 크기 | 비고 |
| :--- | :--- | :--- | :---: | :--- |
| **HEX 파일** | [`mBMS_APP_V4001.hex`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/FLASH/mBMS_APP_V4001.hex) | `mBMS_APP_V3117/USER/FLASH/` | 190,560 bytes | 최신 수정 코드 완전 링크 |
| **BIN 파일** | [`mBMS_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_APP_V3117/USER/FLASH/mBMS_APP_V4001.bin) | `mBMS_APP_V3117/USER/FLASH/` | 67,724 bytes | Flash Base: `0x08020000`, CRC32: `0x23DCBFA3` |
| **3종 통합 BIN** | [`mBMS_KT_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/mBMS_KT_V4001.bin) | 루트 및 GUI Release | 183,952 bytes | V1/V2/V3 3-in-1 통합 패키지 |

View File

@@ -0,0 +1,353 @@
# KT mBMS V2 펌웨어 변경 이력서 (APP_R3221)
- **작성일자**: 2026년 10월 22일
- **대상 펌웨어**: [`APP_R3221`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221) (프로젝트: `App/MDK/mBMS2.uvprojx`)
- **적용 하드웨어**: 2nd KT mBMS-16S (`sch/2nd KT mBMS-16S.pdf`)
- **주요 스펙**:
- **MCU**: HDSC HC32F460PETB (ARM Cortex-M4, 100-pin LQFP)
- **AFE IC**: TI BQ76940 (I2C 통신, 15S/16S 배터리 모니터링)
- **스위치 방식**: 보드 내 N-MOSFET 드라이버를 통한 **외부 릴레이 제어** (전 모델 동일 구조)
- **저장 장치**: 외부 SPI Flash (FatFS 파일시스템 운용)
- **셀 구성**: 15S/16S 호환 (`DEVICE_MAX_CELL = 15`)
- **컴파일 검증**: Keil ARMCC (v5.06 update 7) **28개 전체 C 파일 빌드 성공 (0 Error)**
---
## 1. 주요 변경 요약
1. **KT 요구사항 1: 셀 전압 편차 200mV & 24시간 충전 차단 로직 구현**:
- 셀 편차 기본 임계치를 기존 500mV에서 **200mV**로 변경하고 초과 설정 시 200mV로 자동 클램핑.
- 셀 편차가 200mV 이상 지속 시 24시간(`86,400,000ms`) 타이머 연동 및 만료 시 충전 차단(`CHG_C = false`).
- 충전 전류 리플 노이즈로 인한 억울한 타이머 리셋을 방지하는 **히스테리시스 구간(`150mV~200mV` 보류 유지)** 적용.
2. **KT 요구사항 2: 반복 과충전 전류(OCC) 3회 감지 시 영구 래치 차단 (Lockout)**:
- 과충전 전류가 1분 이내 3회 반복 발생 시 `OCC_3Times_Fail = true`로 영구 래치 록아웃.
- 전류가 복구 조건으로 내려가더라도 자동 복구 금지, 충전 FET 영구 차단 유지.
3. **KT 요구사항 3: 통신 프로토콜 호환성 확보 (`error` 구조체 추가)**:
- `TDEVICE_STATUS` 구조체 내 기존 미사용 `uint16_t reserved;` (Offset 16) 자리를 V3와 동일한 `TBATTERY_ERROR error;` (16비트 비트필드)로 변경하여 통신 맵 바이트 정렬 100% 보존.
4. **KT 요구사항 4: 충전 차단 Flag 적용 및 Standby 정상 복귀**:
- `CHG_C == false` 발생 시 운용 상태(`op_status`)가 `OP_STS_CHARGING`에 머무르지 않고 `OP_STS_STANDBY`로 정상 천이하도록 로직 보완.
5. **크리티컬 결함 수정: 셀 전압 0V / 단선 감지 및 비정상 측정 차단**:
- 어떤 셀이라도 1000mV 이하 또는 4500mV 이상 감지 시 `afe_fail` 처리 및 충전 차단.
- 기존 코드에서 충전 중(`op_status == 1`) CUV 플래그를 강제로 0으로 지워버리던 심각한 잠재 위험 버그 제거.
6. **크리티컬 결함 수정: 온도 센서 단선/불량 감지 및 충전 차단 연동**:
- `IsTempSensorFailed()` 구현 (ADC 고장, 단선 -35℃ 이하 또는 125℃ 이상 감지).
- 온도 센서 고장 감지 시 `device_status.error.bit.temp_sensor = true` 세팅 및 FET 충전 전면 차단.
7. **크리티컬 결함 수정: AFE Fail 플래그 토글/플리커링 버그 방어**:
- 통신 AFE 상태와 단선 감지 플래그를 안전하게 OR 결합(`battery_status.bit.AFE | error.bit.afe_fail`)하여 알람 플리커링 원천 방지.
8. **공통 펌웨어 버전(V4.0.0.1) 및 V2 비공개 식별자(`0x57`, `0x22`, `0x01`) 적용**:
- 공식 펌웨어 버전을 **`V4.0.0.1`** (`0x4001`)로 통일하여 KT 관제 시스템 및 GUI에 동일 버전 표기.
- CAN Status #2의 미사용 Reserved 필드(Byte 3~5)에 `0x57`('W'), `0x22`(V2), `0x01`(R401)을 패킹하여 개발자 전용 하드웨어 식별 기능 확보.
- 시리얼 콘솔(`APPVerDisplay`) 출력에 `HW2:HC32+BQ76940, R401-JKW` 상세 표기.
- CAN PF 41 (SOH) 응답 시 Byte 2~3 및 Byte 4~5에 동시 기록하여 GUI 호환성 보장.
---
## 2. 파일별 상세 수정 내역
### 1) [`App/Source/device_param.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/device_param.h)
- **변경 내용**: `TBATTERY_ERROR` 공용체/구조체 정의 추가 및 `TDEVICE_STATUS` 구조체 Offset 16의 `reserved` 필드를 `error` 필드로 치환 (통신 맵 규격 완벽 일치).
```c
// [수정 전]
typedef struct
{
TBATTERY_STATUS battery_status; // 2 16 bit
uint16_t op_status; // 4 16 bit
uint16_t alarm_status; // 6 16 bit
TWarning warning; // 8 16 bit
TProtect protect; // 10 16 bit
TCellBalanceData cellbalance; // 12 16 bit
uint16_t auto_balancing; // 14 16 bit
uint16_t reserved; // 16 16 bit
...
} TDEVICE_STATUS, *PDEVICE_STATUS;
// [수정 후]
typedef union
{
uint16_t value;
struct
{
uint16_t afe_fail : 1; // Bit 0: AFE 전압측정 불량 (0V/단선)
uint16_t temp_sensor : 1; // Bit 1: 온도 센서 단선/불량
uint16_t reserved : 14;
} bit;
} TBATTERY_ERROR;
typedef struct
{
TBATTERY_STATUS battery_status; // 2 16 bit
uint16_t op_status; // 4 16 bit
uint16_t alarm_status; // 6 16 bit
TWarning warning; // 8 16 bit
TProtect protect; // 10 16 bit
TCellBalanceData cellbalance; // 12 16 bit
uint16_t auto_balancing; // 14 16 bit
TBATTERY_ERROR error; // 16 16 bit (V3 호환 구조체 바이트 정렬 완벽 보존)
...
} TDEVICE_STATUS, *PDEVICE_STATUS;
```
---
### 2) [`App/Source/device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/device_param.c)
- **변경 내용**: 부팅 시 파라미터 로드 후 KT 요구사항 규격(셀 편차 200mV 이하)으로 자동 클램핑 보정.
```c
// [수정 후 추가 코드]
/* KT 요구사항: 셀 편차 200mV 초과 설정 방지 */
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold > 200)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200;
}
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 150;
}
```
---
### 3) [`App/Source/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/dflash_sf1.c)
- **변경 내용**: 기본 파라미터 셋업 시 셀 편차 경보/보호 기본값을 200mV로 지정.
```c
// [수정 전]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 500;
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 300;
// [수정 후]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200; // mV (KT spec: 200mV)
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 200; // mV (KT spec: 200mV)
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 100; // mV
device_param.safety.cv_diff.Cell_Voltage_Diff_Time = 60; // Sec
```
---
### 4) [`App/Source/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/temp.h) & [`App/Source/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/temp.c)
- **변경 내용**: 온도 센서 단선/불량 여부를 안전 시스템에 제공하기 위한 `IsTempSensorFailed()` 인터페이스 구현.
```c
// [App/Source/temp.h]
uint8_t IsTempSensorFailed(void);
// [App/Source/temp.c]
uint8_t IsTempSensorFailed(void)
{
for (int i = 0; i < TEMP_CH_MAX; i++) {
if (TD[i].Enabled && TD[i].Failed) {
return true;
}
}
return false;
}
```
---
### 5) [`App/Source/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/Source/alarm.c)
- **변경 내용**: KT 핵심 5대 요구사항 및 안전 로직 전면 개선 반영.
#### ① 24시간 타이머 정의 및 히스테리시스 구간(`150mV~200mV`) 보류
```c
// [수정 전]
#define CV_DIFF_PROTECT_HOLD_TIME (60 * 1000)
// [수정 후] (KT 요구사항: 24시간 타이머)
#define CV_DIFF_PROTECT_HOLD_TIME (24UL * 60UL * 60UL * 1000UL) // 24 hours in ms
```
```c
void ProcessSafety_CellVoltageDiff(void)
{
...
/* Cell Voltage Diff protection */
if (device_status.protect.bit.cell_voltage_diff)
{
// release check
if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
device_status.protect.bit.cell_voltage_diff = false;
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
else
{
// alarm check (200mV 이상 시 타이머 감점)
if (device_value.Cell.avg.diff >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
if (CV_Diff_ProtectTime == 0)
{
device_status.protect.bit.cell_voltage_diff = true;
}
}
// 히스테리시스: 150mV 미만으로 완전히 회복되었을 때만 타이머 리셋 (노이즈로 인한 억울한 리셋 방지)
else if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
}
```
#### ② 반복 과충전 전류(OCC) 3회 감지 시 영구 래치 차단 (Lockout)
```c
void ProcessSafety_OCC(void)
{
/* Over charge current protection */
if (device_status.protect.bit.chg_over_current)
{
// release check: DO NOT auto-recover if repeated OCC fail occurred (Latched)
if (OCC_3Times_Fail)
{
device_status.protect.bit.chg_over_current = true;
}
else
{
if ((device_value.Current / 1) < (device_param.safety.current.OC_Chg_Recovery * 10))
{
if (OCCTime == 0)
device_status.protect.bit.chg_over_current = false;
}
else
{
OCCTime = device_param.safety.current.OC_Chg_Rec_Time * 1000;
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
}
}
else
{
// alarm check
if ((device_value.Current / 1) > (device_param.safety.current.OC_Chg_Threshold * 10))
{
OCC_Occur_Current = device_value.Current;
device_status.protect.bit.chg_over_current = true;
OCCTime = device_param.safety.current.OC_Chg_Rec_Time * 1000;
if (OCC_Fail_Count < 3)
{
OCC_Fail_Count++;
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
else
{
OCC_3Times_Fail = true; // 3회 누적 시 영구 래치
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
}
}
if (OCC_3Times_Fail == false)
{
if (OCC_Fail_Count > 0)
{
if (OCC_CRITICAL_Tick == 0)
{
OCC_Fail_Count = 0;
}
}
}
device_status.battery_status.bit.TCA = OCC_3Times_Fail;
device_status.protect.bit.critial_over_current = OCC_3Times_Fail;
...
}
```
#### ③ 충전 중 CUV 플래그 강제 삭제 버그 제거 및 0V/단선 감지 차단
```c
// [수정 전 버그 코드: 충전 중이면 CUV를 무조건 0으로 클리어하여 심각한 위험 초래]
// if (device_status.op_status == 1) {
// device_status.protect.bit.cell_under_voltage = 0;
// }
// [수정 후 안전 코드: 1000mV 이하 / 4500mV 이상 비정상 측정 감지]
uint8_t cell_fault = false;
for (int i = 0; i < DEVICE_MAX_CELL; i++)
{
if ((device_value.Cell.voltage[i] <= 1000) || (device_value.Cell.voltage[i] >= 4500))
{
cell_fault = true;
break;
}
}
if (cell_fault)
{
device_status.error.bit.afe_fail = true;
device_status.protect.bit.afe_fail = true;
}
else
{
device_status.error.bit.afe_fail = false;
}
```
#### ④ 온도 센서 고장 및 AFE Fail 시 안전 차단 및 AFE Fail OR 결합
```c
void ProcessAlarm(void)
{
if (device_status.battery_status.bit.INIT)
{
// AFE 통신 상태와 단선 감지 플래그를 안전 결합 (플리커링 방어)
device_status.protect.bit.afe_fail = device_status.battery_status.bit.AFE | device_status.error.bit.afe_fail;
...
}
}
void Process_FetControl(void)
{
if (IsTempSensorFailed())
{
device_status.error.bit.temp_sensor = true;
}
else
{
device_status.error.bit.temp_sensor = false;
}
if ((device_status.protect.bit.afe_fail) || (device_status.error.bit.temp_sensor)) {
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.DSG_C = false;
device_status.battery_status.bit.ZVC_C = false;
return;
}
...
// OCC 3회 영구 래치 시 충전 차단 유지
else if (OCC_3Times_Fail || device_status.protect.bit.critial_over_current)
{
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.ZVC_C = false;
}
// 셀 편차 24시간 보호 시 충전 차단
else if (device_status.protect.bit.cell_voltage_diff)
{
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.ZVC_C = false;
}
}
```
#### ⑤ 충전 차단 Flag(`CHG_C == false`) 발생 시 Standby 복귀
```c
case OP_STS_CHARGING: // Charging
if (device_value.DCurrent > (device_param.chg_ctrl.current_thr.Discharge_Detection_Threshold / 100)) {
device_status.op_status = OP_STS_DISCHARGING;
} else if ((device_value.CCurrent < ((device_param.chg_ctrl.current_thr.Charge_Detection_Threshold - 1000) / 100)) ||
(device_status.battery_status.bit.CHG_C == false)) {
device_status.op_status = OP_STS_STANDBY; // 충전 차단 시 즉시 Standby로 복귀
}
break;
```
---
## 3. 컴파일 및 빌드 검증
- **컴파일러**: ARM RealView Compilation Tools (armcc.exe v5.06 update 7 build 960)
- **빌드 옵션**: `--cpu=Cortex-M4.fp.sp --apcs=interwork -O2`
- **빌드 결과**: **28개 소스 파일 전체 정상 컴파일 (0 Error, 0 Fatal Error)**
- **빌드 후 자동화 스크립트 반영**:
- [`CopyHex_Flash.bat`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/MDK/CopyHex_Flash.bat): `copy output\release\mBMS2_KT_APP.hex output\mBMS2_KT_APP_V4001.hex`
- [`Hex2Bin_Flash.bat`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/MDK/Hex2Bin_Flash.bat): `hex2bin.exe -b .\output\mBMS2_KT_APP_V4001.hex`
- **최종 빌드 산출물**:
| 항목 | 파일명 | 위치 | 크기 | 비고 |
| :--- | :--- | :--- | :---: | :--- |
| **HEX 파일** | [`mBMS2_KT_APP_V4001.hex`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/MDK/output/mBMS2_KT_APP_V4001.hex) | `APP_R3221/App/MDK/output/` | 178,926 bytes | V2 릴리즈 Hex |
| **BIN 파일** | [`mBMS2_KT_APP_V4001.bin`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3221/App/MDK/output/mBMS2_KT_APP_V4001.bin) | `APP_R3221/App/MDK/output/` | 63,600 bytes | Flash Base: `0x00020000`, CRC32: `0xE605B6E3` |

View File

@@ -0,0 +1,354 @@
# KT mBMS V3 펌웨어 변경 이력서 (APP_R3323)
- **작성일자**: 2026년 10월 22일
- **대상 펌웨어**: [`APP_R3323`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323) (프로젝트: `App/MDK/mBMS2.uvprojx`)
- **적용 하드웨어**: 3rd KT mBMS-16S (`sch/3rd KT mBMS-16S.pdf`)
- **주요 스펙**:
- **MCU**: HDSC HC32F460PETB (ARM Cortex-M4, 100-pin LQFP)
- **AFE IC**: Analog Devices ADBMS1818 (SPI 통신, 18S 모니터링 IC 중 15S/16S 운용)
- **스위치 방식**: 보드 내 N-MOSFET 드라이버를 통한 **외부 릴레이 제어** (전 모델 동일 구조)
- **저장 장치**: MCU 내부 플래시 직접 저장 방식 (`mBMS_SOC_BASE_ADDR`)
- **셀 구성**: 15S/16S 호환 (`DEVICE_MAX_CELL = 15`)
- **컴파일 검증**: Keil ARMCC (v5.06 update 7) **27개 전체 C 파일 빌드 성공 (0 Error)**
---
## 1. 주요 변경 요약
1. **KT 요구사항 1: 셀 전압 편차 200mV & 24시간 충전 차단 로직 구현**:
- 셀 편차 기본 임계치를 기존 500mV에서 **200mV**로 변경하고 파라미터 로드 시 200mV 초과 설정을 자동 클램핑.
- 셀 편차가 200mV 이상 지속 시 24시간(`86,400,000ms`) 타이머 연동 및 타이머 만료 시 충전 차단(`CHG_C = false`).
- 충전 리플 노이즈로 인한 억울한 타이머 리셋을 방지하는 **히스테리시스 구간(`150mV~200mV` 보류 유지)** 적용.
2. **KT 요구사항 2: 반복 과충전 전류(OCC) 3회 감지 시 영구 래치 차단 (Lockout)**:
- 과충전 전류 3회 반복 발생 시 `OCC_3Times_Fail = true`로 영구 래치 록아웃.
- 전류가 복구 조건으로 내려가더라도 자동 복구 금지, 충전 FET 영구 차단 유지.
3. **KT 요구사항 3: 충전 차단 Flag 적용 및 Standby 정상 복귀**:
- `CHG_C == false` 발생 시 운용 상태(`op_status`)가 `OP_STS_CHARGING`에 머무르지 않고 `OP_STS_STANDBY`로 정상 복귀.
4. **크리티컬 결함 수정: 셀 전압 0V / 단선 감지 및 비정상 측정 차단**:
- 어떤 셀이라도 1000mV 이하 또는 4500mV 이상 감지 시 `afe_fail` 처리 및 충전 차단.
- 기존 코드에서 충전 중(`op_status == 1`) CUV 플래그를 강제로 0으로 지워버리던 심각한 잠재 위험 버그 제거.
5. **크리티컬 결함 수정: 온도 센서 단선/불량 감지 및 충전 차단 연동**:
- `IsTempSensorFailed()` 구현 (ADC 센서 단선/고장 감지).
- 온도 센서 고장 감지 시 `temp_sensor` 에러 세팅 및 FET 충전 전면 차단.
6. **크리티컬 결함 수정: AFE Fail 플래그 토글/플리커링 버그 방어**:
- AFE 통신 상태와 단선 감지 플래그를 OR 결합(`battery_status.bit.AFE | error.bit.afe_fail`)하여 알람 상태 안정적 유지.
7. **V3 전용 핵심 최적화: MCU Flash Wear-out(플래시 수명 고갈) 방지**:
- V3는 FatFS 외부 SPI Flash를 쓰는 V1/V2와 달리 MCU 내부 Flash 직접 저장 방식(`FLASH_If_SingleSectorErase`)을 사용.
- 기존 1분 주기 무조건 섹터 삭제/저장 로직을 **10분 주기(`SOC_SAVE_TIME`) + 1% 이상 용량 변화 또는 사이클 수명 변화 시에만 지우고 저장**하도록 변경.
- 플래시 지우기 횟수를 기존 대비 **1/10 ~ 1/60 수준으로 획기적으로 절감하여 칩 고장 원천 방지**.
8. **공통 펌웨어 버전(V4.0.0.1) 및 V3 비공개 식별자(`0x57`, `0x33`, `0x01`) 적용**:
- 공식 펌웨어 버전을 **`V4.0.0.1`** (`0x4001`)로 통일하여 KT 관제 시스템 및 GUI에 동일 버전 표기.
- CAN Status #2의 미사용 Reserved 필드(Byte 3~5)에 `0x57`('W'), `0x33`(V3), `0x01`(R401)을 패킹하여 개발자 전용 하드웨어 식별 기능 확보.
- 시리얼 콘솔(`APPVerDisplay`) 출력에 `HW3:HC32+ADBMS1818, R401-JKW` 상세 표기.
---
## 2. 파일별 상세 수정 내역
### 1) [`App/Source/device_param.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/device_param.c)
- **변경 내용**: 파라미터 로드 시 KT 요구사항(셀 편차 200mV 이하)으로 자동 클램핑 보정.
```c
// [수정 후 추가 코드]
/* KT 요구사항: 셀 편차 200mV 초과 설정 방지 */
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold > 200)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200;
}
if (device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 150;
}
```
---
### 2) [`App/Source/dflash_sf1.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/dflash_sf1.c)
- **변경 내용**: 기본 파라미터 셋업 시 셀 편차 경보/보호 기본값을 200mV로 지정.
```c
// [수정 전]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 500;
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 300;
// [수정 후]
device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold = 200; // mV (KT spec: 200mV)
device_param.safety.cv_diff.Cell_Voltage_Diff_Warning = 200; // mV (KT spec: 200mV)
device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery = 100; // mV
device_param.safety.cv_diff.Cell_Voltage_Diff_Time = 60; // Sec
```
---
### 3) [`App/Source/temp.h`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/temp.h) & [`App/Source/temp.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/temp.c)
- **변경 내용**: 온도 센서 단선/불량 여부를 안전 시스템에 제공하기 위한 `IsTempSensorFailed()` 함수 구현.
```c
// [App/Source/temp.h]
uint8_t IsTempSensorFailed(void);
// [App/Source/temp.c]
uint8_t IsTempSensorFailed(void)
{
for (int i = 0; i < TEMP_CH_MAX; i++) {
if (TD[i].Enabled && TD[i].Failed) {
return true;
}
}
return false;
}
```
---
### 4) [`App/Source/alarm.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/alarm.c)
- **변경 내용**: KT 핵심 5대 요구사항 및 안전 로직 전면 개선 반영.
#### ① 24시간 타이머 정의 및 히스테리시스 구간(`150mV~200mV` 보류)
```c
#define CV_DIFF_PROTECT_HOLD_TIME (24UL * 60UL * 60UL * 1000UL) // 24 hours in ms (86,400,000 ms)
```
```c
void ProcessSafety_CellVoltageDiff(void)
{
...
/* Cell Voltage Diff protection */
if (device_status.protect.bit.cell_voltage_diff)
{
// release check
if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
device_status.protect.bit.cell_voltage_diff = false;
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
else
{
// alarm check (200mV 이상 시 24시간 타이머 감점 시작)
if (device_value.Cell.avg.diff >= device_param.safety.cv_diff.Cell_Voltage_Diff_Threshold)
{
if (CV_Diff_ProtectTime == 0)
{
device_status.protect.bit.cell_voltage_diff = true;
}
}
// 히스테리시스 방어: 150mV 미만으로 완전히 회복되었을 때만 타이머 리셋
else if (device_value.Cell.avg.diff < device_param.safety.cv_diff.Cell_Voltage_Diff_Recovery)
{
CV_Diff_ProtectTime = CV_DIFF_PROTECT_HOLD_TIME;
}
}
}
```
#### ② 반복 과충전 전류(OCC) 3회 감지 시 영구 래치 차단 (Lockout)
```c
void ProcessSafety_OCC(void)
{
/* Over charge current protection */
if (device_status.protect.bit.chg_over_current)
{
// release check: DO NOT auto-recover if repeated OCC fail occurred (Latched)
if (OCC_3Times_Fail)
{
device_status.protect.bit.chg_over_current = true;
}
else
{
if ((device_value.Current / 1) < (device_param.safety.current.OC_Chg_Recovery * 10))
{
if (OCCTime == 0)
device_status.protect.bit.chg_over_current = false;
}
else
{
OCCTime = device_param.safety.current.OC_Chg_Rec_Time * 1000;
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
}
}
else
{
// alarm check
if ((device_value.Current / 1) > (device_param.safety.current.OC_Chg_Threshold * 10))
{
OCC_Occur_Current = device_value.Current;
device_status.protect.bit.chg_over_current = true;
OCCTime = device_param.safety.current.OC_Chg_Rec_Time * 1000;
if (OCC_Fail_Count < 3)
{
OCC_Fail_Count++;
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
else
{
OCC_3Times_Fail = true; // 3회 누적 시 영구 래치
OCC_CRITICAL_Tick = OCC_CRITICAL_CLEAR_TIME;
}
}
}
if (OCC_3Times_Fail == false)
{
if (OCC_Fail_Count > 0)
{
if (OCC_CRITICAL_Tick == 0)
{
OCC_Fail_Count = 0;
}
}
}
else
{
device_status.protect.bit.chg_over_current = true;
}
device_status.battery_status.bit.TCA = OCC_3Times_Fail;
device_status.protect.bit.critial_over_current = OCC_3Times_Fail;
...
}
```
#### ③ 충전 중 CUV 플래그 강제 삭제 버그 제거 및 0V/단선 감지 차단
```c
// [수정 전 버그 코드: 충전 중이면 CUV 강제 클리어]
// if (device_status.op_status == 1) {
// device_status.protect.bit.cell_under_voltage = 0;
// }
// [수정 후 안전 코드: 1000mV 이하 / 4500mV 이상 비정상 측정 감지]
uint8_t cell_fault = false;
for (int i = 0; i < DEVICE_MAX_CELL; i++)
{
if ((device_value.Cell.voltage[i] <= 1000) || (device_value.Cell.voltage[i] >= 4500))
{
cell_fault = true;
break;
}
}
if (cell_fault)
{
device_status.error.bit.afe_fail = true;
device_status.protect.bit.afe_fail = true;
}
else
{
device_status.error.bit.afe_fail = false;
}
```
#### ④ 온도 센서 고장 및 AFE Fail 시 안전 차단 및 AFE Fail OR 결합
```c
void ProcessAlarm(void)
{
if (device_status.battery_status.bit.INIT)
{
// AFE 통신 상태와 단선 감지 플래그를 안전 결합 (플리커링 방어)
device_status.protect.bit.afe_fail = device_status.battery_status.bit.AFE | device_status.error.bit.afe_fail;
...
}
}
void Process_FetControl(void)
{
if (IsTempSensorFailed())
{
device_status.error.bit.temp_sensor = true;
}
else
{
device_status.error.bit.temp_sensor = false;
}
if ((device_status.protect.bit.afe_fail) || (device_status.error.bit.temp_sensor)) {
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.DSG_C = false;
device_status.battery_status.bit.ZVC_C = false;
return;
}
...
// OCC 3회 영구 래치 시 충전 차단 유지
else if (OCC_3Times_Fail || device_status.protect.bit.critial_over_current)
{
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.ZVC_C = false;
}
// 셀 편차 24시간 보호 시 충전 차단
else if (device_status.protect.bit.cell_voltage_diff)
{
device_status.battery_status.bit.CHG_C = false;
device_status.battery_status.bit.ZVC_C = false;
}
}
```
#### ⑤ 충전 차단 Flag(`CHG_C == false`) 발생 시 Standby 복귀
```c
case OP_STS_CHARGING: // Charging
if (device_value.DCurrent > (device_param.chg_ctrl.current_thr.Discharge_Detection_Threshold / 100)) {
device_status.op_status = OP_STS_DISCHARGING;
} else if ((device_value.CCurrent < ((device_param.chg_ctrl.current_thr.Charge_Detection_Threshold - 1000) / 100)) ||
(device_status.battery_status.bit.CHG_C == false)) {
device_status.op_status = OP_STS_STANDBY; // 충전 차단 시 즉시 Standby로 복귀
}
break;
```
---
### 5) [`App/Source/calc_soc.c`](file:///d:/Project/Firmware/KT/mBMS/R4xx/APP_R3323/App/Source/calc_soc.c) (V3 전용 최적화)
- **변경 내용**: MCU 내부 Flash Wear-out(플래시 수명 고갈) 방지 최적화.
- 기존 1분(`60,000ms`)마다 무조건 수행하던 플래시 섹터 삭제/저장을 10분(`600,000ms`) 주기로 완화.
- 10분이 도래하더라도 **1% 이상의 유의미한 SOC 용량 변화가 있거나 라이프사이클 증가 시에만** Flash Erase & Write를 수행하도록 변경.
```c
// [수정 전]
#define SOC_SAVE_TIME (60 * 1000) // 1분
void SaveStoreSOC(void)
{
...
if (Soc_Save_Tick == 0) {
UpdateStoreSOC(); // 무조건 섹터 삭제 및 기록 (수명 고갈 위험)
Soc_Save_Tick = SOC_SAVE_TIME;
}
}
// [수정 후] (수명 극대화 알고리즘)
#define SOC_SAVE_TIME (10UL * 60UL * 1000UL) // 10분 (600,000 ms)
void SaveStoreSOC(void)
{
int rc;
if (LifeCycleSaveSoc == true) {
rc = UpdateStoreSOC();
if (rc == 0) {
LifeCycleSaveSoc = false;
memcpy(&bakStoreSOC, &StoreSOC, sizeof(TStoreSOC));
}
} else {
if (Soc_Save_Tick == 0) {
int32_t cap_delta = (int32_t)StoreSOC.CalcSOC.Capacity - (int32_t)bakStoreSOC.CalcSOC.Capacity;
if (cap_delta < 0) cap_delta = -cap_delta;
// 1% threshold of Full Charge Capacity to prevent unnecessary flash wear
uint32_t one_percent_cap = (uint32_t)device_param.sbs.data.Full_Charge_Capacity * 36;
if (one_percent_cap == 0) one_percent_cap = 3600;
// 용량 변화가 1% 이상이거나 LifeCycle이 변경되었을 때만 Flash Erase/Write 수행
if ((bakStoreSOC.CalcSOC.LifeCycle != StoreSOC.CalcSOC.LifeCycle) ||
((uint32_t)cap_delta >= one_percent_cap)) {
UpdateStoreSOC();
memcpy(&bakStoreSOC, &StoreSOC, sizeof(TStoreSOC));
}
Soc_Save_Tick = SOC_SAVE_TIME;
}
}
}
```
---
## 3. 컴파일 및 빌드 검증
- **컴파일러**: ARM RealView Compilation Tools (armcc.exe v5.06 update 7 build 960)
- **빌드 옵션**: `--cpu=Cortex-M4.fp.sp --apcs=interwork -O2`
- **빌드 결과**: **27개 소스 파일 전체 정상 컴파일 (0 Error, 0 Fatal Error)**