Обеспечь единый процесс обновления для STM32F103/F407/G474 и TMS320F2812/F28335: discovery, служебные ответы, BUSY, тайм-аут сессии и подтверждение приложения. Добавь самостоятельные проекты CCS, аппаратные хранилища и Python-клиент. Совместимость: структуры конфигурации и порта расширены, поэтому потребители должны пересобраться и обнулить новые поля. Для C28x callbacks используют pcan_boot_byte_t; периодический pcan_boot_poll обслуживает discovery и настроенный тайм-аут.
49 lines
8.7 KiB
C
49 lines
8.7 KiB
C
#include "boot_profile.h" /* Получает единственный источник адресов, magic и функций входа CANdle G474. */
|
||
#include "stm32g4xx.h" /* Использует исходный CMSIS-порт USART/SRAM без зависимости приложения от загрузочного main. */
|
||
static volatile bool request; /* IRQ либо CAN-диспетчер отмечает запрос, который основной цикл может безопасно обработать. */
|
||
static uint8_t frame[28], used; /* Кадр BEGIN содержит 8 байт заголовка, 16 байт payload и 4 байта CRC, всего 28 байт. */
|
||
static uint32_t read32(const uint8_t *data) { return (uint32_t)data[0] | ((uint32_t)data[1] << 8U) | ((uint32_t)data[2] << 16U) | ((uint32_t)data[3] << 24U); } /* Извлекает little-endian CRC без невыровненного чтения. */
|
||
static uint32_t crc32(const uint8_t *data, uint8_t length) /* Малый app-only codec сохраняет отсутствие зависимости приложения от boot-автомата. */
|
||
{ /* Формат PING/BEGIN совпадает с SETGUI v1, исходный полином IEEE отражённый. */
|
||
uint32_t crc = 0xFFFFFFFFUL; uint8_t index, bit; /* Начинает IEEE CRC32 с инверсного состояния, как исходное приложение CANdle. */
|
||
for (index = 0U; index < length; ++index) { crc ^= data[index]; for (bit = 0U; bit < 8U; ++bit) crc = (crc >> 1U) ^ ((crc & 1U) != 0U ? 0xEDB88320UL : 0U); } /* Обрабатывает только проверенные октеты и восемь бит каждого байта. */
|
||
return ~crc; /* Выполняет финальный XOR, совместимый с zlib и загрузчиком SETGUI v1. */
|
||
} /* Завершает совместимый малый CRC приложения. */
|
||
void can_boot_observe(uint32_t id, bool extended, uint8_t dlc) /* Не переводит приложение в обновление по чужому либо невалидному кадру. */
|
||
{ /* Переход откладывается до вызова can_boot_enter из существующего main. */
|
||
if (extended && dlc == 0U && id <= 0x1FFFFFFFUL && ((id >> 27U) & 1U) == 0U && ((id >> 24U) & 7U) == CAN_BOOT_DEVICE_TYPE && ((id >> 20U) & 15U) == CAN_BOOT_DEVICE_ID && ((id >> 16U) & 15U) == 9U && (id & 255U) == 2U && ((id >> 8U) & 255U) != 0U) request = true; /* Требует Route=0, собственный адрес, BOOT_CONTROL/ENTER_BOOT и ненулевую сессию. */
|
||
} /* Завершает наблюдение входящего CAN без чтения отсутствующего payload. */
|
||
bool can_boot_requested(void) { return request; } /* Сохраняет прежний API опроса приложения CANdle. */
|
||
void can_boot_enter(void) { *(volatile uint32_t *)(uintptr_t)CAN_BOOT_REQUEST_ADDRESS = CAN_BOOT_REQUEST_MAGIC; __DSB(); NVIC_SystemReset(); } /* Записывает CANB в зарезервированное SRAM-слово до системного сброса. */
|
||
static void process_frame(void) /* Принимает только проверенные короткие PING и BEGIN приложения. */
|
||
{ /* Полный firmware flow после BEGIN обслуживает уже общий загрузчик из templates. */
|
||
uint8_t payload = frame[7]; uint32_t checksum; /* Размер payload предварительно ограничен шестнадцатью байтами. */
|
||
if (frame[2] != 1U || read32(frame + 8U + payload) != crc32(frame + 2U, (uint8_t)(6U + payload))) { return; } /* Не выполняет reset по кадру другой версии либо повреждённому CRC. */
|
||
if (frame[3] == 0x0BU && payload == 16U) { can_boot_enter(); return; } /* Сохраняет существующий SETGUI FIRMWARE_BEGIN как запрос входа в загрузчик. */
|
||
if (frame[3] == 1U && payload == 0U) { /* PING подтверждает наличие UART приложения без входа в обновление. */
|
||
uint8_t reply[15] = {0xA5U, 0x5AU, 1U, 0x81U, frame[4], frame[5], 0U, 3U, 0U, 0U, 1U}; uint8_t index; /* Формирует прежний ACK PING с номером входящего запроса и трёхбайтовым результатом. */
|
||
checksum = crc32(reply + 2U, 9U); for (index = 0U; index < 4U; ++index) reply[11U + index] = (uint8_t)(checksum >> (index * 8U)); /* Добавляет CRC32 LE после девяти защищаемых байтов ver/header/payload. */
|
||
for (index = 0U; index < sizeof(reply); ++index) { while ((USART1->ISR & USART_ISR_TXE_TXFNF) == 0U) {} USART1->TDR = reply[index]; } /* Отправляет полный совместимый ответ USART1. */
|
||
while ((USART1->ISR & USART_ISR_TC) == 0U) {} USART1->ICR = USART_ICR_TCCF; /* Дожидается последнего стоп-бита и очищает флаг завершения передачи. */
|
||
} /* Остальные сообщения приложения не относятся к входу в boot и игнорируются. */
|
||
} /* Завершает обработку валидного короткого кадра приложения. */
|
||
void can_boot_uart_init(void) /* Сохраняет исходную разводку и IRQ-приём UART CANdle G474. */
|
||
{ /* Этот код компилируется только в работающем приложении с CONFIG_CAN_BOOT_APP. */
|
||
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; /* Включает GPIOA и USART1 в соответствующих clock-доменах G474. */
|
||
GPIOA->MODER = (GPIOA->MODER & ~((3UL << 18U) | (3UL << 20U))) | (2UL << 18U) | (2UL << 20U); /* Переводит PA9 и PA10 в режим альтернативной функции. */
|
||
GPIOA->AFR[1] = (GPIOA->AFR[1] & ~((15UL << 4U) | (15UL << 8U))) | (7UL << 4U) | (7UL << 8U); /* Назначает AF7 USART1 TX/RX согласно исходной плате CANdle. */
|
||
USART1->BRR = (SystemCoreClock + 256000UL) / 512000UL; USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE | USART_CR1_UE; /* Сохраняет 512000 бод при исходном APB2=HCLK и приём по RXNE IRQ. */
|
||
NVIC_SetPriority(USART1_IRQn, 2U); NVIC_EnableIRQ(USART1_IRQn); used = 0U; /* Сохраняет приоритет 2 исходного UART и очищает parser перед приёмом. */
|
||
} /* Завершает настройку входа приложения через UART. */
|
||
void can_boot_uart_poll(void) /* Принимает один октет, проверяет длину и синхронизацию до записи в малый буфер. */
|
||
{ /* Длина 28 байт покрывает только PING и BEGIN; больших команд здесь нет. */
|
||
uint32_t status = USART1->ISR; uint8_t byte, total; /* Сохраняет флаги USART до чтения RDR. */
|
||
if ((status & (USART_ISR_ORE | USART_ISR_FE | USART_ISR_NE)) != 0U) { USART1->ICR = USART_ICR_ORECF | USART_ICR_FECF | USART_ICR_NECF; used = 0U; return; } /* Сбрасывает parser после аппаратной потери либо повреждения байта. */
|
||
if ((status & USART_ISR_RXNE_RXFNE) == 0U) { return; } byte = (uint8_t)USART1->RDR; /* Не читает RDR без доступного октета. */
|
||
if (used == 0U && byte != 0xA5U) { return; } if (used == 1U && byte != 0x5AU) { used = byte == 0xA5U ? 1U : 0U; return; } /* Ищет двухбайтовую сигнатуру A5 5A с сохранением возможного начала следующего кадра. */
|
||
frame[used++] = byte; if (used < 8U) { return; } /* Накапливает только ограниченный буфер и ждёт полного восьмибайтового заголовка. */
|
||
if (frame[6] != 0U || frame[7] > 16U) { used = 0U; return; } /* Запрещает длину больше шестнадцати байтов до дальнейшего накопления. */
|
||
total = (uint8_t)(12U + frame[7]); if (used < total) { return; } process_frame(); used = 0U; /* Передаёт полный header/payload/CRC обработчику и начинает следующий короткий кадр. */
|
||
} /* Завершает безопасный parser приложения. */
|
||
void USART1_IRQHandler(void) { can_boot_uart_poll(); } /* Сохраняет прежний экспорт IRQ, используемый startup приложения CANdle. */
|