feat(device): 添加早期诊断功能并优化SPI通信实现

- 在startup_gd32e50x_cl.s中添加USART0早期诊断代码,在Reset_Handler中
  发送'R'字符以确认启动过程

- 修改system_gd32e50x.c中的时钟初始化逻辑,支持动态检测当前时钟源
  (PLL或IRC8M)并相应调整串口波特率设置

- 在系统时钟配置函数中添加HXTAL故障诊断输出,当外部晶振启动失败时
  通过USART0输出"HXTAL FAIL"错误信息

- 在AppSpiCommand类中实现DMA传输机制,使用DMA0_CH2进行SPI0数据发送,
  提升传输效率并与BOOT端保持一致

- 重构SPI命令处理逻辑,优化PING命令响应格式,包含APP和BOOT版本信息
  以及运行时间统计

- 添加事务过滤机制,忽略全0xFF或全0x00的数据包以及短帧

- 更新配置文件定义APP固件版本号和OTA配置页面地址

- 修复region配置文件中的ROM大小数值精度问题

- 调整NVIC中断优先级设置,优化SPI中断处理
This commit is contained in:
cbb
2026-06-03 03:13:49 +08:00
parent 771d34398f
commit d0b6d9537e
10 changed files with 502 additions and 49 deletions
+140 -30
View File
@@ -19,10 +19,12 @@ extern "C" {
#include "gd32e50x_gpio.h"
#include "gd32e50x_misc.h"
#include "gd32e50x_rcu.h"
#include "gd32e50x_dma.h"
#include "gd32e50x_spi.h"
#include "sys/config.h"
#include "sys/ota_boot_request.h"
#include "sys/task_manager.h"
#include "sys/systick.h"
#include "main.h"
#include <stdio.h>
#include <string.h>
@@ -44,6 +46,19 @@ static constexpr uint16_t kRxBufSize = 128U;
static constexpr uint16_t kTxBufSize = 64U;
static constexpr uint16_t kLogBufSize = 128U;
/* DMA 配置(与 BOOT SpiOta 一致:DMA0_CH2 用于 SPI0 TX) */
#define SPI_TX_DMA_PERIPH DMA0
#define SPI_TX_DMA_CH DMA_CH2
static void startSpiTxDma(const uint8_t *buf, uint16_t len)
{
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
DMA_INTC(SPI_TX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_TX_DMA_CH);
DMA_CHMADDR(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = (uint32_t)buf;
DMA_CHCNT(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = len;
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) |= DMA_CHXCTL_CHEN;
}
static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
{
if (!data || len == 0U) {
@@ -57,6 +72,24 @@ static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
return true;
}
static bool shouldIgnoreTransaction(const uint8_t *data, uint8_t len)
{
if (!data || len == 0U) {
return true;
}
if (isAllBytesValue(data, len, 0xFFU) || isAllBytesValue(data, len, 0x00U)) {
return true;
}
/* APP 侧只接受两类事务:
* 1) 旧式单字节 0xB0
* 2) 完整协议帧(至少 5 字节)
* 短于 5 字节且不是 0xB0 的事务只能是启动切换期的残包,直接忽略。 */
return (len < kFrameOverhead) &&
!(len == 1U && data[0] == CONFIG_SPI_APP_OTA_BOOT_CMD);
}
/* ============================================================
* 静态成员
* ============================================================ */
@@ -168,13 +201,36 @@ void AppSpiCommand::configureSpi0()
spi_init(SPI0, &spi_init_struct);
spi_nss_internal_low(SPI0);
(void)spi_i2s_data_receive(SPI0);
spi_i2s_data_transmit(SPI0, 0xFFU);
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_RBNE);
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_ERR);
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
nvic_irq_enable(SPI0_IRQn, 1U, 0U);
nvic_irq_enable(SPI0_IRQn, 2U, 0U);
spi_enable(SPI0);
}
void AppSpiCommand::configureDma()
{
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
dma_parameter_struct dma_cfg;
dma_struct_para_init(&dma_cfg);
dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); /* SPI0_DATA 寄存器 */
dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_cfg.memory_addr = (uint32_t)s_txBuf;
dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_cfg.number = 0U;
dma_cfg.priority = DMA_PRIORITY_HIGH;
dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_cfg.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH, &dma_cfg);
dma_circulation_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
dma_channel_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
}
void AppSpiCommand::configureExti()
{
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15);
@@ -200,6 +256,7 @@ void AppSpiCommand::init()
memset(s_logBuf, 0, sizeof(s_logBuf));
configureGpio();
configureDma();
configureSpi0();
configureExti();
}
@@ -249,17 +306,47 @@ void AppSpiCommand::processCommand()
break;
case kTypeAppPing:
/* APP 存活探测 — 回复 ACK 携带状态摘要 */
logDeferred("[SPI-APP] PING → APP_ALIVE");
/* APP 存活探测 — 回复 ACK 携带带标签的版本号
*
* 因 SPI 从机首字节延迟导致帧偏移不稳定,
* 采用“标签+值”格式,ESP32 通过扫描标签定位字段。
*
* payload 布局 (19B):
* [0] = 0xA1 魔术字
* [1] = 0x55 APP 版本标签
* [2..5] = APP 版本 (4 x uint8: major, minor, patch, build)
* [6] = 0xAA BOOT 版本标签
* [7..10]= BOOT 版本 (4 x uint8: major, minor, patch, build)
* [11..14]= uptime (uint32_t LE)
* [15..18]= 填充 0 */
{
/* payload: [0]=0xA1(魔术) [1]=运行秒数低8位 [2]=运行秒数高8位 [3]=任务位图低8位 */
extern volatile uint32_t g_app_uptime_sec;
uint8_t alive_payload[4] = {
0xA1U,
(uint8_t)(g_app_uptime_sec & 0xFFU),
(uint8_t)((g_app_uptime_sec >> 8) & 0xFFU),
(uint8_t)(TaskManager::state().task_flags & 0xFFU)
};
uint8_t alive_payload[19];
memset(alive_payload, 0, sizeof(alive_payload));
alive_payload[0] = 0xA1U; /* magic */
alive_payload[1] = 0x55U; /* APP version tag */
alive_payload[2] = (uint8_t)APP_FW_VERSION_MAJOR;
alive_payload[3] = (uint8_t)APP_FW_VERSION_MINOR;
alive_payload[4] = (uint8_t)APP_FW_VERSION_PATCH;
alive_payload[5] = (uint8_t)APP_FW_VERSION_BUILD;
alive_payload[6] = 0xAAU; /* BOOT version tag */
/* BOOT 版本 (从 Flash OTA 配置页 word6/word7 读取) */
const volatile uint32_t *bv0 = (const volatile uint32_t *)CONFIG_BOOT_VERSION_ADDR0;
const volatile uint32_t *bv1 = (const volatile uint32_t *)CONFIG_BOOT_VERSION_ADDR1;
uint32_t bw0 = *bv0;
uint32_t bw1 = *bv1;
alive_payload[7] = (uint8_t)(bw0 & 0xFFU); /* boot major */
alive_payload[8] = (uint8_t)((bw0 >> 16) & 0xFFU); /* boot minor */
alive_payload[9] = (uint8_t)(bw1 & 0xFFU); /* boot patch */
alive_payload[10] = (uint8_t)((bw1 >> 16) & 0xFFU); /* boot build */
/* uptime (uint32_t LE) */
alive_payload[11] = (uint8_t)(g_app_uptime_sec & 0xFFU);
alive_payload[12] = (uint8_t)((g_app_uptime_sec >> 8) & 0xFFU);
alive_payload[13] = (uint8_t)((g_app_uptime_sec >> 16) & 0xFFU);
alive_payload[14] = (uint8_t)((g_app_uptime_sec >> 24) & 0xFFU);
spi_build_frame(kRspAck, alive_payload, sizeof(alive_payload), s_txBuf, &tx_len);
}
break;
@@ -335,11 +422,39 @@ void AppSpiCommand::poll()
void AppSpiCommand::onCsAssert()
{
if (s_csActive) {
return;
}
s_csActive = true;
s_rxLen = 0U;
s_txPos = 0U;
/* 停止 DMA 并清理旧状态 */
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
/* 与 BOOT 一致:清除 SPI 双缓冲中的残留数据 */
spi_i2s_data_transmit(SPI0, 0xFFU);
if (spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) != RESET) {
spi_i2s_data_transmit(SPI0, 0xFFU);
}
/* 清除错误标志 */
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
(void)spi_i2s_data_receive(SPI0);
}
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) {
spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR);
}
/* 启动 DMA 发送响应帧(与 BOOT 完全一致) */
if (s_txLen > 0U) {
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_TBE);
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
startSpiTxDma(s_txBuf, s_txLen);
} else {
/* 无响应时发送全 0xFF 填充 */
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
startSpiTxDma(s_txBuf, kTxBufSize);
}
}
@@ -349,8 +464,19 @@ void AppSpiCommand::onCsRelease()
return;
}
s_csActive = false;
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
if (isAllBytesValue(s_rxBuf, s_rxLen, 0xFFU)) {
/* 停止 DMA */
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RBNE) != RESET) {
uint8_t last_byte = (uint8_t)(spi_i2s_data_receive(SPI0) & 0xFFU);
if (s_rxLen < sizeof(s_rxBuf)) {
s_rxBuf[s_rxLen++] = last_byte;
}
}
if (shouldIgnoreTransaction(s_rxBuf, s_rxLen)) {
s_rxLen = 0U;
return;
}
@@ -373,23 +499,7 @@ void AppSpiCommand::onInterrupt()
}
}
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_TBE) != RESET) {
if (!s_csActive && gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) {
onCsAssert();
}
uint8_t tx_byte = 0xFFU;
if (s_csActive && s_txPos < s_txLen) {
tx_byte = s_txBuf[s_txPos++];
if (s_txPos >= s_txLen) {
s_txLen = 0U;
s_txPos = 0U;
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
}
} else {
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
}
spi_i2s_data_transmit(SPI0, tx_byte);
}
/* TBE 已由 DMA 替代,不再处理 */
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
(void)spi_i2s_data_receive(SPI0);