Files
GD32E50X_BOOT/GD32E50X_BOOT/sys/initial.cpp
T
cbb 45690f6134 fix(SpiOta): 修复ISR响应缓存逻辑,只缓存ACK/ERROR拒绝WAIT
此前isrProcessFrame()无条件将当前s_txBuf缓存到s_lastRespBuf,
导致OTA数据包在ISR入队时立即返回的WAIT被写入重发缓存,覆盖
了sender()内只缓存ACK的语义。当ESP32超时重发时,DUP-SEQ分支
回放的仍是过期WAIT,造成50ms等待→重发链被拉长的问题。

现在按v2头TYPE字节过滤,只将ACK/ERROR放入重发缓存,WAIT由
sender()途径主循环写ACK时再统一更新缓存。

feat(main): 添加ESP32_EN引脚初始化和调试信息输出

添加PC9引脚初始化代码,确保BOOT阶段ESP32能正常启动。
同时增加调试打印信息,便于检查ESP32_EN状态和D3V3电源域
就绪情况。

perf(boot): 优化主循环节拍控制策略

回滚之前"有工作时不休眠"的尝试,恢复为固定1ms节拍。
因为条件化延迟会让ESP32在armSpiDmasForNextCs()的
spi_dma_disable/enable窗口中抓取CS,读到错帧导致通信异常。

fix(OTA): 恢复20us超时步进以保证SPI通信稳定性

回滚之前"本轮干活就不delay"的尝试,恢复为统一20us步进。
因为实测发现条件化延迟会让ESP32在DMA开关窗口中抓取CS,
导致READ错帧和saw_wait=0问题。

chore(config): 更新固件版本号至v1.1.0.5

feat(UART): 将调试串口波特率提升至3Mbps

提升Bootloader系统的调试串口通信速率,从11520提升至
3000000,加快日志输出和调试效率。
2026-09-01 02:48:04 +08:00

247 lines
7.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "initial.h"
#include "OTA.h"
#include "config.h"
#include "device/SpiOta.h"
#include "core_cm33.h"
#include "gd32e50x.h"
#include "gd32e50x_exti.h"
#include "gd32e50x_fmc.h"
#include "gd32e50x_gpio.h"
#include "gd32e50x_pmu.h"
#include "gd32e50x_rcu.h"
#include "gd32e50x_usart.h"
#include "systick.h"
#include <stdio.h>
/**
* @brief 初始化 USART0 调试串口。
*
* @return 1 表示初始化完成。
*/
u8 BootDebugUart::init()
{
/** 打开 GPIOA 与 USART0 时钟。 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
/** PA9 = TX,PA10 = RX。 */
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
/** 配置串口为 3000000 8N1(其余参数使用默认值)。 */
usart_deinit(USART0);
usart_baudrate_set(USART0, 3000000U);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);
return 1;
}
/**
* @brief printf 重定向输出函数。
*
* @param ch 待输出字符。
* @param f 目标文件流,当前实现未使用。
* @return 输出字符值。
*/
int BootDebugUart::putChar(int ch, FILE *f)
{
/** printf 最终走到这里,通过 USART0 单字节输出。 */
(void)f;
usart_data_transmit(USART0, (uint8_t)ch);
while (RESET == usart_flag_get(USART0, USART_FLAG_TBE)) {
}
return ch;
}
/**
* @brief Redirect standard output character to the boot debug UART.
* @param ch Output character.
* @param f Stream pointer from libc.
* @return Forwarded character value.
*/
int fputc(int ch, FILE *f) throw() { return BootDebugUart::putChar(ch, f); }
/**
* @brief 向指定 Flash 地址写入一个 32 位字。
*
* @param address 目标地址。
* @param data 目标数据。
* @return FMC 编程状态码。
*/
fmc_state_enum BootSleepManager::writeAddress(u32 address, u32 data)
{
/**
* 对单个 Flash word 做安全写入:
* - 解锁 FMC
* - 清状态位
* - 编程
* - 重新上锁
*/
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
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();
return status;
}
/**
* @brief 进入深度睡眠模式。
*
* @return 无。
*/
void BootSleepManager::enterDeepSleep(void)
{
/**
* 进入深睡前先关掉暂时不需要的外设时钟,降低待机功耗。
*/
rcu_periph_clock_disable(RCU_TIMER0);
rcu_periph_clock_disable(RCU_TIMER1);
rcu_periph_clock_disable(RCU_ADC0);
/**
* 使用 PB8 作为唤醒外部事件输入。
* 这里配置为浮空输入,并挂接 EXTI8 上升沿触发。
*/
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOB, GPIO_PIN_SOURCE_8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_RISING);
exti_interrupt_enable(EXTI_8);
nvic_irq_enable(EXTI5_9_IRQn, 0U, 0U);
exti_interrupt_flag_clear(EXTI_8);
__enable_irq();
delay_1ms(10U);
/** 进入深度睡眠,等待外部中断唤醒。 */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
}
/**
* @brief 处理 EXTI5_9 中断实际逻辑。
*
* @return 无。
*/
void BootSleepManager::onExtiInterrupt(void)
{
/**
* EXTI5_9 同时复用了两个来源:
* 1. PB8:深睡唤醒信号
* 2. PD7:SPI OTA 片选信号
*/
if (exti_interrupt_flag_get(EXTI_8) != RESET) {
exti_interrupt_flag_clear(EXTI_8);
exti_interrupt_disable(EXTI_8);
/** 唤醒后重新允许启动主应用。 */
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
/**
* 睡眠配置区按日志式写法追加状态:
* 从后往前找最后一个有效条目,再把 SLEEP_ENABLE 改写为 SLEEP_DISABLE。
*/
for (u32 address = SLEEP_CONFIG_END - SIZE_U32; address >= SLEEP_CONFIG_START; address -= SIZE_U32) {
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 sleep_check = *flash_ptr;
if (sleep_check == FLASH_EMPTY && address > SLEEP_CONFIG_START) {
continue;
} else if (sleep_check == SLEEP_ENABLE && address > SLEEP_CONFIG_START) {
/**
* 若最后一个可写位置已经用满,则整页擦除后从头开始写。
*/
if (address == SLEEP_CONFIG_END - SIZE_U32) {
fmc_unlock();
if (fmc_page_erase(SLEEP_CONFIG_START) != FMC_READY) {
fmc_lock();
return;
}
#if DEBUG_PRINT
printf("[FW] E page=0x%08lX\r\n", (unsigned long)SLEEP_CONFIG_START);
#endif
address = SLEEP_CONFIG_START - SIZE_U32;
}
(void)writeAddress(address + SIZE_U32, SLEEP_DISABLE);
break;
} else if (sleep_check == SLEEP_DISABLE) {
/** 已经记录为禁用睡眠,无需重复写。 */
break;
} else {
/** 配置区为空或数据异常时,直接当前位置补写禁用标记。 */
(void)writeAddress(address, SLEEP_DISABLE);
break;
}
}
/** 通过软复位重新进入正常启动流程。 */
system_soft_reset();
}
/* 注意:SPI OTA 的 CS 边沿信号 PA15 映射到 EXTI_15,属于 EXTI10_15_IRQn,
* 由 EXTI10_15_IRQHandler 直接分发到 SpiOta::onCsExtiInterrupt()。
* 本函数(EXTI5_9)只处理 PB8 深睡唤醒,不处理 SPI OTA CS 信号。 */
}
/**
* @brief 检查睡眠配置并决定是否进入深睡。
*
* @return 0 表示已有有效配置;1 表示写默认配置失败;2 表示已写入默认禁用配置。
*/
int BootSleepManager::check(void)
{
/**
* 启动时检查睡眠配置页中最新的有效状态:
* - SLEEP_ENABLE -> 直接进深睡
* - SLEEP_DISABLE -> 正常启动
* - 全空 -> 默认写入禁用状态
*/
for (u32 address = SLEEP_CONFIG_END - SIZE_U32; address >= SLEEP_CONFIG_START; address -= SIZE_U32) {
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 sleep_check = *flash_ptr;
if (sleep_check == FLASH_EMPTY) {
continue;
} else if (sleep_check == SLEEP_ENABLE) {
enterDeepSleep();
return 0;
} else if (sleep_check == SLEEP_DISABLE) {
return 0;
}
}
/** 首次运行或配置页为空:写入默认值 SLEEP_DISABLE。 */
if (writeAddress(SLEEP_CONFIG_START, SLEEP_DISABLE) != FMC_READY) {
return 1;
}
return 2;
}
/**
* @brief External interrupt handler for EXTI line group 5 to 9.
* @return none
*/
extern "C" void EXTI5_9_IRQHandler(void)
{
BootSleepManager::onExtiInterrupt();
}
/**
* @brief External interrupt handler for EXTI line group 10 to 15.
*
* PA15 (SPI CS from ESP32) maps to EXTI_15, which fires this handler.
* Must directly dispatch to the SPI OTA CS handler — NOT through
* BootSleepManager::onExtiInterrupt(), which only handles EXTI_8
* (PB8 deep-sleep wake) and knows nothing about EXTI_15.
*/
extern "C" void EXTI10_15_IRQHandler(void)
{
SpiOta::onCsExtiInterrupt();
}