Обеспечь единый процесс обновления для STM32F103/F407/G474 и TMS320F2812/F28335: discovery, служебные ответы, BUSY, тайм-аут сессии и подтверждение приложения. Добавь самостоятельные проекты CCS, аппаратные хранилища и Python-клиент. Совместимость: структуры конфигурации и порта расширены, поэтому потребители должны пересобраться и обнулить новые поля. Для C28x callbacks используют pcan_boot_byte_t; периодический pcan_boot_poll обслуживает discovery и настроенный тайм-аут.
96 lines
16 KiB
C
96 lines
16 KiB
C
#include "boot_profile.h" /* Сохраняет карту памяти, BKP-запросы и CAN-адрес исходного KONOR. */
|
|
#include "../../include/pcan_boot.h" /* Использует единственную текущую машину состояний ProtoCAN из templates. */
|
|
#include "../../../rs485-boot/include/rs485_boot.h" /* Повторно использует общий SETGUI v1 UART-автомат вместо локальной копии parser/CRC. */
|
|
#include "stm32f10x.h" /* Работает с существующим CMSIS/StdPeriph проекта без добавления HAL. */
|
|
#include <string.h> /* Предоставляет безопасное копирование коротких хвостов Flash. */
|
|
#define NATIVE_APP_BASE BOOT_APP_ADDRESS /* Выбирает начало приложения после 12 КиБ загрузчика. */
|
|
#define NATIVE_APP_SIZE (BOOT_FLASH_END - BOOT_APP_ADDRESS) /* Ограничивает обновление 52 КиБ штатной Flash C8. */
|
|
#define NATIVE_RAM_END 0x20005000UL /* Начальный MSP допускается до верхней границы 20 КиБ SRAM. */
|
|
#define NATIVE_PROGRAM_UNIT 2U /* Контроллер Flash STM32F103 записывает полуслова по два байта. */
|
|
#define NATIVE_PPRE1_SHIFT 8U /* STM32F1 размещает делитель APB1 в RCC_CFGR[10:8]. */
|
|
static pcan_boot_t boot; static volatile uint32_t ticks_ms; /* Хранит CAN-сессию и монотонный счётчик SysTick загрузчика. */
|
|
static rs485_boot_t uart_boot; /* Хранит UART parser, повторяемую запись блоков и статус общего SETGUI v1-автомата. */
|
|
static bool native_wait(void) /* Ограничивает ожидание Flash и проверяет аппаратные ошибки программирования. */
|
|
{ /* На однобанковой Flash CPU может останавливаться до завершения операции. */
|
|
uint32_t guard = SystemCoreClock; /* Конечный бюджет проверок численно равен HCLK в Гц; это число итераций CPU, а не аппаратный тайм-аут. */
|
|
while ((FLASH->SR & FLASH_SR_BSY) != 0U && guard != 0U) --guard; /* Не продолжает запись до освобождения контроллера Flash. */
|
|
return (FLASH->SR & (FLASH_SR_BSY | FLASH_SR_PGERR | FLASH_SR_WRPRTERR)) == 0U; /* Ошибки записи или защиты делают аппаратную операцию неуспешной. */
|
|
} /* Завершает ожидание Flash с аппаратной проверкой. */
|
|
static bool native_unlock(void) /* Открывает Flash для одной ограниченной операции. */
|
|
{ /* Не меняет Option Bytes или защиту загрузчика. */
|
|
if ((FLASH->SR & FLASH_SR_BSY) != 0U) return false; /* Не посылает ключи занятому контроллеру. */
|
|
FLASH->SR = FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP; /* Очищает завершение и прежние ошибки записью W1C. */
|
|
if ((FLASH->CR & FLASH_CR_LOCK) != 0U) { FLASH->KEYR = 0x45670123UL; FLASH->KEYR = 0xCDEF89ABUL; } /* Использует обязательную последовательность ключей STM32F1 из RM0008. */
|
|
return (FLASH->CR & FLASH_CR_LOCK) == 0U; /* Проверяет фактическое снятие аппаратной блокировки. */
|
|
} /* Завершает разблокировку без сохранения разрешения между командами. */
|
|
static void native_lock(void) { FLASH->CR = FLASH_CR_LOCK; } /* Снимает PG/PER и восстанавливает LOCK даже после обработанной ошибки. */
|
|
static bool native_program(uint32_t address, uint32_t value) /* Записывает и перечитывает одно выровненное полуслово. */
|
|
{ /* Границы и выравнивание заранее проверяет общий writer. */
|
|
volatile uint16_t *destination = (volatile uint16_t *)(uintptr_t)address; /* Выбирает аппаратную 16-битную операцию F103. */
|
|
FLASH->SR = FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP; FLASH->CR = FLASH_CR_PG; /* Очищает прежний результат и разрешает программирование полуслова. */
|
|
*destination = (uint16_t)value; /* Передаёт младшие 16 бит, включая FF-дополнение неполного хвоста. */
|
|
if (!native_wait()) return false; /* Не подтверждает запись при BUSY или аппаратной ошибке. */
|
|
FLASH->CR = 0U; return *destination == (uint16_t)value; /* Убирает PG и проверяет реально прочитанное полуслово. */
|
|
} /* Завершает одну аппаратную запись. */
|
|
static bool native_erase(uint32_t size) /* Стирает страницы приложения, не пересекая область загрузчика. */
|
|
{ /* Общая геометрия страницы задаётся единственным профилем F103C8. */
|
|
uint32_t address, end; bool ok = true; /* Хранит исключённый конец стирания и результат до блокировки Flash. */
|
|
if (size < 8U || size > NATIVE_APP_SIZE) return false; /* Не стирает Flash для пустого или слишком большого образа. */
|
|
end = BOOT_APP_ADDRESS + ((size + BOOT_PAGE_SIZE - 1U) & ~(BOOT_PAGE_SIZE - 1U)); /* Округляет конец вверх к странице 1024 байта внутри профиля. */
|
|
if (end > BOOT_FLASH_END || !native_unlock()) return false; /* Проверяет округлённую верхнюю границу до первого стирания. */
|
|
for (address = BOOT_APP_ADDRESS; address < end; address += BOOT_PAGE_SIZE) { /* Обходит только страницы, покрывающие полезный образ. */
|
|
FLASH->SR = FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP; FLASH->CR = FLASH_CR_PER; FLASH->AR = address; FLASH->CR |= FLASH_CR_STRT; /* Запускает erase одной конкретной страницы вместо mass erase. */
|
|
if (!native_wait()) { ok = false; break; } /* Прекращает стирание на первой аппаратной ошибке. */
|
|
} /* Завершает ограниченный диапазон страниц. */
|
|
native_lock(); return ok; /* Обязательно закрывает Flash после успешного или прерванного erase. */
|
|
} /* Завершает аппаратное стирание F1. */
|
|
static void native_gpio_init(void) /* Сохраняет исходную разводку CAN1 KONOR: PA11 RX и PA12 TX. */
|
|
{ /* UART на PA9/PA10 остаётся независимым загрузочным транспортом. */
|
|
uint32_t value; RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; /* Включает GPIOA, AFIO и CAN1 в соответствующих APB-доменах. */
|
|
value = GPIOA->CRH; value &= ~((0xFUL << 12U) | (0xFUL << 16U)); value |= (0x8UL << 12U) | (0xBUL << 16U); GPIOA->CRH = value; /* Настраивает PA11 как вход с подтяжкой, PA12 как AF push-pull согласно RM0008. */
|
|
GPIOA->BSRR = 1UL << 11U; /* Выбирает подтяжку CAN RX вверх, соответствующую рецессивному состоянию неподключённой линии. */
|
|
} /* Завершает настройку физического CAN-порта. */
|
|
static void native_peripheral_stop(void) { USART1->CR1 = 0U; RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; } /* Отключает загрузочный USART и сбрасывает CAN перед handoff. */
|
|
#include "../stm32-common/boot_native.inc" /* Подключает общий транспорт, single-slot callbacks, CRC readback и безопасный переход MSP. */
|
|
static bool uart_transmit(void *user, const uint8_t *data, uint16_t length) /* Отправляет кадры SETGUI v1 с ожиданием последнего стоп-бита. */
|
|
{ /* Использует прежнюю скорость USART1 512000 бод без управления RS-485 DE. */
|
|
uint16_t index; (void)user; /* UART-автомат использует единственный статический аппаратный порт. */
|
|
for (index = 0U; index < length; ++index) { while ((USART1->SR & USART_SR_TXE) == 0U) {} USART1->DR = data[index]; } /* Передаёт только объявленные байты полного кадра. */
|
|
while ((USART1->SR & USART_SR_TC) == 0U) {} return true; /* Дожидается физического завершения ответа перед возможным reset. */
|
|
} /* Завершает аппаратную передачу UART. */
|
|
static bool uart_erase(void *user, uint32_t size) { (void)user; return boot.state == PCAN_BOOT_STATE_IDLE && native_erase(size); } /* Не позволяет UART стереть Flash во время открытой CAN-сессии. */
|
|
static bool uart_write(void *user, uint32_t offset, const uint8_t *data, uint16_t length) { (void)user; return native_write_bytes(uart_boot.image_size, offset, data, length); } /* Использует ту же проверку диапазонов и аппаратную запись, что CAN. */
|
|
static bool uart_read(void *user, uint32_t offset, uint8_t *data, uint16_t length) /* Позволяет общему UART-автомату подтвердить повторно присланный блок. */
|
|
{ /* Проверяет размер до сложения адреса, исключая переполнение. */
|
|
(void)user; if (data == NULL || offset > NATIVE_APP_SIZE || length > NATIVE_APP_SIZE - offset) return false; /* Не читает за областью приложения. */
|
|
(void)memcpy(data, (const void *)(uintptr_t)(NATIVE_APP_BASE + offset), length); return true; /* Копирует только объявленный безопасный диапазон Flash. */
|
|
} /* Завершает UART readback для повторов передачи. */
|
|
static bool uart_image_valid(void *user, uint32_t size, uint32_t crc) { (void)user; return native_image_valid(size, crc); } /* Перед END использует тот же CRC Flash и проверку векторов, что CAN VERIFY. */
|
|
static void uart_init(void) /* Инициализирует существующий TTL UART и общий SETGUI v1-автомат. */
|
|
{ /* Локальный Proto parser исходного проекта загрузчику больше не нужен. */
|
|
uint32_t value; rs485_boot_config_t config = {BOOT_APP_ADDRESS, NATIVE_APP_SIZE, RS485_BOOT_MAX_BLOCK}; rs485_boot_port_t callbacks = {0}; /* Сохраняет прежний wire-протокол и предел 504 полезных байта на DATA. */
|
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN; value = GPIOA->CRH; value &= ~((0xFUL << 4U) | (0xFUL << 8U)); value |= (0xBUL << 4U) | (0x4UL << 8U); GPIOA->CRH = value; /* Настраивает PA9 TX AF-PP и PA10 RX floating, как исходный KONOR. */
|
|
USART1->BRR = (SystemCoreClock + 256000UL) / 512000UL; USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; /* При штатном APB2=HCLK сохраняет округлённую скорость 512000 бод, 8-N-1. */
|
|
callbacks.transmit = uart_transmit; callbacks.erase = uart_erase; callbacks.write = uart_write; callbacks.read = uart_read; callbacks.image_valid = uart_image_valid; callbacks.reboot = native_reboot; /* Подключает аппаратные операции к общему UART-автомату. */
|
|
(void)rs485_boot_init(&uart_boot, &config, &callbacks, NULL); /* Инициализирует parser и RAM-сессию отдельного транспорта. */
|
|
} /* Завершает настройку UART без локальной копии codec. */
|
|
static void uart_poll(void) /* Обслуживает UART из главного цикла без прерываний и выделения памяти. */
|
|
{ /* Во время CAN-сессии UART BEGIN отвергается callback стирания. */
|
|
uint32_t status = USART1->SR; uint8_t byte; /* Сохраняет статус до чтения регистра данных. */
|
|
if ((status & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) != 0U) { byte = (uint8_t)USART1->DR; (void)byte; } /* Снимает аппаратные ошибки чтением SR/DR и отбрасывает повреждённый байт. */
|
|
else if ((status & USART_SR_RXNE) != 0U) { byte = (uint8_t)USART1->DR; rs485_boot_feed(&uart_boot, &byte, 1U); } /* Передаёт исправный байт общему parser с прежним кадрированием A5 5A. */
|
|
} /* Завершает неблокирующий приём UART. */
|
|
int main(void) /* Самостоятельная точка входа общего загрузчика KONOR для Keil/GCC. */
|
|
{ /* Тактирование и startup остаются в исходном проекте и выполняются до main. */
|
|
uint16_t request; bool can_ready; /* Хранит одноразовое назначение reset и результат включения CAN. */
|
|
RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; PWR->CR |= PWR_CR_DBP; request = (uint16_t)BKP->DR1; BKP->DR1 = 0U; /* Потребляет совместимый BKP-запрос ровно один раз. */
|
|
if (request == BOOT_REQUEST_SYSTEM_MAGIC) { /* Сохраняет исходную подготовку периферии перед переходом в заводской UART-загрузчик. */
|
|
__disable_irq(); RCC->APB1RSTR = 0U; RCC->APB2RSTR = 0U; RCC->AHBENR = 0x00000014UL; RCC->APB2ENR = 0U; RCC->APB1ENR = 0U; /* Как исходный KONOR, оставляет только AHB Flash/SRAM и отключает внешнюю APB-периферию перед System Memory. */
|
|
native_jump(0x1FFFF000UL); /* AN2606: фиксированное начало System Memory STM32F103 medium density, дальнейший переход выполняется без доступа к старому MSP. */
|
|
} /* Завершает совместимую ветку заводского загрузчика. */
|
|
if (request != BOOT_REQUEST_FIRMWARE_MAGIC && native_vectors_valid(NATIVE_APP_SIZE)) native_jump(BOOT_APP_ADDRESS); /* Сохраняет немедленный обычный запуск KONOR при допустимых векторах приложения. */
|
|
SystemCoreClockUpdate(); if (SysTick_Config(SystemCoreClock / 1000U) != 0U || !native_init()) for (;;) {} /* Создаёт миллисекундный tick и проверяет текущую конфигурацию ProtoCAN до обработки команд. */
|
|
uart_init(); can_ready = native_can_init(); /* При отказе CAN сохраняет доступный UART-канал восстановления. */
|
|
for (;;) { uart_poll(); if (can_ready && uart_boot.state != RS485_BOOT_RECEIVING) native_can_poll(); } /* Не позволяет двум транспортам одновременно изменять одну область Flash. */
|
|
} /* Завершает самостоятельный загрузчик; управление остаётся в сервисном цикле до успешного обновления. */
|