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:
cbb
2026-06-02 22:13:30 +08:00
parent c801701783
commit 061bcabc37
5 changed files with 194 additions and 29 deletions
+19 -5
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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();
+5 -1
View File
@@ -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();
+8
View File
@@ -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);