Set CAN1 to 500K and CAN2 to 250K with matching DBCs and PDF

This commit is contained in:
shenlefengnai-rgb
2026-10-03 17:44:55 +08:00
parent ff6ef4c5e7
commit 9120bd0ec1
17 changed files with 316 additions and 33 deletions
+54
View File
@@ -10,11 +10,48 @@ from pathlib import Path
import re
import cantools
from export_dbc_docs import can2_dbc_text
ROOT = Path(__file__).resolve().parents[1]
def verify_bit_timing():
ioc = dict(line.split('=', 1) for line in
(ROOT / 'firmware/AMI_Controller.ioc').read_text().splitlines() if '=' in line)
source = (ROOT / 'firmware/Core/Src/fdcan.c').read_text()
clock_source = (ROOT / 'firmware/Core/Src/main.c').read_text()
for setting in ('RCC_HSI_DIV1', 'RCC_PLLM_DIV1', 'RCC_PLLR_DIV4', 'RCC_HCLK_DIV1'):
assert setting in clock_source
assert re.search(r'PLL\.PLLN\s*=\s*16;', clock_source)
assert 'RCC_FDCANCLKSOURCE_PCLK1' in source
clock = int(ioc['RCC.FDCANFreq_Value'])
assert clock == 16000000 * 16 // 4 == int(ioc['RCC.APBFreq_Value'])
result = {}
for port, expected_rate, expected_prescaler in ((1, 500000, 8), (2, 250000, 16)):
def value(field):
found = re.search(rf'hfdcan{port}\.Init\.{field}\s*=\s*(\w+);', source)
assert found, field
return found.group(1)
assert value('ClockDivider') == 'FDCAN_CLOCK_DIV1'
assert value('FrameFormat') == 'FDCAN_FRAME_CLASSIC'
prescaler, seg1, seg2, sjw = (int(value(field)) for field in
('NominalPrescaler', 'NominalTimeSeg1',
'NominalTimeSeg2', 'NominalSyncJumpWidth'))
assert (prescaler, seg1, seg2, sjw) == (expected_prescaler, 13, 2, 1)
rate = clock // prescaler // (1 + seg1 + seg2)
near((1 + seg1) / (1 + seg1 + seg2), 0.875)
assert rate == expected_rate == int(ioc[f'FDCAN{port}.CalculateBaudRateNominal'])
for field in ('NominalPrescaler', 'NominalTimeSeg1', 'NominalTimeSeg2',
'DataPrescaler', 'DataTimeSeg1', 'DataTimeSeg2'):
assert int(value(field)) == int(ioc[f'FDCAN{port}.{field}'])
near(float(ioc[f'FDCAN{port}.CalculateTimeBitNominal']), 1e9 / rate)
near(float(ioc[f'FDCAN{port}.CalculateTimeQuantumNominal']), 1e9 * prescaler / clock)
result[f'CAN{port}'] = rate
return result
def near(actual, expected):
assert math.isclose(actual, expected, rel_tol=0, abs_tol=1e-8), (actual, expected)
@@ -24,6 +61,12 @@ def main():
parser.add_argument('--sdc-dbc', type=Path)
args = parser.parse_args()
db = cantools.database.load_file(ROOT / 'AMI.dbc', strict=True)
db2 = cantools.database.load_file(ROOT / 'AMI_CAN2_250k.dbc', strict=True)
assert (ROOT / 'AMI_CAN2_250k.dbc').read_text(encoding='ascii') == can2_dbc_text()
assert db2.dbc.attributes['Baudrate'].value == 250000
assert db.dbc.attributes['AMIPort'].value == 'CAN1'
assert db2.dbc.attributes['AMIPort'].value == 'CAN2'
bitrates = verify_bit_timing()
header = (ROOT / 'firmware/Core/Inc/ami_protocol.h').read_text(encoding='utf-8')
identifiers = {int(value, 16) for value in re.findall(
r'^#define\s+AMI_CAN_ID_\w+\s+(0x[\dA-Fa-f]+)', header, re.MULTILINE)}
@@ -58,6 +101,7 @@ def main():
assert task == {'Protocol_Version':1,'Task_ID':2,'Request_Sequence':0,
'Request_Source':1,'MCU_Uptime':1000}
request = db.get_message_by_frame_id(0x300)
request2 = db2.get_message_by_frame_id(0x300)
assert request.encode(task) == bytes.fromhex('01 02 00 01 E8 03 00 00')
for task_id in range(1,7):
for seq in (0,255):
@@ -65,6 +109,8 @@ def main():
payload=request.encode(data)
assert payload == bytes([1,task_id,seq,1,255,255,255,255])
assert request.decode(payload,decode_choices=False) == data
assert request2.encode(data) == payload
assert request2.decode(payload,decode_choices=False) == data
assert set(request.get_signal_by_name('Task_ID').choices) == set(range(1,7))
for raw, volts in [('08 20',8.2),('0C 30',12.3),('88 31',12.68),('A4 38',14.5),('FF FF',65.535)]:
@@ -137,8 +183,16 @@ def main():
assert all(getattr(a,key)==getattr(b,key) for key in fields),(ident,a,b)
source_check=True
for check in checks:
ident = int(check['id'], 16)
payload = bytes.fromhex(check['data'])
for decode_choices in (False, True):
assert db.decode_message(ident, payload, decode_choices=decode_choices) == \
db2.decode_message(ident, payload, decode_choices=decode_choices)
print(json.dumps({'result':'passed','messages':len(db.messages),'signals':33,
'known_payload_checks':len(checks),'all_task_ids_and_counter_edges':True,
'ports_bitrate':bitrates,'both_dbc_layouts_and_payloads_match':True,
'firmware_and_ioc_bit_timing_match':True,
'firmware_ids_match':True,'sdc_source_layout_match':source_check,
'cantools_version':cantools.__version__},indent=2))