#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 /* Копирует 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. */