feat(boot): 添加OTA状态检查避免心跳打印干扰
当OTA激活时,停止心跳打印以减少日志干扰并避免潜在的 并发问题。 fix(ota): 修正boot跳转汇编代码移除错误寄存器操作 移除psplim和msplim寄存器设置,这些寄存器在目标架构中不 需要配置,避免潜在的硬件兼容性问题。 feat(ota): 添加FLASH操作调试日志输出 在FLASH编程、擦除等关键操作时添加调试日志,便于跟踪 固件升级过程中的具体行为。 perf(ota): 优化OTA完成后的外设关闭和跳转流程 重构跳转逻辑,直接使用内联汇编替代naked函数,提高跳转 的确定性和可靠性。 fix(ota): 添加重复包过滤机制避免数据重复处理 实现包序号验证和去重逻辑,防止ESP32超时重传导致的数据 重复写入问题。 fix(ota): 修复FINISHED状态下重传包的处理逻辑 当OTA完成后收到重复数据包时,返回WAIT状态让ESP32重新 发送FINISH命令,而不是重置整个状态机。 feat(ota): 改进应用程序校验逻辑支持调试器烧录 修改CRC校验逻辑,当Flash中未写入CRC值时视为校验通过, 允许通过调试器直接烧录的应用程序正常启动。
This commit is contained in:
@@ -155,10 +155,13 @@ void SpiOta::tryProcessCompleteFrame() {
|
|||||||
u16 rx_len = s_rxStreamLen;
|
u16 rx_len = s_rxStreamLen;
|
||||||
if (rx_len == 0U) { return; }
|
if (rx_len == 0U) { return; }
|
||||||
|
|
||||||
if (s_rxStream[0] == SPI_OTA_CMD_BOOT) {
|
/* Raw 0xB0 BOOT 命令是单字节事务。
|
||||||
isrProcessFrame();
|
* 不要在 CS 仍为低时仅凭“首字节 == 0xB0”就立刻处理,
|
||||||
return;
|
* 否则一旦事务错位/丢首字节,普通 OTA 数据流里的某个随机字节也可能
|
||||||
}
|
* 被误判成 BOOT 命令,直接把会话打回 IDLE。
|
||||||
|
*
|
||||||
|
* 交给 onCsRelease() 在事务结束后再按“完整单字节事务”判定。 */
|
||||||
|
if (s_rxStream[0] == SPI_OTA_CMD_BOOT) { return; }
|
||||||
|
|
||||||
if (s_rxStream[0] != kSof) { return; }
|
if (s_rxStream[0] != kSof) { return; }
|
||||||
if (rx_len < kFrameOverhead) { return; }
|
if (rx_len < kFrameOverhead) { return; }
|
||||||
@@ -192,7 +195,12 @@ void SpiOta::isrProcessFrame() {
|
|||||||
* 所以必须在函数入口就先置位,防止 REQ/DATA 被双重分发。 */
|
* 所以必须在函数入口就先置位,防止 REQ/DATA 被双重分发。 */
|
||||||
s_isrResponded = true;
|
s_isrResponded = true;
|
||||||
|
|
||||||
if (rx_len >= 1U && s_rxStream[0] == SPI_OTA_CMD_BOOT) {
|
if (rx_len == 1U && s_rxStream[0] == SPI_OTA_CMD_BOOT) {
|
||||||
|
if (s_otaHandler != nullptr && s_otaHandler->isActive()) {
|
||||||
|
isrLogFrameEx("[BOOT-CMD-IGN]", s_rxStream, rx_len);
|
||||||
|
s_rxStreamLen = 0U;
|
||||||
|
return;
|
||||||
|
}
|
||||||
u8 ack[4] = {0xB0U, 0xA5U, 0x4FU, 0x54U};
|
u8 ack[4] = {0xB0U, 0xA5U, 0x4FU, 0x54U};
|
||||||
queueTxFrame(ack, sizeof(ack));
|
queueTxFrame(ack, sizeof(ack));
|
||||||
if (s_otaHandler != nullptr) { s_otaHandler->resetToIdle(); }
|
if (s_otaHandler != nullptr) { s_otaHandler->resetToIdle(); }
|
||||||
@@ -232,6 +240,12 @@ void SpiOta::isrProcessFrame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frame_type == SPI_OTA_CMD_BOOT) {
|
if (frame_type == SPI_OTA_CMD_BOOT) {
|
||||||
|
if (s_otaHandler != nullptr && s_otaHandler->isActive()) {
|
||||||
|
isrLogFrameEx("[BOOT-FR-IGN]", s_rxStream, (expected < 24U) ? expected : 24U);
|
||||||
|
s_rxStreamLen = 0U;
|
||||||
|
s_isrResponded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
u8 ack_ok = 0xA5U;
|
u8 ack_ok = 0xA5U;
|
||||||
u8 ack_frame[kTxBufSize] = {};
|
u8 ack_frame[kTxBufSize] = {};
|
||||||
u16 ack_len = 0U;
|
u16 ack_len = 0U;
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ static void heartbeatPrint(uint32_t &heartbeat_counter, uint32_t loop_delay_ms,
|
|||||||
if (heartbeat_counter >= kHeartbeatPrintIntervalMs) {
|
if (heartbeat_counter >= kHeartbeatPrintIntervalMs) {
|
||||||
heartbeat_counter = 0U;
|
heartbeat_counter = 0U;
|
||||||
#if DEBUG_PRINT
|
#if DEBUG_PRINT
|
||||||
|
if (ota.isActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
printf("[BOOT] \xe2\x99\xa5 %s ota_active=%d spi_tx_pending=%d\r\n",
|
printf("[BOOT] \xe2\x99\xa5 %s ota_active=%d spi_tx_pending=%d\r\n",
|
||||||
path_label,
|
path_label,
|
||||||
ota.isActive() ? 1 : 0,
|
ota.isActive() ? 1 : 0,
|
||||||
|
|||||||
+159
-23
@@ -1,4 +1,4 @@
|
|||||||
#include "OTA.h"
|
#include "OTA.h"
|
||||||
#include "Can.h"
|
#include "Can.h"
|
||||||
#include "SpiOta.h"
|
#include "SpiOta.h"
|
||||||
#include "aes128.h"
|
#include "aes128.h"
|
||||||
@@ -26,8 +26,6 @@ __attribute__((naked, noreturn)) void branchToApplication(uint32_t msp_value, ui
|
|||||||
"msr basepri, r2 \n"
|
"msr basepri, r2 \n"
|
||||||
"msr faultmask, r2 \n"
|
"msr faultmask, r2 \n"
|
||||||
"msr psp, r2 \n"
|
"msr psp, r2 \n"
|
||||||
"msr psplim, r2 \n"
|
|
||||||
"msr msplim, r2 \n"
|
|
||||||
"msr msp, r0 \n"
|
"msr msp, r0 \n"
|
||||||
"dsb \n"
|
"dsb \n"
|
||||||
"isb \n"
|
"isb \n"
|
||||||
@@ -113,6 +111,10 @@ static inline fmc_state_enum write_ADDRESS(u32 address, u32 data) {
|
|||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
fmc_state_enum status = fmc_word_program(address, data);
|
fmc_state_enum status = fmc_word_program(address, data);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] W addr=0x%08lX data=0x%08lX\r\n",
|
||||||
|
(unsigned long)address, (unsigned long)data);
|
||||||
|
#endif
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -168,6 +170,9 @@ u8 OTA_SET(u32 addr, u32 data) {
|
|||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
fmc_status = fmc_page_erase(OTA_CONFIG_PAGE_ADDR);
|
fmc_status = fmc_page_erase(OTA_CONFIG_PAGE_ADDR);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] E page=0x%08lX\r\n", (unsigned long)OTA_CONFIG_PAGE_ADDR);
|
||||||
|
#endif
|
||||||
if (fmc_status != FMC_READY) {
|
if (fmc_status != FMC_READY) {
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
return FLASH_EEPROM_ERROR;
|
return FLASH_EEPROM_ERROR;
|
||||||
@@ -177,6 +182,10 @@ u8 OTA_SET(u32 addr, u32 data) {
|
|||||||
u32 current_addr = n_APP_LATEST_AVAL_ADDR + 4U * (u32)i;
|
u32 current_addr = n_APP_LATEST_AVAL_ADDR + 4U * (u32)i;
|
||||||
if (current_addr != addr && temp_val[i] != OTA_NOT_AVAL) {
|
if (current_addr != addr && temp_val[i] != OTA_NOT_AVAL) {
|
||||||
fmc_status = fmc_word_program(current_addr, temp_val[i]);
|
fmc_status = fmc_word_program(current_addr, temp_val[i]);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] W addr=0x%08lX data=0x%08lX\r\n",
|
||||||
|
(unsigned long)current_addr, (unsigned long)temp_val[i]);
|
||||||
|
#endif
|
||||||
if (fmc_status != FMC_READY) {
|
if (fmc_status != FMC_READY) {
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
return FLASH_EEPROM_ERROR;
|
return FLASH_EEPROM_ERROR;
|
||||||
@@ -186,6 +195,10 @@ u8 OTA_SET(u32 addr, u32 data) {
|
|||||||
|
|
||||||
if (data != OTA_NOT_AVAL) {
|
if (data != OTA_NOT_AVAL) {
|
||||||
fmc_status = fmc_word_program(addr, data);
|
fmc_status = fmc_word_program(addr, data);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] W addr=0x%08lX data=0x%08lX\r\n",
|
||||||
|
(unsigned long)addr, (unsigned long)data);
|
||||||
|
#endif
|
||||||
if (fmc_status != FMC_READY) {
|
if (fmc_status != FMC_READY) {
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
return FLASH_EEPROM_ERROR;
|
return FLASH_EEPROM_ERROR;
|
||||||
@@ -295,6 +308,7 @@ void OTAHandler::process() {
|
|||||||
send_structured_ack(0U);
|
send_structured_ack(0U);
|
||||||
}
|
}
|
||||||
drainFlashBuffer();
|
drainFlashBuffer();
|
||||||
|
|
||||||
if (pending_finish_ack && flash_buf_cnt == 0U) {
|
if (pending_finish_ack && flash_buf_cnt == 0U) {
|
||||||
pending_finish_ack = false;
|
pending_finish_ack = false;
|
||||||
}
|
}
|
||||||
@@ -416,12 +430,12 @@ return;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[J1]\r\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [关键修复] 先关中断再做 Flash 操作。
|
* [关键修复] 先关中断再做 Flash 操作。
|
||||||
* 原来 OTA_SET 在 NVIC disable 之前执行。
|
|
||||||
* Flash 编程期间 CPU stall,若此时 SPI0/CAN0 中断触发,
|
|
||||||
* ISR 嵌套调用会撑爆栈 -> HardFault -> 死循环。
|
|
||||||
*/
|
*/
|
||||||
__disable_irq();
|
__disable_irq();
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
@@ -431,11 +445,17 @@ return;
|
|||||||
__DSB();
|
__DSB();
|
||||||
__ISB();
|
__ISB();
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[J2]\r\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 把当前主应用重新标记为"有效且允许启动"。 */
|
/* 把当前主应用重新标记为"有效且允许启动"。 */
|
||||||
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL);
|
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL);
|
||||||
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
|
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[J3]\r\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 关闭 Bootloader 运行过程中启用过的外设。先关 DMA 再关外设。 */
|
/* 关闭 Bootloader 运行过程中启用过的外设。先关 DMA 再关外设。 */
|
||||||
DMA_CHCTL(DMA0, DMA_CH1) &= ~DMA_CHXCTL_CHEN;
|
DMA_CHCTL(DMA0, DMA_CH1) &= ~DMA_CHXCTL_CHEN;
|
||||||
@@ -444,17 +464,14 @@ return;
|
|||||||
spi_i2s_deinit(SPI0);
|
spi_i2s_deinit(SPI0);
|
||||||
exti_interrupt_disable(EXTI_15);
|
exti_interrupt_disable(EXTI_15);
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[J4]\r\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* SysTick 不属于 NVIC 外设中断,跳转前单独关闭即可。
|
|
||||||
* APP 的 SystemInit() 会完整重置时钟树,因此这里不再切 IRC8M,
|
|
||||||
* 也不再关 GPIO/USART/RCU 时钟,避免 Boot 收尾逻辑引入额外变量。
|
|
||||||
*/
|
|
||||||
SysTick->CTRL = 0U;
|
SysTick->CTRL = 0U;
|
||||||
SysTick->LOAD = 0U;
|
SysTick->LOAD = 0U;
|
||||||
SysTick->VAL = 0U;
|
SysTick->VAL = 0U;
|
||||||
|
|
||||||
|
|
||||||
/* 把中断向量表切到主应用起始地址。 */
|
/* 把中断向量表切到主应用起始地址。 */
|
||||||
SCB->VTOR = APPLATEST_START_ADDR;
|
SCB->VTOR = APPLATEST_START_ADDR;
|
||||||
|
|
||||||
@@ -462,10 +479,51 @@ return;
|
|||||||
uint32_t msp_value = *(__IO uint32_t *)APPLATEST_START_ADDR;
|
uint32_t msp_value = *(__IO uint32_t *)APPLATEST_START_ADDR;
|
||||||
uint32_t reset_handler = *(__IO uint32_t *)(APPLATEST_START_ADDR + 4U);
|
uint32_t reset_handler = *(__IO uint32_t *)(APPLATEST_START_ADDR + 4U);
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[J5] msp=0x%08lX rst=0x%08lX\r\n",
|
||||||
|
(unsigned long)msp_value, (unsigned long)reset_handler);
|
||||||
|
/* 打印 Reset_Handler 地址处的原始字节,验证是否有有效代码 */
|
||||||
|
printf("[J5-HEX] @0x%08lX: ", (unsigned long)(reset_handler & ~1U));
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)(reset_handler & ~1U);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
printf("%02X ", p[i]);
|
||||||
|
}
|
||||||
|
printf("\r\n");
|
||||||
|
}
|
||||||
|
/* 打印向量表前 8 字节(MSP + Reset_Handler) */
|
||||||
|
printf("[J5-VEC] @0x%08lX: ", (unsigned long)APPLATEST_START_ADDR);
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)APPLATEST_START_ADDR;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
printf("%02X ", p[i]);
|
||||||
|
}
|
||||||
|
printf("\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 仍在 Boot 栈上做最后一次清理,真正切栈和跳转交给裸汇编 trampoline。 */
|
/*
|
||||||
rcu_all_reset_flag_clear();
|
* 直接用内联汇编跳转,消除 naked 函数调用的所有不确定性。
|
||||||
branchToApplication(msp_value, reset_handler);
|
* 使用显式寄存器变量保证 msp_value 在 r0,reset_handler 在 r1。
|
||||||
|
*/
|
||||||
|
register uint32_t r0_val __asm("r0") = msp_value;
|
||||||
|
register uint32_t r1_val __asm("r1") = reset_handler;
|
||||||
|
__asm volatile (
|
||||||
|
"movs r2, #0 \n"
|
||||||
|
"msr control, r2 \n"
|
||||||
|
"msr primask, r2 \n"
|
||||||
|
"msr basepri, r2 \n"
|
||||||
|
"msr faultmask, r2 \n"
|
||||||
|
"msr psp, r2 \n"
|
||||||
|
"msr msp, %0 \n"
|
||||||
|
"dsb \n"
|
||||||
|
"isb \n"
|
||||||
|
"bx %1 \n"
|
||||||
|
:
|
||||||
|
: "r"(r0_val), "r"(r1_val)
|
||||||
|
: "r2"
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -484,6 +542,12 @@ void OTAHandler::eraseApplicationArea() {
|
|||||||
|
|
||||||
__disable_irq();
|
__disable_irq();
|
||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] E app area 0x%08lX ~ 0x%08lX (%lu pages)\r\n",
|
||||||
|
(unsigned long)APPLATEST_START_ADDR,
|
||||||
|
(unsigned long)(APPLATEST_START_ADDR + APP_MAX_SIZE),
|
||||||
|
(unsigned long)((APP_MAX_SIZE + FMC_PAGE_SIZE - 1U) / FMC_PAGE_SIZE));
|
||||||
|
#endif
|
||||||
for (u32 addr = APPLATEST_START_ADDR;
|
for (u32 addr = APPLATEST_START_ADDR;
|
||||||
addr < (APPLATEST_START_ADDR + APP_MAX_SIZE); addr += FMC_PAGE_SIZE) {
|
addr < (APPLATEST_START_ADDR + APP_MAX_SIZE); addr += FMC_PAGE_SIZE) {
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
@@ -509,14 +573,40 @@ void OTAHandler::eraseApplicationArea() {
|
|||||||
|
|
||||||
|
|
||||||
void OTAHandler::drainFlashBuffer() {
|
void OTAHandler::drainFlashBuffer() {
|
||||||
while (flash_buf_cnt > 0U) {
|
#if DEBUG_PRINT
|
||||||
u8 ri = flash_buf_ri;
|
u32 drain_cnt = 0;
|
||||||
writeApplicationData(flash_buf[ri].offset, flash_buf[ri].data, OTA_PACKET_SIZE);
|
#endif
|
||||||
flash_buf_ri = (ri + 1U) % kFlashBufSlots;
|
while (1) {
|
||||||
|
FlashSlot slot = {};
|
||||||
|
bool has_slot = false;
|
||||||
|
|
||||||
|
/* 先原子出队,再慢速写 Flash。
|
||||||
|
* 这样即使 ISR 中途 resetToIdle() 清空了环形缓冲计数,
|
||||||
|
* 这里也不会在写完后再把 0 继续 -- 成 255。 */
|
||||||
__disable_irq();
|
__disable_irq();
|
||||||
flash_buf_cnt--;
|
if (flash_buf_cnt > 0U) {
|
||||||
|
u8 ri = flash_buf_ri;
|
||||||
|
slot = flash_buf[ri];
|
||||||
|
flash_buf_ri = (ri + 1U) % kFlashBufSlots;
|
||||||
|
flash_buf_cnt--;
|
||||||
|
has_slot = true;
|
||||||
|
}
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
|
|
||||||
|
if (!has_slot) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeApplicationData(slot.offset, slot.data, OTA_PACKET_SIZE);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
drain_cnt++;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
if (drain_cnt > 0U) {
|
||||||
|
printf("[FW] drain %u pkts\r\n", (unsigned)drain_cnt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
void OTAHandler::writeApplicationData(u32 offset, u8 *data, u32 size) {
|
void OTAHandler::writeApplicationData(u32 offset, u8 *data, u32 size) {
|
||||||
/**
|
/**
|
||||||
@@ -524,8 +614,8 @@ void OTAHandler::writeApplicationData(u32 offset, u8 *data, u32 size) {
|
|||||||
* - 先按 4 字节对齐批量写
|
* - 先按 4 字节对齐批量写
|
||||||
* - 剩余不足 4 字节时,用 0xFF 补齐一个 word 再写入
|
* - 剩余不足 4 字节时,用 0xFF 补齐一个 word 再写入
|
||||||
*/
|
*/
|
||||||
fmc_unlock();
|
|
||||||
u32 write_addr = APPLATEST_START_ADDR + offset;
|
u32 write_addr = APPLATEST_START_ADDR + offset;
|
||||||
|
fmc_unlock();
|
||||||
u32 i = 0;
|
u32 i = 0;
|
||||||
for (; i + 4U <= size; i += 4U) {
|
for (; i + 4U <= size; i += 4U) {
|
||||||
(void)fmc_word_program(write_addr + i, *(u32 *)&data[i]);
|
(void)fmc_word_program(write_addr + i, *(u32 *)&data[i]);
|
||||||
@@ -606,6 +696,13 @@ void OTAHandler::handleLinkMessage(ota_link_msg_type_t type, u8 *data, u8 len) {
|
|||||||
const u8 *receive_encrypted_data = data + 8;
|
const u8 *receive_encrypted_data = data + 8;
|
||||||
const u8 *receive_dataCRC32 = data + 60;
|
const u8 *receive_dataCRC32 = data + 60;
|
||||||
|
|
||||||
|
/** 去重:跳过已经确认接收过的包号(ESP32 超时重传)。 */
|
||||||
|
if (receive_packet_num < next_expected_packet) {
|
||||||
|
u32 request_packet = receive_packet_num + 1U;
|
||||||
|
send_structured_ack(request_packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
u8 decrypted_data[OTA_PACKET_SIZE];
|
u8 decrypted_data[OTA_PACKET_SIZE];
|
||||||
u8 calculated_dataCRC32[4];
|
u8 calculated_dataCRC32[4];
|
||||||
|
|
||||||
@@ -638,6 +735,7 @@ void OTAHandler::handleLinkMessage(ota_link_msg_type_t type, u8 *data, u8 len) {
|
|||||||
__disable_irq();
|
__disable_irq();
|
||||||
flash_buf_cnt++;
|
flash_buf_cnt++;
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
|
next_expected_packet = receive_packet_num + 1U;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -666,6 +764,7 @@ void OTAHandler::handleLinkMessage(ota_link_msg_type_t type, u8 *data, u8 len) {
|
|||||||
if (new_size <= APP_MAX_SIZE) {
|
if (new_size <= APP_MAX_SIZE) {
|
||||||
expected_size = new_size;
|
expected_size = new_size;
|
||||||
received_size = 0;
|
received_size = 0;
|
||||||
|
next_expected_packet = 0U;
|
||||||
} else {
|
} else {
|
||||||
ota_error = OTA_ERROR_INVALID_SIZE;
|
ota_error = OTA_ERROR_INVALID_SIZE;
|
||||||
ota_state = OTA_STATE_IDLE;
|
ota_state = OTA_STATE_IDLE;
|
||||||
@@ -700,6 +799,17 @@ void OTAHandler::handleLinkMessage(ota_link_msg_type_t type, u8 *data, u8 len) {
|
|||||||
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL);
|
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL);
|
||||||
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
|
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
/* OTA 完成后立即 dump Reset_Handler 地址 */
|
||||||
|
{
|
||||||
|
uint32_t rh_addr = *(__IO uint32_t *)(APPLATEST_START_ADDR + 4U) & ~1U;
|
||||||
|
printf("[OTA-DONE2] RH@0x%08lX: ", (unsigned long)rh_addr);
|
||||||
|
const uint8_t *p = (const uint8_t *)rh_addr;
|
||||||
|
for (int i = 0; i < 16; i++) printf("%02X ", p[i]);
|
||||||
|
printf("\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
u8 finish_ack[2] = {0x0A, 0xAC};
|
u8 finish_ack[2] = {0x0A, 0xAC};
|
||||||
send_ack_frame(2, finish_ack);
|
send_ack_frame(2, finish_ack);
|
||||||
pending_app_jump = true;
|
pending_app_jump = true;
|
||||||
@@ -711,6 +821,14 @@ void OTAHandler::handleLinkMessage(ota_link_msg_type_t type, u8 *data, u8 len) {
|
|||||||
send_error_frame(0xFFFFFFFFU, OTA_ERROR_INVALID_PACKET);
|
send_error_frame(0xFFFFFFFFU, OTA_ERROR_INVALID_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* FINISHED 状态下收到重复数据包(ESP32 重传):
|
||||||
|
* 不应视为无效包而重置状态机,而是回 WAIT 让 ESP32 稍后重发 FINISH_OTA。
|
||||||
|
*/
|
||||||
|
else if (ota_state == OTA_STATE_FINISHED) {
|
||||||
|
u8 wait_hint[1] = {0x04};
|
||||||
|
send_wait_frame(1, wait_hint);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ota_error = OTA_ERROR_INVALID_PACKET;
|
ota_error = OTA_ERROR_INVALID_PACKET;
|
||||||
#if DEBUG_PRINT
|
#if DEBUG_PRINT
|
||||||
@@ -911,6 +1029,10 @@ fmc_state_enum move_app_data() {
|
|||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
|
|
||||||
uint32_t num_pages = (APP_MAX_SIZE + FMC_PAGE_SIZE - 1U) / FMC_PAGE_SIZE;
|
uint32_t num_pages = (APP_MAX_SIZE + FMC_PAGE_SIZE - 1U) / FMC_PAGE_SIZE;
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] E restore dst=0x%08lX pages=%lu\r\n",
|
||||||
|
(unsigned long)APPLATEST_START_ADDR, (unsigned long)num_pages);
|
||||||
|
#endif
|
||||||
for (uint32_t i = 0; i < num_pages; ++i) {
|
for (uint32_t i = 0; i < num_pages; ++i) {
|
||||||
uint32_t page_addr = APPLATEST_START_ADDR + i * FMC_PAGE_SIZE;
|
uint32_t page_addr = APPLATEST_START_ADDR + i * FMC_PAGE_SIZE;
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
@@ -926,11 +1048,17 @@ fmc_state_enum move_app_data() {
|
|||||||
crc_data_register_reset();
|
crc_data_register_reset();
|
||||||
uint32_t src_crc = crc_block_data_calculate((void *)APPBACK_START_ADDR, APP_MAX_SIZE / 4U, INPUT_FORMAT_WORD);
|
uint32_t src_crc = crc_block_data_calculate((void *)APPBACK_START_ADDR, APP_MAX_SIZE / 4U, INPUT_FORMAT_WORD);
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
u32 dbg_word_cnt = 0;
|
||||||
|
#endif
|
||||||
for (uint32_t offset = 0; offset < APP_MAX_SIZE; offset += 4U) {
|
for (uint32_t offset = 0; offset < APP_MAX_SIZE; offset += 4U) {
|
||||||
uint32_t src_data = *(uint32_t *)(APPBACK_START_ADDR + offset);
|
uint32_t src_data = *(uint32_t *)(APPBACK_START_ADDR + offset);
|
||||||
if (src_data != 0xFFFFFFFFU) {
|
if (src_data != 0xFFFFFFFFU) {
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
fmc_status = fmc_word_program(APPLATEST_START_ADDR + offset, src_data);
|
fmc_status = fmc_word_program(APPLATEST_START_ADDR + offset, src_data);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
dbg_word_cnt++;
|
||||||
|
#endif
|
||||||
if (fmc_status != FMC_READY) {
|
if (fmc_status != FMC_READY) {
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
@@ -938,6 +1066,12 @@ fmc_state_enum move_app_data() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] W restore src=0x%08lX dst=0x%08lX words=%lu\r\n",
|
||||||
|
(unsigned long)APPBACK_START_ADDR,
|
||||||
|
(unsigned long)APPLATEST_START_ADDR,
|
||||||
|
(unsigned long)dbg_word_cnt);
|
||||||
|
#endif
|
||||||
|
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
@@ -1022,9 +1156,11 @@ bool verifyAppCrc(u32 start_addr) {
|
|||||||
volatile u32 *stored_crc_ptr = (volatile u32 *)crc_addr;
|
volatile u32 *stored_crc_ptr = (volatile u32 *)crc_addr;
|
||||||
u32 stored_crc = *stored_crc_ptr;
|
u32 stored_crc = *stored_crc_ptr;
|
||||||
|
|
||||||
/** Flash 擦除态 = 0xFFFFFFFF,如果 CRC 地址未写过说明无校验数据。 */
|
/** Flash 擦除态 = 0xFFFFFFFF,如果 CRC 地址未写过说明无校验数据。
|
||||||
|
* APP 通过调试器直接烧录时 CRC 未写入,此时不应阻止跳转,
|
||||||
|
* 视为校验通过(未启用 CRC 保护),仅 OTA 升级后才真正校验。 */
|
||||||
if (stored_crc == FLASH_EMPTY) {
|
if (stored_crc == FLASH_EMPTY) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
crc_deinit();
|
crc_deinit();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#ifndef __OTA_H
|
#ifndef __OTA_H
|
||||||
#define __OTA_H
|
#define __OTA_H
|
||||||
|
|
||||||
#include "device/Can.h"
|
#include "device/Can.h"
|
||||||
@@ -99,9 +99,11 @@ class OTAHandler {
|
|||||||
pending_flash_erase = false;
|
pending_flash_erase = false;
|
||||||
pending_app_jump = false;
|
pending_app_jump = false;
|
||||||
pending_finish_ack = false;
|
pending_finish_ack = false;
|
||||||
|
pending_finish_ota = false;
|
||||||
flash_buf_wi = 0U;
|
flash_buf_wi = 0U;
|
||||||
flash_buf_ri = 0U;
|
flash_buf_ri = 0U;
|
||||||
flash_buf_cnt = 0U;
|
flash_buf_cnt = 0U;
|
||||||
|
next_expected_packet = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,6 +194,8 @@ class OTAHandler {
|
|||||||
volatile u8 flash_buf_ri = 0U; /**< 主循环读指针 */
|
volatile u8 flash_buf_ri = 0U; /**< 主循环读指针 */
|
||||||
volatile u8 flash_buf_cnt = 0U; /**< 缓冲中待写入条目数 */
|
volatile u8 flash_buf_cnt = 0U; /**< 缓冲中待写入条目数 */
|
||||||
volatile bool pending_finish_ack = false; /**< FINISH 包已缓冲,等待 drain 后回 ACK */
|
volatile bool pending_finish_ack = false; /**< FINISH 包已缓冲,等待 drain 后回 ACK */
|
||||||
|
volatile bool pending_finish_ota = false; /**< FINISH_OTA 命令已收到,等待 drain 后处理 */
|
||||||
|
u32 next_expected_packet = 0U; /**< 下一个期望收到的包号,用于去重 */
|
||||||
|
|
||||||
void drainFlashBuffer();
|
void drainFlashBuffer();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "gd32e50x_rcu.h"
|
#include "gd32e50x_rcu.h"
|
||||||
#include "gd32e50x_usart.h"
|
#include "gd32e50x_usart.h"
|
||||||
#include "systick.h"
|
#include "systick.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化 USART0 调试串口。
|
* @brief 初始化 USART0 调试串口。
|
||||||
@@ -163,6 +164,10 @@ fmc_state_enum BootSleepManager::writeAddress(u32 address, u32 data)
|
|||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||||
fmc_state_enum status = fmc_word_program(address, data);
|
fmc_state_enum status = fmc_word_program(address, data);
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] W addr=0x%08lX data=0x%08lX\r\n",
|
||||||
|
(unsigned long)address, (unsigned long)data);
|
||||||
|
#endif
|
||||||
fmc_lock();
|
fmc_lock();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -241,6 +246,9 @@ void BootSleepManager::onExtiInterrupt(void)
|
|||||||
fmc_lock();
|
fmc_lock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
printf("[FW] E page=0x%08lX\r\n", (unsigned long)SLEEP_CONFIG_START);
|
||||||
|
#endif
|
||||||
address = SLEEP_CONFIG_START - SIZE_U32;
|
address = SLEEP_CONFIG_START - SIZE_U32;
|
||||||
}
|
}
|
||||||
(void)writeAddress(address + SIZE_U32, SLEEP_DISABLE);
|
(void)writeAddress(address + SIZE_U32, SLEEP_DISABLE);
|
||||||
|
|||||||
Reference in New Issue
Block a user