feat: KT mBMS 펌웨어 3종(HW1/2/3) 및 통합 빌드 환경 초기 커밋 (V4.0.0.1)
This commit is contained in:
583
generate_word_doc_en.py
Normal file
583
generate_word_doc_en.py
Normal file
@@ -0,0 +1,583 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
KT mBMS CAN Communication Protocol Specification English Word(.docx) Builder
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from docx import Document
|
||||
from docx.shared import Inches, Pt, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
|
||||
from docx.oxml import OxmlElement, parse_xml
|
||||
from docx.oxml.ns import nsdecls, qn
|
||||
|
||||
def set_cell_background(cell, hex_color):
|
||||
"""Set cell shading color"""
|
||||
shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{hex_color}"/>')
|
||||
cell._tc.get_or_add_tcPr().append(shading_elm)
|
||||
|
||||
def set_cell_margins(cell, top=100, bottom=100, left=150, right=150):
|
||||
"""Set cell margins in dxa (1pt = 20dxa)"""
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
tcMar = parse_xml(
|
||||
f'<w:tcMar {nsdecls("w")}>'
|
||||
f'<w:top w:w="{top}" w:type="dxa"/>'
|
||||
f'<w:bottom w:w="{bottom}" w:type="dxa"/>'
|
||||
f'<w:left w:w="{left}" w:type="dxa"/>'
|
||||
f'<w:right w:w="{right}" w:type="dxa"/>'
|
||||
f'</w:tcMar>'
|
||||
)
|
||||
tcPr.append(tcMar)
|
||||
|
||||
def set_table_borders(table, color="D3D3D3", sz="4", val="single"):
|
||||
"""Set uniform table borders"""
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = parse_xml(
|
||||
f'<w:tblBorders {nsdecls("w")}>'
|
||||
f'<w:top w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'<w:left w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'<w:bottom w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'<w:right w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'<w:insideH w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'<w:insideV w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
|
||||
f'</w:tblBorders>'
|
||||
)
|
||||
tblPr.append(borders)
|
||||
|
||||
def make_callout_box(doc, text, title="CRITICAL REQUIREMENT"):
|
||||
"""Create callout block with thick left border"""
|
||||
tbl = doc.add_table(rows=1, cols=1)
|
||||
tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
tbl.autofit = False
|
||||
|
||||
cell = tbl.cell(0, 0)
|
||||
cell.width = Inches(6.5)
|
||||
set_cell_background(cell, "F0F4F8")
|
||||
set_cell_margins(cell, top=140, bottom=140, left=200, right=200)
|
||||
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
borders = parse_xml(
|
||||
f'<w:tcBorders {nsdecls("w")}>'
|
||||
f'<w:left w:val="single" w:sz="24" w:space="0" w:color="1F4E79"/>'
|
||||
f'<w:top w:val="none"/>'
|
||||
f'<w:right w:val="none"/>'
|
||||
f'<w:bottom w:val="none"/>'
|
||||
f'</w:tcBorders>'
|
||||
)
|
||||
tcPr.append(borders)
|
||||
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
run_t = p.add_run(f"[{title}] ")
|
||||
run_t.bold = True
|
||||
run_t.font.name = "Segoe UI"
|
||||
run_t.font.size = Pt(9.5)
|
||||
run_t.font.color.rgb = RGBColor(31, 78, 121)
|
||||
|
||||
run_b = p.add_run(text)
|
||||
run_b.font.name = "Segoe UI"
|
||||
run_b.font.size = Pt(9.5)
|
||||
run_b.font.color.rgb = RGBColor(50, 50, 50)
|
||||
|
||||
doc.add_paragraph().paragraph_format.space_after = Pt(4)
|
||||
|
||||
def format_cell(cell, text, bold=False, color=RGBColor(50, 50, 50), size=Pt(9), align=WD_ALIGN_PARAGRAPH.LEFT, font_name="Segoe UI"):
|
||||
p = cell.paragraphs[0]
|
||||
p.alignment = align
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.15
|
||||
run = p.add_run(text)
|
||||
run.bold = bold
|
||||
run.font.name = font_name
|
||||
run.font.size = size
|
||||
run.font.color.rgb = color
|
||||
|
||||
def create_styled_table(doc, headers, data, col_widths=None, alignments=None):
|
||||
"""Create styled data table with repeated header"""
|
||||
table = doc.add_table(rows=len(data) + 1, cols=len(headers))
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
set_table_borders(table, color="D9D9D9", sz="4")
|
||||
|
||||
# Header Row
|
||||
hdr_row = table.rows[0]
|
||||
trPr = hdr_row._tr.get_or_add_trPr()
|
||||
trPr.append(parse_xml(f'<w:tblHeader {nsdecls("w")}/>'))
|
||||
|
||||
for i, title in enumerate(headers):
|
||||
cell = hdr_row.cells[i]
|
||||
set_cell_background(cell, "1F4E79")
|
||||
set_cell_margins(cell, top=140, bottom=140, left=160, right=160)
|
||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
|
||||
format_cell(cell, title, bold=True, color=RGBColor(255, 255, 255), size=Pt(9.5), align=WD_ALIGN_PARAGRAPH.CENTER)
|
||||
if col_widths and i < len(col_widths):
|
||||
cell.width = col_widths[i]
|
||||
|
||||
# Data Rows
|
||||
for r_idx, row_data in enumerate(data):
|
||||
row = table.rows[r_idx + 1]
|
||||
bg_color = "F9FAFC" if (r_idx % 2 == 1) else "FFFFFF"
|
||||
for c_idx, cell_value in enumerate(row_data):
|
||||
cell = row.cells[c_idx]
|
||||
set_cell_background(cell, bg_color)
|
||||
set_cell_margins(cell, top=100, bottom=100, left=140, right=140)
|
||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
|
||||
|
||||
align = WD_ALIGN_PARAGRAPH.LEFT
|
||||
if alignments and c_idx < len(alignments):
|
||||
align = alignments[c_idx]
|
||||
|
||||
is_bold = (c_idx == 0 and len(headers) > 3)
|
||||
format_cell(cell, cell_value, bold=is_bold, color=RGBColor(50, 50, 50), size=Pt(9), align=align)
|
||||
if col_widths and c_idx < len(col_widths):
|
||||
cell.width = col_widths[c_idx]
|
||||
|
||||
p_after = doc.add_paragraph()
|
||||
p_after.paragraph_format.space_before = Pt(0)
|
||||
p_after.paragraph_format.space_after = Pt(6)
|
||||
|
||||
def build_protocol_document_en():
|
||||
doc = Document()
|
||||
|
||||
# Page Margins (A4: 210mm x 297mm, margins: 0.8 in)
|
||||
sections = doc.sections
|
||||
for section in sections:
|
||||
section.top_margin = Inches(0.8)
|
||||
section.bottom_margin = Inches(0.8)
|
||||
section.left_margin = Inches(0.8)
|
||||
section.right_margin = Inches(0.8)
|
||||
|
||||
# Header & Footer
|
||||
header = section.header
|
||||
hp = header.paragraphs[0]
|
||||
hp.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
||||
hrun = hp.add_run("KT mBMS CAN Communication Protocol Specification (v1.0)")
|
||||
hrun.font.name = "Segoe UI"
|
||||
hrun.font.size = Pt(8.5)
|
||||
hrun.font.color.rgb = RGBColor(140, 140, 140)
|
||||
|
||||
footer = section.footer
|
||||
fp = footer.paragraphs[0]
|
||||
fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
frun = fp.add_run("AMOTECH Energy Solution Team | Confidential")
|
||||
frun.font.name = "Segoe UI"
|
||||
frun.font.size = Pt(8.5)
|
||||
frun.font.color.rgb = RGBColor(140, 140, 140)
|
||||
|
||||
# Document Title
|
||||
title_p = doc.add_paragraph()
|
||||
title_p.paragraph_format.space_before = Pt(4)
|
||||
title_p.paragraph_format.space_after = Pt(4)
|
||||
title_run = title_p.add_run("KT mBMS CAN Communication Protocol Specification")
|
||||
title_run.bold = True
|
||||
title_run.font.name = "Segoe UI"
|
||||
title_run.font.size = Pt(21)
|
||||
title_run.font.color.rgb = RGBColor(31, 78, 121)
|
||||
|
||||
sub_p = doc.add_paragraph()
|
||||
sub_p.paragraph_format.space_before = Pt(0)
|
||||
sub_p.paragraph_format.space_after = Pt(14)
|
||||
sub_run = sub_p.add_run("Unified Common Specification for 3 Hardware Platforms (V1 / V2 / V3 Firmware & Host GUI)")
|
||||
sub_run.font.name = "Segoe UI"
|
||||
sub_run.font.size = Pt(12)
|
||||
sub_run.font.color.rgb = RGBColor(80, 80, 80)
|
||||
|
||||
# Metadata Table
|
||||
meta_table = doc.add_table(rows=4, cols=2)
|
||||
meta_table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
set_table_borders(meta_table, color="D0D7DE", sz="4")
|
||||
meta_data = [
|
||||
("Document Version / Date", "V1.0.0 / October 23, 2026"),
|
||||
("Target Firmware Platforms", "• V1 (1st HW): mBMS_APP_V2403 (STM32F407VGT6 + TI BQ76PL455, External Relay Control)\n• V2 (2nd HW): APP_R3221 (HDSC HC32F460PETB + TI BQ76940, External Relay Control)\n• V3 (3rd HW): APP_R3323 (HDSC HC32F460PETB + ADI ADBMS1818, External Relay Control)"),
|
||||
("Reference Host Application", "LFP_Manager_GL (V3.3.0.16) - Official KT Diagnostic & Telemetry GUI"),
|
||||
("Primary Communication Specs", "CAN 2.0B Extended Frame (29-bit Identifier, 250 kbps, Base Address: 200)")
|
||||
]
|
||||
for r_i, (k, v) in enumerate(meta_data):
|
||||
row = meta_table.rows[r_i]
|
||||
c0 = row.cells[0]
|
||||
c1 = row.cells[1]
|
||||
c0.width = Inches(2.2)
|
||||
c1.width = Inches(4.3)
|
||||
set_cell_background(c0, "F2F4F8")
|
||||
set_cell_background(c1, "FFFFFF")
|
||||
set_cell_margins(c0, top=80, bottom=80, left=120, right=120)
|
||||
set_cell_margins(c1, top=80, bottom=80, left=120, right=120)
|
||||
format_cell(c0, k, bold=True, color=RGBColor(31, 78, 121), size=Pt(9))
|
||||
format_cell(c1, v, bold=False, color=RGBColor(40, 40, 40), size=Pt(9))
|
||||
|
||||
doc.add_paragraph().paragraph_format.space_after = Pt(10)
|
||||
|
||||
# Section 1
|
||||
h1 = doc.add_heading("1. Overview & Physical Layer", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
p = doc.add_paragraph("This specification governs the Controller Area Network (CAN) communication interface between the three hardware revisions of the KT mBMS (V1, V2, V3) and supervisory host systems (Host GUI, rack controller, EMS). All three hardware platforms share the exact same external architecture, driving external heavy-duty relays (Charge, Discharge, and Pre-charge contactors) via on-board N-MOSFET circuits. External system wiring and power switching topologies are 100% identical across all revisions, differing only in internal MCU and AFE IC selections. All platforms share a 100% unified communication frame structure and register mapping.")
|
||||
p.paragraph_format.space_after = Pt(8)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Parameter (Item)", "Specification", "Technical Remarks"],
|
||||
data=[
|
||||
["Communication Standard", "CAN 2.0B Extended Frame (29-bit Identifier)", "Compliant with SAE J1939 PDU1 format"],
|
||||
["Baud Rate", "250 kbps (Default Standard)", "Software configurable (125k, 250k, 500k, 1 Mbps)"],
|
||||
["Termination Resistor", "120 Ω (Both bus endpoints)", "Essential for differential line reflection damping"],
|
||||
["Data Length Code (DLC)", "8 Bytes (Fixed)", "Unused payload bytes padded with 0xFF or 0x00"],
|
||||
["Base Node Address", "200 (0xC8)", "Assigned as 201 ~ 216 according to Module ID (1~16)"],
|
||||
["Broadcast Address", "255 (0xFF)", "Simultaneous command broadcasting across all modules"],
|
||||
["Host (Master/GUI) Address", "1 or 200", "GUI transmits using SA=1, PS=200+ID"]
|
||||
],
|
||||
col_widths=[Inches(2.2), Inches(2.8), Inches(1.9)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 2
|
||||
h1 = doc.add_heading("2. CAN 29-bit Extended Identifier Architecture", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
p = doc.add_paragraph("The CAN message identifier is based on the SAE J1939 PDU1 (Point-to-Point and Broadcast) specification, structured as follows across 29 bits:")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Priority / Index", "Reserved (R)", "Data Page (DP)", "PDU Format (PF)", "PDU Specific (PS)", "Source Addr (SA)"],
|
||||
data=[
|
||||
["Bit 28 ~ 26 (3b)", "Bit 25 (1b)", "Bit 24 (1b)", "Bit 23 ~ 16 (8b)", "Bit 15 ~ 8 (8b)", "Bit 7 ~ 0 (8b)"],
|
||||
["Service Priority", "Fixed to 0", "Fixed to 0", "Command / Function Code", "Destination Address (DA)", "Source Address (SA)"]
|
||||
],
|
||||
col_widths=[Inches(1.2), Inches(0.9), Inches(1.0), Inches(1.3), Inches(1.2), Inches(1.1)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER] * 6
|
||||
)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Field Name", "Bit Range", "Width", "Functional Description & Rules"],
|
||||
data=[
|
||||
["Index (Priority)", "Bit 28 ~ 26", "3 bits", "Service classification & priority:\n• 0x6 (6): Normal telemetry, control, and parameter configuration\n• 0x1 (1): Manufacturing & serial inventory data (Inventory Data)\n• 0x4 (4): In-field firmware download and updates (F/W Update)"],
|
||||
["R (Reserved)", "Bit 25", "1 bit", "Fixed to 0"],
|
||||
["DP (Data Page)", "Bit 24", "1 bit", "Fixed to 0 (1 for extended page)"],
|
||||
["PF (PDU Format)", "Bit 23 ~ 16", "8 bits", "Command / Function Code:\n• Host Request Code (Request: 1 ~ 25)\n• BMS Response Code (Response: 11, 12, 13, 191~194, 31, 32, etc.)"],
|
||||
["PS (PDU Specific)", "Bit 15 ~ 8", "8 bits", "Destination Address (Target Node):\n• Host → BMS: 200 + Target_ID (Module 1 = 201, Broadcast = 0xFF)\n• BMS → Host: Host Address (200 or 1)"],
|
||||
["SA (Source Addr)", "Bit 7 ~ 0", "8 bits", "Source Address (Origin Node):\n• Host → BMS: 1 (Host Address)\n• BMS → Host: 200 + My_ID (Self node address, e.g., 201)"]
|
||||
],
|
||||
col_widths=[Inches(1.5), Inches(1.1), Inches(0.8), Inches(3.4)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 3
|
||||
h1 = doc.add_heading("3. Normal Monitoring & Control Protocol (Index = 6)", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
p = doc.add_paragraph("When the host queries data with a Request packet, the target BMS immediately responds with one or more sequential Response packets. When configuring parameters, the host transmits desired setpoints; for read-only queries, unused payload bytes are populated with 0xFF.")
|
||||
p.paragraph_format.space_after = Pt(8)
|
||||
|
||||
cmd_data = [
|
||||
["System Overall Status", "1", "11, 12, 13, 14", "Pack voltage, current, SOC, state, warning/protect bits, FW version (4 packets)"],
|
||||
["Individual Cell Voltages", "19", "191 ~ 194", "Cell #1 ~ #16 individual cell voltages (1 mV resolution, 4 packets)"],
|
||||
["Temperature Telemetry", "3", "31, 32", "6 Cell thermistors + 2 Internal/FET ambient sensors (0.1 ℃ resolution)"],
|
||||
["SOC / SOH", "4", "41", "High-precision SOC (0.1%), SOH (%), cumulative cycle count"],
|
||||
["Cell Diff Parameters", "2", "21", "Cell diff warning/trip thresholds (KT Spec: 200 mV) & 24h timer linkage"],
|
||||
["Cell Voltage Parameters", "5", "51", "CUV / COV warning and trip thresholds (mV resolution)"],
|
||||
["Temperature Parameters", "6", "61", "Charge/Discharge low/high temp warning & trip (+80 offset, 1 ℃ resolution)"],
|
||||
["System Voltage Parameters", "7", "71", "SUV / SOV warning and trip thresholds (0.01 V resolution)"],
|
||||
["Battery Hardware Config", "8", "81", "Cell quantity (15/16), temperature sensor quantity, nominal capacity (0.1 Ah)"],
|
||||
["Cell Balancing Parameters", "10", "101", "Balancing start voltage threshold, delta window, interval"],
|
||||
["Cell Balancing Status", "11", "111", "Real-time active balancing status and per-cell bitmask"],
|
||||
["Manual Forced Balancing", "12", "-", "Manual forced ON/OFF control for individual cells"],
|
||||
["Manual SOC Calibration", "13", "-", "Forced SOC value override (0 ~ 100%)"],
|
||||
["Low SOC Parameters", "14", "141", "Low capacity warning and recovery SOC thresholds (%)"],
|
||||
["Option Parameters", "15", "151", "Charge option flags and system configuration"],
|
||||
["Default Restore / Shutdown", "16", "-", "Factory parameter reset or forced shutdown command"],
|
||||
["Current Calibration", "17", "171", "Charge/Discharge current sensor gain calibration and shunt range"],
|
||||
["Pack Voltage Calibration", "18", "181", "Pack voltage measurement gain calibration"],
|
||||
["Heartbeat & Sampling", "20", "201", "Cumulative tick counter and 1-cycle AFE sampling latency (ms)"],
|
||||
["RTC Time Synchronization", "21", "211", "Real-time clock (Year, Month, Day, Hour, Min, Sec) & reboot reason"],
|
||||
["Overcurrent Parameters", "22", "221", "Charge/Discharge overcurrent (OCC/OCD) warning/trip (+30000 offset, 0.1 A)"],
|
||||
["Voltage Recovery Param", "23", "231", "CUV, COV, SUV, SOV recovery voltage thresholds"],
|
||||
["Temp & Current Recovery", "24", "241", "Low/High temp and overcurrent recovery thresholds"],
|
||||
["Remote Software Reset", "25", "-", "Initiates system software reboot"]
|
||||
]
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Functional Category", "Req PF", "Rsp PF", "Payload & Operational Summary"],
|
||||
data=cmd_data,
|
||||
col_widths=[Inches(1.8), Inches(0.8), Inches(1.1), Inches(3.1)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 4
|
||||
h1 = doc.add_heading("4. Detailed Data Payload Specifications", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
# 4.1 System Overall Status
|
||||
h2 = doc.add_heading("4.1 System Overall Status (PF: 1 → 11, 12, 13, 14)", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
|
||||
p = doc.add_paragraph("• Host Request (PF: 1): Data[0~5]=0xFF, Data[6]=DCP Flag (0x01: Forced trip linkage, 0x00: Normal), Data[7]=0xFF (Status query) / 0 or 1 (Auto balancing On/Off)\n• BMS Response: The BMS replies with 4 consecutive packets (PF 11, 12, 13, 14) at 5 ms intervals.")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
doc.add_heading("▶ Status #1 (PF: 11) - Primary Telemetry & Alarms 1", level=3)
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Byte", "Parameter Description", "Type", "Unit / Scale", "Remarks & Detailed Meaning"],
|
||||
data=[
|
||||
["0 ~ 1", "Pack Voltage", "uint16", "0.01 V", "e.g., 5120 = 51.20 V"],
|
||||
["2", "State of Charge (SOC)", "uint8", "1 %", "Remaining battery capacity (0 ~ 100%)"],
|
||||
["3", "Auto Cell Balancing", "uint8", "0 or 1", "0: Inactive, 1: Actively balancing"],
|
||||
["4", "Warning Status Bitfield 1", "uint8", "Bitfield", "[Bit 0] COV Warning [Bit 1] CUV Warning\n[Bit 2] High Temp W [Bit 3] Low Temp W\n[Bit 4] SOV Warning [Bit 5] SUV Warning\n[Bit 6] Cell Voltage Deviation Warning"],
|
||||
["5", "Protection Status Bitfield 1", "uint8", "Bitfield", "[Bit 0] COV Fault [Bit 1] CUV Fault\n[Bit 2] High Temp F [Bit 3] Low Temp F\n[Bit 4] SOV Fault [Bit 5] SUV Fault\n[Bit 6] Cell Voltage Deviation Fault (24h Lockout)\n[Bit 7] AFE Fail / Disconnection Fault"],
|
||||
["6 ~ 7", "Pack Current", "int16", "0.1 A", "Offset: +30000\nActual Current = (Value - 30000) × 0.1 A\n(Positive: Charge, Negative: Discharge)"]
|
||||
],
|
||||
col_widths=[Inches(0.8), Inches(2.0), Inches(0.8), Inches(1.1), Inches(2.1)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
doc.add_heading("▶ Status #2 (PF: 12) - Alarms 2, State & Version", level=3)
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Byte", "Parameter Description", "Type", "Details & Enumeration"],
|
||||
data=[
|
||||
["0", "Warning / Protect Bitfield 2", "uint8", "[Bit 0] Module Temp Diff Fault (protect.bit.temp_diff: 3.0 deg C hold 24h)\n[Bit 1] Critical OC Lockout (protect.bit.critial_over_current: 3-times lockout)\n[Bit 2] Low SOC Warning\n[Bit 4] Charge High Temp Warning [Bit 5] Charge Low Temp Warning\n[Bit 6] Discharge High Temp Warning [Bit 7] Discharge Low Temp Warning"],
|
||||
["1", "Protect Status Bitfield 2", "uint8", "[Bit 0] Charge OC Warning [Bit 1] Charge OC Fault (normal trip, auto-recover)\n[Bit 2] Discharge OC Warning [Bit 3] Discharge OC Fault\n[Bit 4] Charge High Temp Fault [Bit 5] Charge Low Temp Fault\n[Bit 6] Discharge High Temp Fault [Bit 7] Discharge Low Temp Fault"],
|
||||
["2", "Operating Status (op_status)", "uint8", "0: Standby 1: Charging\n2: Discharging 3: Floating\n4: Warming Up"],
|
||||
["3 ~ 5", "Secret Identifier", "-", "Byte 3: Signature 0x57 ('W')\nByte 4: HW Type (0x11:V1, 0x22:V2, 0x33:V3)\nByte 5: Build Rev 0x02 (R402 Build)"],
|
||||
["6 ~ 7", "Firmware Version", "uint16", "4-nibble BCD format (e.g., 0x4001 -> V4.0.0.1)"]
|
||||
],
|
||||
col_widths=[Inches(0.8), Inches(2.2), Inches(0.8), Inches(3.0)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
doc.add_heading("▶ Status #3 (PF: 13) & Status #4 (PF: 14) - Internal Bitmasks", level=3)
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Rsp PF", "Byte", "Parameter Description", "Technical Details"],
|
||||
data=[
|
||||
["PF 13", "0 ~ 1", "battery_status.value", "Internal operational status register (Bit 0: INIT, Bit 1: CHG_C, Bit 2: DSG_C, Bit 3: ZVC_C, Bit 4: CB, Bit 5: ACB, Bit 6: TCA, Bit 7: TDA, etc.)"],
|
||||
["PF 13", "2 ~ 3", "protect.value", "Comprehensive 16-bit protection flag register"],
|
||||
["PF 13", "4 ~ 7", "Reserved", "0x00"],
|
||||
["PF 14", "0 ~ 1", "COV Protect Cell Mask", "Cell overvoltage protection bitmask (Bits 0~14: Cells 1~15)"],
|
||||
["PF 14", "2 ~ 3", "COV Warning Cell Mask", "Cell overvoltage warning bitmask"],
|
||||
["PF 14", "4 ~ 5", "CUV Protect Cell Mask", "Cell undervoltage protection bitmask"],
|
||||
["PF 14", "6 ~ 7", "CUV Warning Cell Mask", "Cell undervoltage warning bitmask"]
|
||||
],
|
||||
col_widths=[Inches(0.9), Inches(0.8), Inches(2.1), Inches(3.0)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# 4.2 Individual Cell Voltages
|
||||
h2 = doc.add_heading("4.2 Individual Cell Voltages (PF: 19 → 191 ~ 194)", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
p = doc.add_paragraph("Upon receiving PF=19, the BMS transmits 4 sequential packets at 5 ms intervals. Voltage resolution is 1 mV.")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Response PF", "Byte 0 ~ 1", "Byte 2 ~ 3", "Byte 4 ~ 5", "Byte 6 ~ 7", "Resolution"],
|
||||
data=[
|
||||
["PF 191", "Cell #1 Voltage", "Cell #2 Voltage", "Cell #3 Voltage", "Cell #4 Voltage", "1 mV"],
|
||||
["PF 192", "Cell #5 Voltage", "Cell #6 Voltage", "Cell #7 Voltage", "Cell #8 Voltage", "1 mV"],
|
||||
["PF 193", "Cell #9 Voltage", "Cell #10 Voltage", "Cell #11 Voltage", "Cell #12 Voltage", "1 mV"],
|
||||
["PF 194", "Cell #13 Voltage", "Cell #14 Voltage", "Cell #15 Voltage", "Cell #16 Voltage (0 for 15S)", "1 mV"]
|
||||
],
|
||||
col_widths=[Inches(1.1), Inches(1.3), Inches(1.3), Inches(1.3), Inches(1.3), Inches(0.5)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER] * 6
|
||||
)
|
||||
|
||||
# 4.3 Temperature Telemetry
|
||||
h2 = doc.add_heading("4.3 Temperature Telemetry (PF: 3 → 31, 32)", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
p = doc.add_paragraph("Temperature values are encoded using an Offset of +800 (0.1 ℃ resolution).\n• Actual Temperature (℃) = (Raw Value - 800) / 10 (e.g., 25.0 ℃ = 1050, -10.0 ℃ = 700)")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Response PF", "Byte 0 ~ 1", "Byte 2 ~ 3", "Byte 4 ~ 5", "Byte 6 ~ 7", "Sensor Placement"],
|
||||
data=[
|
||||
["PF 31", "Temp #1 (Cell 1)", "Temp #2 (Cell 2)", "Temp #3 (Cell 3)", "Temp #4 (Cell 4)", "Battery cell surface thermistors"],
|
||||
["PF 32", "Temp #5 (Cell 5)", "Temp #6 (Cell 6)", "Ext #1 (BMS PCB)", "Ext #2 (FET Ambient)", "Internal environmental thermistors"]
|
||||
],
|
||||
col_widths=[Inches(1.0), Inches(1.3), Inches(1.3), Inches(1.3), Inches(1.3), Inches(1.6)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# 4.4 Cell Voltage Deviation Parameters
|
||||
h2 = doc.add_heading("4.4 Cell Voltage Deviation Parameters (PF: 2 ↔ 21) [KT Core Requirement]", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
p = doc.add_paragraph("• Query: Host sends PF=2 with Data[0~7]=0xFF; BMS responds with PF=21\n• Setting: Host sends PF=2 with new parameters; BMS automatically validates, clamps, and saves to non-volatile Flash.")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Byte", "Parameter Name", "Type", "KT Default Value", "Technical Description"],
|
||||
data=[
|
||||
["0 ~ 1", "Cell Voltage Diff Threshold", "uint16", "200 mV", "Cell deviation Trip threshold (Clamped: ≤ 200 mV per KT requirement)"],
|
||||
["2 ~ 3", "Cell Voltage Diff Warning", "uint16", "200 mV", "Cell deviation Warning threshold"],
|
||||
["4 ~ 5", "Cell Voltage Diff Recovery", "uint16", "150 mV", "Cell deviation Recovery threshold (Hysteresis lower bound)"],
|
||||
["6 ~ 7", "Cell Voltage Diff Time", "uint16", "60 Sec", "Warning filter detection duration"]
|
||||
],
|
||||
col_widths=[Inches(0.8), Inches(2.2), Inches(0.8), Inches(1.3), Inches(2.4)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
make_callout_box(
|
||||
doc,
|
||||
"24-Hour Continuous Deviation Protection Logic: If the maximum cell-to-cell deviation persists at >= 200 mV for 24 continuous hours (86,400,000 ms), protect.bit.cell_voltage_diff latches and the charge switch is disabled (CHG_C = false). To prevent erroneous timer resets caused by charging ripple jitter, deviation between 150 mV and 200 mV holds the timer count; the timer resets only when deviation fully recovers to < 150 mV.",
|
||||
title="KT CORE REQUIREMENT: CELL DIFF 200mV 24-HOUR CUTOFF"
|
||||
)
|
||||
|
||||
# 4.5 Protection & Recovery Parameters
|
||||
h2 = doc.add_heading("4.5 Protection & Recovery Parameter Configuration", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Parameter Category", "Req/Rsp PF", "Byte 0 ~ 1", "Byte 2 ~ 3", "Byte 4 ~ 5", "Byte 6 ~ 7"],
|
||||
data=[
|
||||
["Cell Voltage Protection", "5 / 51", "CUV Warning (mV)", "CUV Trip (mV)", "COV Warning (mV)", "COV Trip (mV)"],
|
||||
["Voltage Recovery", "23 / 231", "CUV Recovery (mV)", "COV Recovery (mV)", "SUV Recovery (0.01V)", "SOV Recovery (0.01V)"],
|
||||
["System Pack Voltage", "7 / 71", "SUV Warning (0.01V)", "SUV Trip (0.01V)", "SOV Warning (0.01V)", "SOV Trip (0.01V)"],
|
||||
["Temperature Protection", "6 / 61", "Charge Low W/T (+80)", "Charge High W/T (+80)", "Discharge Low W/T (+80)", "Discharge High W/T (+80)"],
|
||||
["Temp & Current Recovery", "24 / 241", "Chg Low/High Recov", "Dsg Low/High Recov", "Chg Overcurrent Recov", "Dsg Overcurrent Recov"],
|
||||
["Overcurrent Protection", "22 / 221", "Chg Overcurrent W", "Chg Overcurrent T", "Dsg Overcurrent W", "Dsg Overcurrent T"]
|
||||
],
|
||||
col_widths=[Inches(1.5), Inches(1.0), Inches(1.2), Inches(1.2), Inches(1.3), Inches(1.3)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
make_callout_box(
|
||||
doc,
|
||||
"Repeated OCC 3-Times Lockout Logic: If charge overcurrent occurs 3 times within a 1-minute window, OCC_3Times_Fail = true is latched. Even if charge current subsequently drops below recovery levels, automatic recovery is inhibited and charging remains permanently disabled until manual clearance.",
|
||||
title="KT CORE REQUIREMENT: OCC 3-TIMES REPEAT LOCKOUT"
|
||||
)
|
||||
|
||||
# 4.6 Heartbeat & RTC
|
||||
h2 = doc.add_heading("4.6 Heartbeat Telemetry & RTC Time Synchronization", level=2)
|
||||
h2.paragraph_format.space_before = Pt(10)
|
||||
h2.paragraph_format.space_after = Pt(4)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Function", "PF", "Payload Byte Mapping", "Technical Details"],
|
||||
data=[
|
||||
["Heartbeat & Countdown", "20 → 201", "Byte 0~3: Heartbeat Counter (ms Tick, Big-Endian)\nByte 4~5: Reserved (0x0000)\nByte 6~7: Deviation Cutoff Countdown (GetCvDiff(), sec)", "Remaining seconds until cell / temp deviation cutoff. Tripping latches charge relay off until MCU reset."],
|
||||
["RTC Synchronization", "21 ↔ 211", "Byte 0: Year (Epoch: Year - 1970; e.g., 2026 = 56)\nByte 1: Month (1~12) Byte 2: Day (1~31)\nByte 3: Hour (0~23) Byte 4: Minute (0~59) Byte 5: Second (0~59)\nByte 6~7: [Response] Last Reboot Reason Code", "Synchronizes internal battery clock with host time and diagnoses reset cause (POR, Watchdog, Soft-reset)."]
|
||||
],
|
||||
col_widths=[Inches(1.4), Inches(0.9), Inches(2.9), Inches(2.2)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 5
|
||||
h1 = doc.add_heading("5. Inventory & Serial Number Management Protocol (Index = 1)", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
p = doc.add_paragraph("Dedicated communication channel (Index = 1) for reading and programming module production history, manufacture date, and tracking serial numbers.")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Host Req PF", "BMS Rsp PF", "Target Identification Field", "Data Type", "Length & Payload Representation"],
|
||||
data=[
|
||||
["1", "11", "Manufacture Date", "4 Bytes uint32", "YYYYMMDD integer format"],
|
||||
["2", "21", "BMS Serial Number (High 8 bytes)", "8 Bytes ASCII", "BMS board serial number characters [0 ~ 7]"],
|
||||
["3", "31", "BMS Serial Number (Low 8 bytes)", "8 Bytes ASCII", "BMS board serial number characters [8 ~ 15] (Total 16-char S/N)"],
|
||||
["4", "41", "Module Serial Number #1", "8 Bytes ASCII", "Battery pack module serial number characters [0 ~ 7]"],
|
||||
["5", "51", "Module Serial Number #2", "8 Bytes ASCII", "Battery pack module serial number characters [8 ~ 15]"],
|
||||
["6", "61", "Module Serial Number #3", "8 Bytes ASCII", "Battery pack module serial number characters [16 ~ 23]"],
|
||||
["7", "71", "Module Serial Number #4", "8 Bytes ASCII", "Battery pack module serial number characters [24 ~ 31] (Total 32-char S/N)"]
|
||||
],
|
||||
col_widths=[Inches(0.9), Inches(0.9), Inches(2.2), Inches(1.3), Inches(2.1)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 6
|
||||
h1 = doc.add_heading("6. Remote Firmware Download & Update Protocol (Index = 4)", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
p = doc.add_paragraph("In-field firmware update protocol (Index = 4) utilized by LFP_Manager_GL to flash bootloader-supported firmware images over the CAN network.")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Function Code (FC)", "Command Name", "Functional Operation & State Action"],
|
||||
data=[
|
||||
["0", "DL_START", "Requests entry into firmware update mode (BMS acknowledges state readiness)"],
|
||||
["1", "DL_SECTOR_ERASE", "Instructs MCU to erase application Flash sectors"],
|
||||
["2", "DL_SET_ADDRESS", "Sets starting destination Flash memory base write address"],
|
||||
["3", "DL_READ_DATA", "Reads written Flash content for post-programming validation"],
|
||||
["4", "DL_WRITE_DATA", "Transmits binary chunk payload and programs target MCU Flash"],
|
||||
["5", "DL_WRITE_DATA_CSUM", "Validates block-level transmission checksum"],
|
||||
["6", "DL_IMAGE_CSUM", "Computes and verifies complete firmware binary end-to-end checksum"],
|
||||
["7", "DL_RESTART", "Concludes update sequence, reboots bootloader, and launches new firmware"]
|
||||
],
|
||||
col_widths=[Inches(1.3), Inches(1.9), Inches(4.2)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
# Section 7
|
||||
h1 = doc.add_heading("7. Host Polling Architecture & Sequence Guide (LFP_Manager_GL)", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
p = doc.add_paragraph("LFP_Manager_GL enforces a two-tier polling scheme to balance deterministic real-time telemetry with CAN bus load management:")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Polling Tier", "Cycle Time", "Command PF", "Acquired Telemetry Data"],
|
||||
data=[
|
||||
["Tier 1: Fast Real-Time Status", "1.0 Second", "PF: 1 (Status Req)", "Pack voltage, SOC, pack current, alarm/protect flags, operational state"],
|
||||
["Tier 2: Detailed Cell / Temp", "2.0 ~ 3.0 Seconds", "PF: 19 (Cell V)\nPF: 3 (Temp)\nPF: 4 (SOC/SOH)", "16 Cell voltages, 8 Temperature channels, SOH, cumulative cycle counts"],
|
||||
["Parameter View Initialization", "Single Sync Burst", "PF: 5, 6, 7, 14, 22, 23, 24, 2", "Sequential batch query of all safety/recovery thresholds (50 ms inter-query delay)"]
|
||||
],
|
||||
col_widths=[Inches(1.8), Inches(1.1), Inches(2.0), Inches(2.5)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT]
|
||||
)
|
||||
|
||||
make_callout_box(
|
||||
doc,
|
||||
"Timing Recommendations: When the BMS transmits multi-frame responses, an inter-frame transmission delay of at least 5 ms (CAN_TX_CONT_DELAY = 5 ms) is enforced to prevent host controller FIFO saturation. The recommended host response timeout is 500 ms.",
|
||||
title="COMMUNICATION TIMING RECOMMENDATION"
|
||||
)
|
||||
|
||||
# Section 8
|
||||
h1 = doc.add_heading("8. Multi-Hardware Implementation & Build Verification", level=1)
|
||||
h1.paragraph_format.space_before = Pt(14)
|
||||
h1.paragraph_format.space_after = Pt(6)
|
||||
|
||||
p = doc.add_paragraph("This unified protocol is verified and fully implemented across all three hardware revisions using Keil MDK ARMCC:")
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
|
||||
create_styled_table(
|
||||
doc,
|
||||
headers=["Platform", "Hardware Configuration", "Implemented Source Files", "ARMCC Build Verification"],
|
||||
data=[
|
||||
["V1 Firmware\n(mBMS_APP_V2403)", "STM32F407VGT6\nTI BQ76PL455 (UART)\nExternal Relays (N-MOSFET driven)", "APP/can_comm.c\nAPP/can_data_process.c\nAPP/can_update_process.c", "All 98 C sources built successfully\n(0 Errors, Keil ARMCC v5.06)"],
|
||||
["V2 Firmware\n(APP_R3221)", "HDSC HC32F460PETB\nTI BQ76940 (I2C)\nExternal Relays (N-MOSFET driven)", "App/Source/can_comm.c\nApp/Source/can_data_process.c\nApp/Source/can_inv_data_process.c", "All 28 C sources built successfully\n(0 Errors, Keil ARMCC v5.06)"],
|
||||
["V3 Firmware\n(APP_R3323)", "HDSC HC32F460PETB\nADI ADBMS1818 (SPI)\nExternal Relays (N-MOSFET driven)", "App/Source/can_comm.c\nApp/Source/can_data_process.c\nApp/Source/can_inv_data_process.c", "All 27 C sources built successfully\n(0 Errors, Keil ARMCC v5.06)"]
|
||||
],
|
||||
col_widths=[Inches(1.5), Inches(1.8), Inches(2.4), Inches(1.7)],
|
||||
alignments=[WD_ALIGN_PARAGRAPH.CENTER, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.LEFT, WD_ALIGN_PARAGRAPH.CENTER]
|
||||
)
|
||||
|
||||
output_path = r"d:\Project\Firmware\KT\mBMS\R4xx\Docs\Protocol\KT_mBMS_CAN_Communication_Protocol_Specification.docx"
|
||||
try:
|
||||
doc.save(output_path)
|
||||
print(f"Successfully generated: {output_path}")
|
||||
except PermissionError:
|
||||
output_path_alt = r"d:\Project\Firmware\KT\mBMS\R4xx\Docs\Protocol\KT_mBMS_CAN_Communication_Protocol_Specification_Rev1.docx"
|
||||
doc.save(output_path_alt)
|
||||
print(f"File locked by Word. Successfully generated alternative: {output_path_alt}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
build_protocol_document_en()
|
||||
Reference in New Issue
Block a user