此前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,加快日志输出和调试效率。
247 lines
7.5 KiB
C++
247 lines
7.5 KiB
C++
#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();
|
||
}
|