Files
DCU_APP/GD32E508_DCU_APP/device/app_spi_command.cpp
T
cbb 771d34398f feat(core): OTA可用版本,添加看门狗功能并优化系统初始化日志
- 添加watchdog组件用于系统监控
- 在system_gd32e50x.c中增加USART0诊断输出,在IRC8M和PLL配置后
  发送"APP"和"CLK"标识
- 优化AppSpiCommand类中的日志处理机制,使用临界区保护
- 添加对全0xFF数据包的过滤处理,避免无效命令处理
- 将OTA重启延时从滴答计数改为毫秒级延时,提高响应速度
- 移除温度传感器相关文件,添加看门狗相关文件到项目配置
- 更新FWDGT API调用方式,使用新的配置函数
- 增加详细的启动日志输出,便于调试和追踪初始化过程
2026-05-30 20:03:31 +08:00

425 lines
14 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.
/**
* @file app_spi_command.cpp
* @brief APP 运行期 SPI 命令从机实现。
*
* 复用 BOOT 的 SpiOta 协议帧格式(SOF+TYPE+LEN+PAYLOAD+CRC),
* APP 只处理进入 Bootloader 命令(TYPE=0xB0),
* 收到 OTA 命令帧(TYPE=0x01/0x02/0x03)时回复 WAIT 帧告知
* ESP32 "当前不在 BOOT 模式"。
*
* 调试日志采用延迟输出:processCommand() 只写 s_logBuf + 置 s_logPending,
* 同时置位 TASK_SPI_LOG 唤醒主循环,poll() 中立刻 printf。
*/
#include "device/app_spi_command.h"
extern "C" {
#include "gd32e50x_exti.h"
}
#include "gd32e50x_gpio.h"
#include "gd32e50x_misc.h"
#include "gd32e50x_rcu.h"
#include "gd32e50x_spi.h"
#include "sys/config.h"
#include "sys/ota_boot_request.h"
#include "sys/task_manager.h"
#include "main.h"
#include <stdio.h>
#include <string.h>
/* ============================================================
* 协议常量(与 BOOT SpiOta 一致)
* ============================================================ */
static constexpr uint8_t kSof = 0xA5U;
static constexpr uint8_t kTypeBootReq = 0xB0U;
static constexpr uint8_t kTypeOtaReq = 0x01U;
static constexpr uint8_t kTypeAppPing = 0xA1U;
static constexpr uint8_t kTypeOtaData = 0x02U;
static constexpr uint8_t kTypeOtaCancel = 0x03U;
static constexpr uint8_t kRspAck = 0x80U;
static constexpr uint8_t kRspWait = 0x81U;
static constexpr uint8_t kRspError = 0x82U;
static constexpr uint16_t kFrameOverhead = 5U;
static constexpr uint16_t kRxBufSize = 128U;
static constexpr uint16_t kTxBufSize = 64U;
static constexpr uint16_t kLogBufSize = 128U;
static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
{
if (!data || len == 0U) {
return false;
}
for (uint8_t i = 0U; i < len; ++i) {
if (data[i] != value) {
return false;
}
}
return true;
}
/* ============================================================
* 静态成员
* ============================================================ */
volatile bool AppSpiCommand::s_csActive = false;
volatile uint8_t AppSpiCommand::s_rxLen = 0U;
volatile uint8_t AppSpiCommand::s_txLen = 0U;
volatile uint8_t AppSpiCommand::s_txPos = 0U;
volatile bool AppSpiCommand::s_logPending = false;
uint8_t AppSpiCommand::s_rxBuf[kRxBufSize] = {};
uint8_t AppSpiCommand::s_txBuf[kTxBufSize] = {};
char AppSpiCommand::s_logBuf[kLogBufSize] = {};
/* ============================================================
* CRC8 / 帧构建 / 帧解析(与 BOOT SpiOta 一致)
* ============================================================ */
static uint8_t spi_crc8(const uint8_t *data, uint16_t len)
{
uint8_t crc = 0x00U;
for (uint16_t i = 0U; i < len; ++i) {
crc ^= data[i];
}
return crc;
}
static bool spi_build_frame(uint8_t type, const uint8_t *payload, uint16_t payload_len,
uint8_t *out, uint16_t *out_len)
{
if (!out || !out_len) { return false; }
uint16_t total = (uint16_t)(kFrameOverhead + payload_len);
if (total > kTxBufSize) { return false; }
out[0] = kSof;
out[1] = type;
out[2] = (uint8_t)(payload_len & 0xFFU);
out[3] = (uint8_t)((payload_len >> 8) & 0xFFU);
if (payload && payload_len > 0U) {
memcpy(&out[4], payload, payload_len);
}
out[4 + payload_len] = spi_crc8(out, (uint16_t)(4U + payload_len));
*out_len = total;
return true;
}
static bool spi_try_parse_frame(const uint8_t *frame, uint16_t frame_len,
uint8_t *type, const uint8_t **payload, uint16_t *payload_len)
{
if (!frame || frame_len < kFrameOverhead) { return false; }
if (frame[0] != kSof) { return false; }
uint16_t len = (uint16_t)(frame[2] | ((uint16_t)frame[3] << 8));
uint16_t expected = (uint16_t)(kFrameOverhead + len);
if (expected != frame_len) { return false; }
if (spi_crc8(frame, (uint16_t)(4U + len)) != frame[4U + len]) { return false; }
if (type) { *type = frame[1]; }
if (payload) { *payload = &frame[4]; }
if (payload_len) { *payload_len = len; }
return true;
}
/* ============================================================
* 延迟日志:写入缓冲(ISR 安全,不调用 printf)
* ============================================================ */
void AppSpiCommand::logDeferred(const char *msg)
{
if (s_logPending) { return; }
snprintf(s_logBuf, kLogBufSize, "[SPI-APP] %s", msg);
s_logPending = true;
}
void AppSpiCommand::logDeferredFmt(const char *fmt, uint8_t val)
{
if (s_logPending) { return; }
snprintf(s_logBuf, kLogBufSize, fmt, val);
s_logPending = true;
}
/* ============================================================
* 硬件配置(与 BOOT 完全一致)
* ============================================================ */
void AppSpiCommand::configureGpio()
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_pin_remap_config(GPIO_SPI0_REMAP, ENABLE);
gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
}
void AppSpiCommand::configureSpi0()
{
rcu_periph_clock_enable(RCU_SPI0);
spi_i2s_deinit(SPI0);
spi_parameter_struct spi_init_struct;
spi_struct_para_init(&spi_init_struct);
spi_init_struct.device_mode = SPI_SLAVE;
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.prescale = SPI_PSC_8;
spi_init(SPI0, &spi_init_struct);
spi_nss_internal_low(SPI0);
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);
spi_enable(SPI0);
}
void AppSpiCommand::configureExti()
{
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15);
exti_init(EXTI_15, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
exti_interrupt_enable(EXTI_15);
exti_interrupt_flag_clear(EXTI_15);
nvic_irq_enable(EXTI10_15_IRQn, 1U, 1U);
}
/* ============================================================
* 初始化
* ============================================================ */
void AppSpiCommand::init()
{
s_csActive = false;
s_rxLen = 0U;
s_txLen = 0U;
s_txPos = 0U;
s_logPending = false;
memset(s_rxBuf, 0, sizeof(s_rxBuf));
memset(s_txBuf, 0xFF, sizeof(s_txBuf));
memset(s_logBuf, 0, sizeof(s_logBuf));
configureGpio();
configureSpi0();
configureExti();
}
/* ============================================================
* 帧处理:CS 释放后解析帧并准备回复(不调用 printf)
* ============================================================ */
void AppSpiCommand::processCommand()
{
if (s_rxLen == 0U) {
return;
}
/* 尝试按协议帧解析 */
uint8_t frame_type = 0U;
const uint8_t *payload = nullptr;
uint16_t payload_len = 0U;
if (spi_try_parse_frame(s_rxBuf, s_rxLen, &frame_type, &payload, &payload_len)) {
/* ★ 合法帧:按 TYPE 分发 */
uint16_t tx_len = 0U;
switch (frame_type) {
case kTypeBootReq:
/* 请求进入 Bootloader */
logDeferred("BOOT_REQ → 进BOOT");
{
const bool ok = OtaBootRequest::requestEnterBoot();
uint8_t ack = ok ? CONFIG_SPI_APP_OTA_BOOT_ACK_OK
: CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR;
spi_build_frame(kRspAck, &ack, 1U, s_txBuf, &tx_len);
if (ok) {
TaskManager::set(1UL << TASK_HEARTBEAT);
}
}
break;
case kTypeOtaReq:
case kTypeOtaData:
case kTypeOtaCancel:
/* OTA 命令帧 — APP 不处理,回复 WAIT */
logDeferredFmt("[SPI-APP] OTA type=0x%02X → WAIT", frame_type);
{
uint8_t hint = 0x01U;
spi_build_frame(kRspWait, &hint, 1U, s_txBuf, &tx_len);
}
break;
case kTypeAppPing:
/* APP 存活探测 — 回复 ACK 携带状态摘要 */
logDeferred("[SPI-APP] PING → APP_ALIVE");
{
/* 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)
};
spi_build_frame(kRspAck, alive_payload, sizeof(alive_payload), s_txBuf, &tx_len);
}
break;
default:
/* 未知帧类型 */
logDeferredFmt("[SPI-APP] 未知 type=0x%02X → ERROR", frame_type);
{
uint8_t err = 0xEEU;
spi_build_frame(kRspError, &err, 1U, s_txBuf, &tx_len);
}
break;
}
s_txLen = (uint8_t)tx_len;
s_txPos = 0U;
} else {
/* ★ 不符合协议帧 — 兼容旧逻辑 */
if (s_rxBuf[0] == CONFIG_SPI_APP_OTA_BOOT_CMD) {
/* 旧式单字节 0xB0 */
logDeferred("旧式0xB0 → 进BOOT");
const bool ok = OtaBootRequest::requestEnterBoot();
uint8_t ack = ok ? CONFIG_SPI_APP_OTA_BOOT_ACK_OK
: CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR;
s_txBuf[0] = CONFIG_SPI_APP_OTA_BOOT_CMD;
s_txBuf[1] = ack;
s_txBuf[2] = 0x4FU;
s_txBuf[3] = 0x54U;
s_txLen = 4U;
s_txPos = 0U;
if (ok) {
TaskManager::set(1UL << TASK_HEARTBEAT);
}
} else {
/* 无法识别 */
logDeferredFmt("[SPI-APP] 非协议 0x%02X → ERROR", s_rxBuf[0]);
uint16_t tx_len = 0U;
uint8_t err = 0xEEU;
spi_build_frame(kRspError, &err, 1U, s_txBuf, &tx_len);
s_txLen = (uint8_t)tx_len;
s_txPos = 0U;
}
}
/* 置位 TASK_SPI_LOG,唤醒主循环立刻打印日志 */
TaskManager::set(1UL << TASK_SPI_LOG);
}
/* ============================================================
* 主循环轮询:打印延迟日志
* ============================================================ */
void AppSpiCommand::poll()
{
char log_copy[kLogBufSize] = {};
bool has_log = false;
__disable_irq();
if (s_logPending) {
memcpy(log_copy, s_logBuf, sizeof(log_copy));
s_logPending = false;
has_log = true;
}
__enable_irq();
if (has_log) {
printf("%s\r\n", log_copy);
}
}
/* ============================================================
* CS 边沿处理
* ============================================================ */
void AppSpiCommand::onCsAssert()
{
s_csActive = true;
s_rxLen = 0U;
s_txPos = 0U;
if (s_txLen > 0U) {
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_TBE);
}
}
void AppSpiCommand::onCsRelease()
{
if (!s_csActive) {
return;
}
s_csActive = false;
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
if (isAllBytesValue(s_rxBuf, s_rxLen, 0xFFU)) {
s_rxLen = 0U;
return;
}
processCommand();
}
/* ============================================================
* SPI0 / EXTI 中断处理
* ============================================================ */
void AppSpiCommand::onInterrupt()
{
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RBNE) != RESET) {
if (!s_csActive && gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) {
onCsAssert();
}
uint8_t byte = (uint8_t)(spi_i2s_data_receive(SPI0) & 0xFFU);
if (s_csActive && s_rxLen < sizeof(s_rxBuf)) {
s_rxBuf[s_rxLen++] = byte;
}
}
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);
}
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
(void)spi_i2s_data_receive(SPI0);
s_rxLen = 0U;
}
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) {
spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR);
}
}
void AppSpiCommand::onCsExtiInterrupt()
{
if (exti_interrupt_flag_get(EXTI_15) != RESET) {
exti_interrupt_flag_clear(EXTI_15);
if (gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) {
onCsAssert();
} else {
onCsRelease();
}
}
}
extern "C" void SPI0_IRQHandler(void)
{
AppSpiCommand::onInterrupt();
}
extern "C" void EXTI10_15_IRQHandler(void)
{
AppSpiCommand::onCsExtiInterrupt();
}