Files
templates/python/set_devices/firmware_flashing.py
T
Andrey f9d369d032 feat(f28335-rs-boot): добавьте постоянный загрузчик по RS
Принимайте INITLOAD, LOAD и TFLASH общим автоматом с повторной
проверкой SCI8/FAB1 до стирания и сохранением сектора A.

Сохраняйте параметры прошивки в паспорте prebuild, выпускайте
независимые образы загрузчика и приложения с manifest и SHA-256.

Проверено: шесть испытаний модели, включая обрывы записи и восстановление.
2026-11-13 15:07:55 +03:00

51 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Настройки формы прошивки из JSON проекта и manifest без зависимости от Qt.""" # Один контракт обслуживает сборщики Keil/CCS и потребителей комплекта.
from copy import deepcopy # Возвращает отдельный профиль, чтобы GUI не изменял сохранённый паспорт.
LOADER_TRANSPORTS = {"manual": "manual", "set-rs485": "rs485", "set-can": "can", "protocan-can": "can", "stm32-uart": "stm32", "stm32-usb": "stm32", "f28335-jtag": "tms", "f28335-eeprom": "tms", "f28335-flash-rs": "tms"} # Коды совпадают с действующими загрузчиками SETGUI; транспорт сам по себе не определяет протокол.
COMMON_MODES = {"set-rs485": 0, "set-can": 1, "stm32-uart": 2, "protocan-can": 6, "stm32-usb": 7} # Индексы существующей общей формы нужны только порту GUI, без кодирования пакетов.
CAN_FIELDS = {"bitrate", "node_id", "product_type", "hardware_min", "hardware_max", "base_address"} # Скорость CAN выражается в бит/с, адрес приложения — в байтах.
RS_FIELDS = {"baudrate", "controller", "timeout_ms"} # Профиль ПМ35 задаёт скорость UART в бодах, адрес контроллера и добавочный тайм-аут ответа в миллисекундах.
PROFILE_FIELDS = {"manual": set(), "set-rs485": {"baudrate", "base_address"}, "set-can": CAN_FIELDS, "protocan-can": (CAN_FIELDS - {"base_address"}) | {"device_type", "session_id", "memory"}, "stm32-uart": {"baudrate", "base_address"}, "stm32-usb": {"base_address"}, "f28335-jtag": set(), "f28335-eeprom": RS_FIELDS, "f28335-flash-rs": RS_FIELDS} # ProtoCAN получает адреса из самого образа; остальные поля соответствуют действующим контролам загрузчиков.
INTEGER_LIMITS = {"bitrate": (1, 1000000), "baudrate": (1, 2147483647), "node_id": (0, 255), "product_type": (0, 65535), "hardware_min": (0, 255), "hardware_max": (0, 255), "base_address": (0, 4294967295), "controller": (0, 255), "timeout_ms": (50, 10000), "device_type": (0, 7), "session_id": (1, 255)} # Пределы взяты из полей SETGUI: Classic CAN до 1 Мбит/с, uint32 адреса и диапазоны существующих виджетов.
LOADER_TRANSPORTS["f28335-sci"] = "tms" # Штатный первичный SCI-загрузчик UlitCo использует собственный маршрут, отдельный от BIOS ПМ35.
COMMON_MODES["f28335-sci"] = 9 # Новый режим добавлен после существующих USB и STM32/J-Link без изменения их индексов.
PROFILE_FIELDS["f28335-sci"] = {"baudrate", "oscillator_hz", "enter_monitor", "timeout_ms"} # Частота кварца нужна Flash API, а переход Monitor передаётся только по явной настройке.
def validate_flashing(value): # Проверяет весь блок до изменения версии, формы или сохранённого manifest.
if not isinstance(value, dict) or set(value) - {"default_loader", "profiles"}: # Опечатки и null не должны молча превращаться в штатную скорость.
raise ValueError("flashing: ожидается объект с default_loader и profiles") # Сообщает имя исправляемого раздела проектного JSON.
profiles = value.get("profiles", {}) # Отсутствующий раздел сохраняет совместимость старых проектов без настроек прошивки.
if not isinstance(profiles, dict): # Список профилей должен сопоставлять стабильный код загрузчика с его параметрами.
raise ValueError("flashing.profiles: ожидается объект") # Не допускает строку или массив вместо именованных профилей.
for loader, profile in profiles.items(): # Проверяет также альтернативные CAN/RS-профили до резервирования номера сборки.
if loader not in PROFILE_FIELDS or not isinstance(profile, dict): # Неизвестный протокол требует поддержки в GUI, а не угадывания по названию.
raise ValueError("flashing.profiles: неизвестный загрузчик или неверный профиль " + str(loader)) # Указывает конкретный неподдержанный ключ.
unknown = set(profile) - PROFILE_FIELDS[loader] # Исключает параметры другого протокола и неработающие настройки интерфейса.
if unknown: # Лишнее поле часто означает опечатку в скорости или неверный выбор загрузчика.
raise ValueError("flashing." + loader + ": неподдерживаемые поля " + ", ".join(sorted(unknown))) # Не создаёт manifest с настройками, которые GUI не способен применить.
for field, number in profile.items(): # Проверяет типы без неявного преобразования строк и логических значений в числа.
if field in INTEGER_LIMITS: # Все числовые параметры имеют явно определённые единицы и пределы.
low, high = INTEGER_LIMITS[field] # Диапазон соответствует конкретному виджету или целочисленному адресу загрузчика.
if loader == "protocan-can" and field == "node_id": high = 15 # Поле Device действующей формы ProtoCAN ограничено четырьмя битами.
if type(number) is not int or not low <= number <= high: # JSON true и дробные скорости запрещены даже при числовой совместимости Python.
raise ValueError(f"flashing.{loader}.{field}: нужно целое число {low}..{high}") # Останавливает prebuild и импорт до применения неверного значения.
elif field == "enter_monitor" and type(number) is not bool: # Переход UlitCo в первичный загрузчик включается явным логическим значением JSON.
raise ValueError("flashing.f28335-sci.enter_monitor: нужно true или false") # Не трактует строку false как разрешение управляющей команды.
elif field == "oscillator_hz" and (type(number) is not int or number not in (20000000, 25000000, 30000000)): # Целочисленные частоты кварца дают 100, 125 и 150 МГц при штатном PLL ядра TI.
raise ValueError("flashing.f28335-sci.oscillator_hz: нужно 20000000, 25000000 или 30000000") # Ошибочная частота не должна приводить к неверным временам Flash API.
elif field == "memory" and number not in ("eeprom", "flash", "stm32"): # Коды соответствуют существующему выбору памяти ProtoCAN.
raise ValueError("flashing.protocan-can.memory: нужно eeprom, flash или stm32") # Не подменяет назначение неподдержанного значения EEPROM по умолчанию.
if profile.get("hardware_min", 0) > profile.get("hardware_max", 255): # Проверяет согласованность диапазона аппаратных ревизий до изменения формы.
raise ValueError("flashing." + loader + ": hardware_min больше hardware_max") # Неверный диапазон совместимости не должен попасть в опубликованный паспорт.
default = value.get("default_loader") # Отсутствующий маршрут оставляет прежнее определение назначения по артефакту.
if "default_loader" in value and (not isinstance(default, str) or default not in profiles): # Явный маршрут обязан иметь собственный проверенный профиль, включая пустой manual/JTAG.
raise ValueError("flashing.default_loader: укажите загрузчик из profiles") # Ссылка на отсутствующий профиль не должна оставлять параметры предыдущего изделия.
return deepcopy(value) # Сохраняет исходные единицы и типы JSON, отделяя данные потребителя от настроек проекта.
def flashing_profile(metadata, loader): # Возвращает параметры конкретного загрузчика одного проверенного выпуска.
return validate_flashing(metadata.get("flashing", {})).get("profiles", {}).get(loader, {}) # Старый manifest без flashing продолжает работать с прежними ручными настройками.