#include "boot_profile.h" /* Сохраняет карту Flash climate и зарезервированное SRAM-слово запроса. */ #include "pcan_boot_config.h" /* Идентичность прибора и окно ACK остаются конфигурацией исходного проекта. */ #include "../../include/pcan_boot.h" /* Подключает единственную текущую state machine ProtoCAN из templates. */ #include "stm32f407xx.h" /* Сохраняет лёгкую CMSIS-сборку загрузчика без HAL-драйверов. */ #include /* Предоставляет безопасное формирование слов из коротких хвостов CAN. */ #define NATIVE_APP_BASE PCAN_BOOT_APP_ADDRESS /* Приложение climate начинается с физического сектора 2. */ #define NATIVE_APP_SIZE PCAN_BOOT_APP_SIZE /* Ограничивает прошивку секторами 2..5, сохраняя EEPROM-кольцо. */ #define NATIVE_RAM_END 0x20020000UL /* Физическая верхняя граница 128 КиБ SRAM сохраняет совместимость векторов ранее установленных приложений. */ #define NATIVE_PROGRAM_UNIT 4U /* F407 программирует слова x32 при VDD 2,7..3,6 В. */ #define NATIVE_PPRE1_SHIFT 10U /* STM32F4 размещает делитель APB1 в RCC_CFGR[12:10]. */ #define NATIVE_FLASH_ERRORS (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR) /* Проверяет последовательность, разрядность, выравнивание и аппаратную защиту Flash по RM0090. */ static pcan_boot_t boot; static volatile uint32_t ticks_ms; /* Хранит единственный автомат CAN и миллисекундные часы загрузчика. */ static bool native_wait(void) /* Ограничивает аппаратное ожидание Flash без бесконечной блокировки. */ { /* Код продолжит исполняться после аппаратного Flash stall на однобанковом F407. */ uint32_t guard = SystemCoreClock; while ((FLASH->SR & FLASH_SR_BSY) != 0U && guard != 0U) --guard; /* Использует конечный бюджет проверок в единицах итераций текущего CPU. */ return (FLASH->SR & (FLASH_SR_BSY | NATIVE_FLASH_ERRORS)) == 0U; /* Не подтверждает операцию при BUSY или любой выбранной аппаратной ошибке. */ } /* Завершает проверку готовности Flash. */ static bool native_unlock(void) /* Открывает Flash только для текущей операции загрузчика. */ { /* Геометрия профиля исключает массовое стирание и запись Option Bytes. */ if ((FLASH->SR & FLASH_SR_BSY) != 0U) { return false; } FLASH->SR = NATIVE_FLASH_ERRORS; /* Не посылает ключи занятой Flash и очищает предыдущие ошибки W1C. */ if ((FLASH->CR & FLASH_CR_LOCK) != 0U) { FLASH->KEYR = 0x45670123UL; FLASH->KEYR = 0xCDEF89ABUL; } /* Использует штатную последовательность ключей RM0090. */ return (FLASH->CR & FLASH_CR_LOCK) == 0U; /* Проверяет фактическое снятие блокировки Flash. */ } /* Завершает разблокировку контроллера Flash. */ static void native_lock(void) { FLASH->CR = FLASH_CR_LOCK; } /* Выключает PG/SER и восстанавливает LOCK после успеха и обработанных отказов. */ static void native_sync_cache(void) /* Убирает устаревшие строки Flash cache перед readback соседнего записанного слова. */ { /* Сохраняет исходное включение instruction/data cache и остальные настройки ACR. */ uint32_t saved = FLASH->ACR; FLASH->ACR = saved & ~(FLASH_ACR_ICEN | FLASH_ACR_DCEN); /* Отключает кэши перед их аппаратным сбросом. */ FLASH->ACR |= FLASH_ACR_ICRST | FLASH_ACR_DCRST; FLASH->ACR &= ~(FLASH_ACR_ICRST | FLASH_ACR_DCRST); FLASH->ACR = saved; /* Сбрасывает обе cache-памяти и возвращает прежние enable/latency/prefetch настройки. */ __DSB(); __ISB(); /* Синхронизирует последующие чтения Flash и исполняемый поток после reset кэшей. */ } /* Завершает синхронизацию Flash cache без HAL. */ static bool native_program(uint32_t address, uint32_t value) /* Программирует и перечитывает одно слово x32. */ { /* Границы и выравнивание проверяет общий writer до вызова. */ bool ok; FLASH->SR = NATIVE_FLASH_ERRORS; FLASH->CR = FLASH_CR_PG | (2UL << FLASH_CR_PSIZE_Pos); /* Выбирает PSIZE=2, то есть 32-битную запись при VDD 2,7..3,6 В. */ *(volatile uint32_t *)(uintptr_t)address = value; ok = native_wait(); FLASH->CR &= ~FLASH_CR_PG; native_sync_cache(); /* Дожидается завершения записи и сбрасывает устаревшие строки cache перед чтением. */ return ok && *(const volatile uint32_t *)(uintptr_t)address == value; /* Проверяет аппаратные флаги и реально прочитанное значение, включая дополнение FF. */ } /* Завершает одну аппаратную запись F407. */ static bool native_erase(uint32_t size) /* Стирает только используемые секторы 2..5 приложения climate. */ { /* EEPROM-кольцо 6/7 и загрузочные 0/1 не включаются в таблицу разрешённых секторов. */ static const uint32_t starts[4] = {0x08008000UL, 0x0800C000UL, 0x08010000UL, 0x08020000UL}; /* RM0090: начала секторов 2,3,4,5 объёмом 16,16,64,128 КиБ. */ uint32_t index; bool ok = true; /* Хранит текущий сектор и результат до восстановления LOCK. */ if (size < 8U || size > NATIVE_APP_SIZE || !native_unlock()) return false; /* Отвергает неподдерживаемый размер до первого изменения Flash. */ for (index = 0U; index < 4U && starts[index] < NATIVE_APP_BASE + size; ++index) { /* Не стирает следующий сектор, когда образ заканчивается точно на его границе. */ (void)pcan_boot_report_busy(&boot, PCAN_BOOT_CMD_ERASE, ticks_ms); FLASH->SR = NATIVE_FLASH_ERRORS; /* Продлевает сессию и сообщает прогресс между длительными стираниями. */ FLASH->CR = FLASH_CR_SER | (2UL << FLASH_CR_PSIZE_Pos) | ((index + 2U) << FLASH_CR_SNB_Pos); FLASH->CR |= FLASH_CR_STRT; /* Запускает erase конкретного сектора с разрядностью x32. */ if (!native_wait()) { ok = false; break; } /* Прерывает обновление на первом аппаратном отказе. */ } /* Завершает обход разрешённых секторов. */ native_lock(); native_sync_cache(); return ok; /* Восстанавливает LOCK и обновляет cache как после успеха, так и после обработанного отказа. */ } /* Завершает аппаратное стирание F407. */ static void native_gpio_init(void) /* Сохраняет исходный CAN1 climate на PD0/PD1 AF9. */ { /* Тактирование APB1 определяется общим кодом из фактического RCC_CFGR. */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; /* Разрешает аппаратные такты GPIOD и CAN1. */ GPIOD->MODER = (GPIOD->MODER & ~15UL) | 10UL; GPIOD->PUPDR = (GPIOD->PUPDR & ~3UL) | 1UL; /* Назначает PD0/PD1 альтернативной функцией и подтягивает RX к рецессивному уровню. */ GPIOD->OSPEEDR |= 15UL; GPIOD->AFR[0] = (GPIOD->AFR[0] & ~255UL) | 0x99UL; /* Выбирает высокую скорость GPIO и AF9 обоих выводов CAN1 согласно datasheet F407. */ } /* Завершает настройку физического CAN-порта. */ static void native_peripheral_stop(void) { RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; } /* Сбрасывает CAN1 перед handoff без изменения памяти приложения и EEPROM. */ #include "../stm32-common/boot_native.inc" /* Подключает общий ProtoCAN transport, CRC, callbacks и безопасную замену MSP. */ int main(void) /* Самостоятельный загрузчик climate, подключаемый тонкой обёрткой исходного проекта. */ { /* Сборка PCAN_BOOTLOADER_BUILD сохраняет VTOR startup в начале Flash. */ bool requested = *(volatile uint32_t *)(uintptr_t)PCAN_BOOT_REQUEST_ADDRESS == PCAN_BOOT_REQUEST_MAGIC; /* Читает запрос из отдельного слова SRAM, не используемого стеком или ZI. */ *(volatile uint32_t *)(uintptr_t)PCAN_BOOT_REQUEST_ADDRESS = 0U; /* Потребляет запрос ровно один раз до запуска интерфейсов. */ SystemCoreClockUpdate(); if (SysTick_Config(SystemCoreClock / 1000U) != 0U || !native_init()) for (;;) {} /* Использует фактический HCLK и создаёт millisecond tick до CAN-тайм-аутов. */ while (!native_can_init()) { if (!requested && native_vectors_valid(NATIVE_APP_SIZE)) native_jump(NATIVE_APP_BASE); native_peripheral_stop(); } /* При недоступной шине сохраняет обычный запуск установленного приложения, а по запросу остаётся восстанавливаемым. */ if (!requested && native_vectors_valid(NATIVE_APP_SIZE)) { /* Сохраняет исходное десятисекундное окно входа в обновление после обычного reset. */ uint32_t started = ticks_ms; while ((uint32_t)(ticks_ms - started) < 10000U) { native_can_poll(); if (boot.state != PCAN_BOOT_STATE_IDLE) { requested = true; break; } } /* Обслуживает discovery и команды без ложного входа по одному IDENTIFY. */ if (!requested) native_jump(NATIVE_APP_BASE); /* Запускает приложение после окна, используя безопасный naked handoff. */ } /* Завершает политику обычного запуска. */ for (;;) native_can_poll(); /* Остаётся в общем ProtoCAN-автомате до подтверждённого COMMIT/REBOOT. */ } /* Завершает самостоятельный CMSIS-загрузчик F407. */