Files
templates/c/protocan-boot/projects/ccs/f28335/main.c
T
Andrey 41e24629ce Расширить DEIF, загрузку CAN и общую обработку сигналов
Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt.
Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor.
Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов.
Обновить справочники устройств, проекты CCS и регрессионные проверки.
2026-10-24 15:40:51 +03:00

128 lines
20 KiB
C
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.
#include "DSP2833x_Device.h" /* Использует действующую частоту и штатную карту регистров той же платы 167. */
#include "DSP2833x_Examples.h" /* Предоставляет InitSysCtrl, InitPieCtrl, InitFlash и управление Timer1. */
#include "../../../ports/tms320f28335/f28335_can_boot.h" /* Подключает адрес, сервисы и очередь существующего загрузчика. */
#include "../../../ports/tms320f28335/f28335_boot_platform.h" /* Использует общий handoff, SPI и энергонезависимый A/B-журнал. */
#include <string.h> /* Копирует ramfuncs до настройки времен доступа Flash. */
extern Uint16 RamfuncsLoadStart, RamfuncsLoadEnd, RamfuncsRunStart; /* Адреса LOAD/RUN задаёт отдельный linker загрузчика в словах C28x. */
static f28335_boot_store_t image_store; /* Журнал загрузки остаётся в RAM E000..FFFF до перехода в приложение. */
interrupt void boot_tick(void) /* Timer1 продолжает миллисекундные тайм-ауты общего CAN/SPI порта. */
{ /* Этот ISR принадлежит постоянному загрузчику, а не кандидату приложения. */
CpuTimer1.InterruptCount++; /* Один тик равен одной миллисекунде согласно ConfigCpuTimer ниже. */
ServiceDog(); /* Длительные CRC/SPI операции загрузчика не должны сбрасывать аппарат во время обновления. */
} /* Возвращает управление основному циклу загрузчика. */
interrupt void boot_can_rx(void) /* CAN IRQ выполняет только короткий перенос принятых кадров в общую очередь. */
{ /* Запись EEPROM, CRC и публикация pending остаются в главном цикле. */
F28335CanBoot_RxISR(); /* Читает только выделенные mailbox 3 и 6 существующего адаптера. */
PieCtrlRegs.PIEACK.all = 0x0100U; /* Бит 8 подтверждает группу PIE9, в которой находится ECAN0INTA. */
} /* Позволяет принять следующее окно CAN, пока main занят SPI. */
static void boot_can_init(void) /* Настраивает тот же физический CAN-A и 250 кбит/с, что приложение 167. */
{ /* Загрузчик не использует прикладные mailbox 0..2 и не меняет адрес общего порта. */
Uint32 shadow; /* Защищённые eCAN-регистры изменяются только полными 32-битными обращениями. */
EALLOW; /* Разрешает настройку мультиплексора, eCAN и таблицы векторов. */
GpioCtrlRegs.GPAMUX2.bit.GPIO30 = 1; /* GPIO30 служит CANRXA, как в штатном InitCan проекта. */
GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 1; /* GPIO31 служит CANTXA того же трансивера. */
ECanaRegs.CANTIOC.all = 8UL; /* Бит TXFUNC включает функцию CAN у выхода контроллера. */
ECanaRegs.CANRIOC.all = 8UL; /* Бит RXFUNC включает аппаратный вход CAN. */
ECanaRegs.CANME.all = 0UL; /* Изменяет mailbox и фильтры только при выключенных ящиках. */
ECanaRegs.CANMIM.all = 0UL; /* Ни один старый mailbox не должен создавать IRQ во время настройки. */
ECanaRegs.CANMIL.all = 0UL; /* Направляет служебные mailbox на ECAN0INTA группы PIE9. */
ECanaRegs.CANMC.all = 0x00003080UL; /* SCB=1 выбирает eCAN, ABO=1 автоматическое восстановление, CCR=1 режим настройки битового времени; SRES остаётся нулём. */
while (!ECanaRegs.CANES.bit.CCE) {} /* Не пишет CANBTC до аппаратного подтверждения режима настройки. */
shadow = ((Uint32)(CLKMULT * 3L - 1L) << 16) | 0x0000017AUL; /* BRP=CLKMULT*3-1, SJW=1, TSEG1=15, TSEG2=2 как в приложении: 250000 бит/с при текущем SYSCLKOUT. */
ECanaRegs.CANBTC.all = shadow; /* Передаёт параметры одним 32-битным обращением к eCAN. */
ECanaRegs.CANMC.all &= ~0x00001000UL; /* Снимает CCR после настройки тактов CAN. */
while (ECanaRegs.CANES.bit.CCE) {} /* Ждёт возврата контроллера к обычной работе перед включением фильтров. */
ECanaRegs.CANTA.all = 0xFFFFFFFFUL; /* Удаляет старые TX ACK, которые не относятся к новой сессии. */
ECanaRegs.CANRMP.all = 0xFFFFFFFFUL; /* Удаляет старые RX-флаги до включения IRQ. */
ECanaRegs.CANRML.all = 0xFFFFFFFFUL; /* Начинает контроль потери mailbox с чистого состояния. */
ECanaRegs.CANGIF0.all = 0xFFFFFFFFUL; /* Снимает накопленные флаги линии CAN0. */
ECanaRegs.CANGIM.all = 1UL; /* I0EN разрешает mailbox IRQ по линии 0; ошибки не требуют прикладного ISR. */
PieVectTable.ECAN0INTA = &boot_can_rx; /* Использует постоянный обработчик загрузчика вместо кода RAM-приложения. */
PieCtrlRegs.PIEIER9.bit.INTx5 = 1; /* Разрешает ECAN0INTA в группе PIE9. */
EDIS; /* Возвращает защиту аппаратной конфигурации. */
IER |= M_INT9; /* Разрешает группе CAN доставлять IRQ ядру после общего EINT. */
} /* Общий порт затем настроит адресные mailbox 3/4/6. */
static unsigned int boot_periph_id(void) /* Повторяет аппаратное чтение Mode из peripher.c, чтобы после reset адрес загрузчика совпадал с приложением. */
{ /* Четыре активных низким уровнем перемычки существующей ПМ35 задают номера 1–16. */
unsigned int periph_id; /* Номер является ID PERIPH из Excel, а не уменьшенным на единицу индексом старого CAN. */
EALLOW; /* Разрешает настройку только четырёх входов адресных перемычек. */
GpioCtrlRegs.GPAMUX2.bit.GPIO20=0; /* GPIO20 работает входом адреса с весом 2, как в get_Mode приложения. */
GpioCtrlRegs.GPAMUX2.bit.GPIO21=0; /* GPIO21 работает входом адреса с весом 1, а не альтернативной периферией. */
GpioCtrlRegs.GPAMUX2.bit.GPIO22=0; /* GPIO22 работает входом адреса с весом 4. */
GpioCtrlRegs.GPBMUX2.bit.GPIO51=0; /* GPIO51 работает входом адреса с весом 8. */
GpioCtrlRegs.GPADIR.bit.GPIO20=0; /* Запрещает выходной драйвер, чтобы загрузчик не воздействовал на перемычку. */
GpioCtrlRegs.GPADIR.bit.GPIO21=0; /* Оставляет младшую адресную перемычку входом. */
GpioCtrlRegs.GPADIR.bit.GPIO22=0; /* Оставляет третий разряд аппаратного номера входом. */
GpioCtrlRegs.GPBDIR.bit.GPIO51=0; /* Оставляет старшую перемычку входом порта B. */
EDIS; /* Восстанавливает защиту конфигурации GPIO до чтения физического номера. */
periph_id=1U+!GpioDataRegs.GPADAT.bit.GPIO21; /* Штатный Mode начинается с 1; замкнутая GPIO21 добавляет единицу. */
periph_id+=2U*!GpioDataRegs.GPADAT.bit.GPIO20; /* Замкнутая GPIO20 добавляет два к номеру платы. */
periph_id+=4U*!GpioDataRegs.GPADAT.bit.GPIO22; /* Замкнутая GPIO22 добавляет четыре к номеру платы. */
periph_id+=8U*!GpioDataRegs.GPBDAT.bit.GPIO51; /* Замкнутая GPIO51 добавляет восемь; аппаратный диапазон заканчивается на 16. */
return periph_id; /* Общий CAN-порт преобразует номер 16 в Type 2 / Device 0 согласно Excel. */
} /* Приложение и recovery используют один и тот же физический адрес без общего значения 3/7. */
static bool ram_word(void *user, uint32_t address, uint16_t value) /* Аппаратная запись одного слова уже проверенной таблицы SCI8. */
{ /* Повторяет нижнюю защиту RAM даже при ошибке вызывающего слоя. */
(void)user; /* Контекст не нужен для единственной внутренней RAM данного F28335. */
if (address < 0x8000UL || address >= 0xE000UL) return false; /* Не позволяет таблице затронуть периферию, handoff, стек или RAM загрузчика. */
*(volatile Uint16 *)address = value; /* Адрес измеряется в 16-битных словах C28x, значение уже декодировано из SCI8. */
return true; /* Сообщает полную запись слова, а не частичное выполнение секции. */
} /* После загрузки всех слов общий store вернёт проверенную точку входа. */
static uint16_t flash_word(void *user, uint32_t address) /* Читает только фиксированный заголовок и проверенный диапазон приложения ниже сектора A. */
{ /* Загрузчик остаётся во Flash A, поэтому чтение H–B не меняет выполняемый код. */
(void)user; /* Единственная внутренняя Flash не требует отдельного контекста источника. */
return *(volatile Uint16 *)address; /* C28x адресует Flash 16-битными словами; разбиение на октеты выполняет общий валидатор CRC. */
} /* Никакая запись или стирание Flash при выборе источника запуска не выполняется. */
static void start_application(uint32_t entry, unsigned int source) /* Передаёт управление Flash либо EEPROM только после проверки соответствующего образа. */
{ /* Останавливает собственные IRQ до C startup приложения, который заново настроит периферию и рабочую RAM. */
DINT; IER = 0U; IFR = 0U; /* Старые прерывания не должны войти в очередь или стек завершённого загрузчика. */
CpuTimer1Regs.TCR.bit.TSS = 1; /* Прекращает миллисекундные IRQ до запуска прикладного InitSysCtrl. */
ECanaRegs.CANMIM.all = 0UL; /* Не доставляет CAN IRQ во время смены векторов приложения. */
F28335Boot_SetRunning(source); /* Сохраняет достоверный источник: 0/1 для EEPROM A/B либо FE для проверенной Flash. */
((void (*)(void))entry)(); /* Вызывает watchdog-safe startup Flash или проверенную точку C runtime RAM-образа. */
for (;;) {} /* Ошибочный возврат приложения не использует перезаписанное состояние загрузчика. */
} /* Возврат к boot происходит только аппаратным reset с согласованным handoff. */
void F28335Boot_Quiesce(void) /* Перед повторным входом загрузчика его собственные IRQ будут отключены общим Reset. */
{ /* Силовые выходы загрузчик не включает; прикладная остановка выполняется до входа в него. */
CpuTimer2Regs.TCR.bit.TSS = 1; /* Удерживает старый SPI Timer2 остановленным перед watchdog reset. */
} /* Общий аппаратный слой сохранит session и включит watchdog. */
void main(void) /* Постоянный загрузчик запускается аппаратным Flash boot по адресу 33FFF6. */
{ /* Пустая EEPROM и неподтверждённый кандидат оставляют доступным CAN recovery. */
f28335_boot_io_t io; /* Доступ к M95M02 предоставляет существующий переносимый SPI-порт. */
unsigned int slot, session, periph_id; /* Слот, сессия handoff и физический номер PERIPH сохраняются отдельно от адресов памяти. */
uint32_t entry, started; /* Адрес запуска измеряется словами CPU; started хранит миллисекунды стартового окна CAN. */
InitSysCtrl(); /* Повторяет PLL и SYSCLKOUT проекта 167, отключая watchdog на раннем старте. */
DINT; /* Не использует IRQ до настройки таблицы векторов и Timer1. */
IER = 0U; IFR = 0U; /* Исключает доставку старых прерываний после watchdog reset. */
memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, &RamfuncsLoadEnd - &RamfuncsLoadStart); /* Переносит InitFlash в RAM до изменения времен доступа Flash. */
InitFlash(); /* Настраивает доступ к постоянному коду по штатной процедуре TI. */
InitPieCtrl(); /* Очищает разрешения и флаги всех групп PIE перед установкой двух собственных векторов. */
InitCpuTimers(); /* Останавливает Timer0/1/2 и сбрасывает их управляющие структуры. */
ConfigCpuTimer(&CpuTimer1, SYSCLKOUT / 1000000L, 1000UL); /* Период 1000 мкс даёт единый миллисекундный tick для протокола и SPI WIP. */
EALLOW; PieVectTable.XINT13 = &boot_tick; EDIS; /* Привязывает прямое прерывание Timer1 к постоянному ISR загрузчика. */
CpuTimer1Regs.TCR.all = 0x4020U; /* Разрешает прерывание Timer1 и запускает счётчик, как приложение 167. */
IER |= M_INT13; /* Разрешает миллисекундные прерывания ядра. */
periph_id=boot_periph_id(); /* Читает реальные перемычки до адресных mailbox и до ответа на ENTER_BOOT после reset. */
boot_can_init(); /* Настраивает трансивер и физическую скорость до общих фильтров и первой передачи. */
session = F28335Boot_RequestedSession(); /* Потребляет только достоверный запрос приложения с magic и XOR-контролем. */
F28335CanBoot_Init(periph_id); /* Открывает журнал и адресные Boot mailbox только для физического PERIPH данной платы. */
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; /* Разрешает таблицу PIE после записи обоих используемых векторов. */
EINT; /* Приём окна CAN продолжается во время синхронных SPI-транзакций main. */
if (session) F28335CanBoot_Resume(session); /* Отвечает ENTER_BOOT после reset с той же session и остаётся в recovery. */
else { /* Обычный reset даёт CAN окно, затем предпочитает проверенную внутреннюю Flash и использует EEPROM как резерв. */
started = F28335Boot_Now(); /* Отсчитывает три секунды от готового CAN, а не от начала настройки PLL и SPI. */
while ((uint32_t)(F28335Boot_Now()-started)<3000UL && !F28335CanBoot_Active()) { F28335CanBoot_Service(); ServiceDog(); } /* ENTER_BOOT удерживает recovery; обычный PING не изменяет выбранный источник запуска. */
F28335CanBoot_Service(); /* Разбирает кадр на границе трёхсекундного окна до последней проверки активного сеанса. */
if (!F28335CanBoot_Active()) { /* Не запускает основную или резервную программу поверх открытого сеанса CAN Boot. */
if (f28335_flash_app_validate(flash_word, 0, &entry)) start_application(entry, F28335_BOOT_FLASH_SLOT); /* Flash приоритетна только при правильном FAB1, допустимых адресах и совпавшей CRC всех LOAD-секций. */
io.read = F28335Boot_Read; io.write = F28335Boot_Write; io.user = 0; /* Передаёт store ту же аппаратную реализацию, что обслуживает CAN. */
if (f28335_boot_open(&image_store, &io) && f28335_boot_select(&image_store, &slot) && slot < 2U && f28335_boot_load(&image_store, slot, ram_word, 0, &entry)) { /* Проверяет CRC/SCI8, сохраняет attempted до пробного запуска и копирует только допустимую RAM. */
start_application(entry, slot); /* Резервный EEPROM-образ получает прежние trial/CONFIRM и автоматический откат A/B. */
} /* При ошибке чтения, CRC, таблицы или журнала не выполняет неизвестный entry. */
F28335CanBoot_Init(periph_id); /* При переходе в recovery перечитывает журнал, сохраняя прежний физический адрес платы. */
} /* Открытый CAN-сеанс сохраняет метаданные, выбранный слот и подтверждения без повторного Init. */
} /* Завершает выбор между адресным входом и обычным запуском приложения. */
F28335Boot_SetRunning(F28335_AB_NONE); /* Отмечает режим загрузчика, сохраняя уже восстановленную session в RAM автомата. */
for (;;) { F28335CanBoot_Service(); ServiceDog(); } /* Обслуживает обновления и остаётся доступным при пустой/повреждённой EEPROM. */
} /* Нормальный выход выполняется только через запуск RAM-приложения либо watchdog reset. */