diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h index 2bf1be6..e57c663 100644 --- a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/regions_GD32E508VE.h @@ -1,4 +1,4 @@ -#ifndef REGIONS_GD32E508VE_H +#ifndef REGIONS_GD32E508VE_H #define REGIONS_GD32E508VE_H @@ -17,7 +17,7 @@ #define __ROM0_BASE 0x08048000 // Region size [bytes] <0x0-0xFFFFFFFF:8> // Defines size of memory region. Default: 0x00080000 -#define __ROM0_SIZE 0x00036000 +#define __ROM0_SIZE 0x00035FFC // // __ROM1 (unused) diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s index 42956e4..d6f49d2 100644 --- a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/startup_gd32e50x_cl.s @@ -175,6 +175,48 @@ Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main + + ;=== EARLY DIAG: send 'R' via raw USART0 before any C code === + ; 使 GPIOA + USART0 时钟可用 + LDR R1, =0x40021018 ; RCU_APB2EN + LDR R2, [R1] + ORRS R2, R2, #(1 << 2) ; RCU_GPIOA + ORRS R2, R2, #(1 << 14) ; RCU_USART0 + STR R2, [R1] + ; PA9 设为 AF_PP 50MHz (CTL1 bits[7:4] = 0xB) + LDR R1, =0x40010804 ; GPIOA_CTL1 + LDR R2, [R1] + LDR R3, =0xFFFFFF0F ; mask: keep all except bits[7:4] + ANDS R2, R2, R3 + ORRS R2, R2, #0xB0 ; bits[7:4] = 0xB (AF_PP 50MHz) + STR R2, [R1] + ; 检测当前时钟源,选择正确波特率 + LDR R1, =0x40021000 ; RCU_CFG0 + LDR R2, [R1] + LSRS R2, R2, #2 ; SCSS bits [3:2] + ANDS R2, R2, #0x3 + CMP R2, #0x2 ; SCSS == PLL ? + LDR R3, =1562 ; 180MHz / 115200 + LDR R4, =69 ; 8MHz / 115200 + ITE EQ + MOVEQ R2, R3 ; PLL -> 180MHz baud divisor + MOVNE R2, R4 ; IRC8M -> 8MHz baud divisor + LDR R1, =0x40013808 ; USART0_BAUD + STR R2, [R1] + LDR R1, =0x40013800 ; USART0_CTL0 + MOVS R2, #(1 << 3) ; TE + ORRS R2, R2, #(1 << 13) ; | USARTEN + STR R2, [R1] + LDR R1, =0x40013804 ; USART0_DATA + MOVS R2, #'R' + STR R2, [R1] + LDR R1, =0x40013800 ; USART0_CTL0 +_rh_wait_tc + LDR R2, [R1] + LSRS R2, R2, #7 ; TC bit + BCC _rh_wait_tc + ;=== END EARLY DIAG === + LDR R0, =SystemInit BLX R0 LDR R0, =__main diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c index a8c94d2..f2c4030 100644 --- a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/system_gd32e50x.c @@ -162,7 +162,7 @@ static void _soft_delay_(uint32_t time) */ void SystemInit (void) { - /* ===== DIAG: raw USART0 output at IRC8M (8MHz) ===== */ + /* ===== DIAG: raw USART0 output — detect current clock for baudrate ===== */ /* Enable AFIO + GPIOA + USART0 clocks via raw register */ *((volatile uint32_t *)0x40021018U) |= (1U << 14) | (1U << 2) | (1U << 0); /* PA9 = AF_PP, 50MHz (CTL=0b11, MD=0b11) -> CTL1 bit[4..7]=0b1011 */ @@ -172,8 +172,18 @@ void SystemInit (void) ctl |= (0xBU << 4); /* AF_PP 50MHz */ *((volatile uint32_t *)0x40010804U) = ctl; } - /* USART0 baud: BAUD = 8000000/115200 = 69 */ - *((volatile uint32_t *)0x40013808U) = 69U; + /* Detect current system clock: if PLL is selected, assume 180MHz; otherwise IRC8M=8MHz. + * This handles both cold boot (IRC8M) and BOOT jump (PLL 180MHz). */ + { + uint32_t scss = (*((volatile uint32_t *)0x40021000U) >> 2) & 0x3U; /* RCU_CFG0 SCSS bits */ + uint32_t baud_div; + if (scss == 0x2U) { /* PLL selected as system clock -> 180MHz */ + baud_div = 180000000U / 115200U; /* = 1562 */ + } else { /* IRC8M or HXTAL -> 8MHz */ + baud_div = 8000000U / 115200U; /* = 69 */ + } + *((volatile uint32_t *)0x40013808U) = baud_div; + } /* USART0 CTL0: TE + EN = bit3 + bit13 */ *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); /* Send "APP" via USART0 DATA register */ @@ -647,8 +657,18 @@ static void system_clock_hxtal(void) /* if fail */ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ - while(1){ - } + /* HXTAL failed: output diagnostic via USART0 at IRC8M */ + *((volatile uint32_t *)0x40021018U) |= (1U << 2) | (1U << 0); + { volatile uint32_t _ctl = *((volatile uint32_t *)0x40010804U); + _ctl &= ~(0xFU << 4); _ctl |= (0xBU << 4); + *((volatile uint32_t *)0x40010804U) = _ctl; } + *((volatile uint32_t *)0x40013808U) = 69U; /* 8MHz/115200 */ + *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); + { const char *_msg = "HXTAL FAIL\r\n"; + for(int i=0; _msg[i]; i++){ + *((volatile uint32_t *)0x40013804U) = _msg[i]; + while((*((volatile uint32_t *)0x40013800U) & (1U << 7)) == 0U) {} } } + while(1){ } } /* AHB = SYSCLK */ @@ -693,8 +713,18 @@ static void system_clock_72m_hxtal(void) /* if fail */ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ - while(1){ - } + /* HXTAL failed: output diagnostic via USART0 at IRC8M */ + *((volatile uint32_t *)0x40021018U) |= (1U << 2) | (1U << 0); + { volatile uint32_t _ctl = *((volatile uint32_t *)0x40010804U); + _ctl &= ~(0xFU << 4); _ctl |= (0xBU << 4); + *((volatile uint32_t *)0x40010804U) = _ctl; } + *((volatile uint32_t *)0x40013808U) = 69U; /* 8MHz/115200 */ + *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); + { const char *_msg = "HXTAL FAIL\r\n"; + for(int i=0; _msg[i]; i++){ + *((volatile uint32_t *)0x40013804U) = _msg[i]; + while((*((volatile uint32_t *)0x40013800U) & (1U << 7)) == 0U) {} } } + while(1){ } } FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1); @@ -801,8 +831,18 @@ static void system_clock_120m_hxtal(void) /* if fail */ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ - while(1){ - } + /* HXTAL failed: output diagnostic via USART0 at IRC8M */ + *((volatile uint32_t *)0x40021018U) |= (1U << 2) | (1U << 0); + { volatile uint32_t _ctl = *((volatile uint32_t *)0x40010804U); + _ctl &= ~(0xFU << 4); _ctl |= (0xBU << 4); + *((volatile uint32_t *)0x40010804U) = _ctl; } + *((volatile uint32_t *)0x40013808U) = 69U; /* 8MHz/115200 */ + *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); + { const char *_msg = "HXTAL FAIL\r\n"; + for(int i=0; _msg[i]; i++){ + *((volatile uint32_t *)0x40013804U) = _msg[i]; + while((*((volatile uint32_t *)0x40013800U) & (1U << 7)) == 0U) {} } } + while(1){ } } FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3); @@ -910,8 +950,18 @@ static void system_clock_168m_hxtal(void) /* if fail */ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ - while(1){ - } + /* HXTAL failed: output diagnostic via USART0 at IRC8M */ + *((volatile uint32_t *)0x40021018U) |= (1U << 2) | (1U << 0); + { volatile uint32_t _ctl = *((volatile uint32_t *)0x40010804U); + _ctl &= ~(0xFU << 4); _ctl |= (0xBU << 4); + *((volatile uint32_t *)0x40010804U) = _ctl; } + *((volatile uint32_t *)0x40013808U) = 69U; /* 8MHz/115200 */ + *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); + { const char *_msg = "HXTAL FAIL\r\n"; + for(int i=0; _msg[i]; i++){ + *((volatile uint32_t *)0x40013804U) = _msg[i]; + while((*((volatile uint32_t *)0x40013800U) & (1U << 7)) == 0U) {} } } + while(1){ } } FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); @@ -1019,8 +1069,18 @@ static void system_clock_180m_hxtal(void) /* if fail */ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ - while(1){ - } + /* HXTAL failed: output diagnostic via USART0 at IRC8M */ + *((volatile uint32_t *)0x40021018U) |= (1U << 2) | (1U << 0); + { volatile uint32_t _ctl = *((volatile uint32_t *)0x40010804U); + _ctl &= ~(0xFU << 4); _ctl |= (0xBU << 4); + *((volatile uint32_t *)0x40010804U) = _ctl; } + *((volatile uint32_t *)0x40013808U) = 69U; /* 8MHz/115200 */ + *((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13); + { const char *_msg = "HXTAL FAIL\r\n"; + for(int i=0; _msg[i]; i++){ + *((volatile uint32_t *)0x40013804U) = _msg[i]; + while((*((volatile uint32_t *)0x40013800U) & (1U << 7)) == 0U) {} } } + while(1){ } } FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4); diff --git a/GD32E508_DCU_APP/device/app_spi_command.cpp b/GD32E508_DCU_APP/device/app_spi_command.cpp index 5454916..1761621 100644 --- a/GD32E508_DCU_APP/device/app_spi_command.cpp +++ b/GD32E508_DCU_APP/device/app_spi_command.cpp @@ -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 #include @@ -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); diff --git a/GD32E508_DCU_APP/device/app_spi_command.h b/GD32E508_DCU_APP/device/app_spi_command.h index 49c3ab6..ebe97b5 100644 --- a/GD32E508_DCU_APP/device/app_spi_command.h +++ b/GD32E508_DCU_APP/device/app_spi_command.h @@ -1,4 +1,4 @@ -#ifndef DEVICE_APP_SPI_COMMAND_H +#ifndef DEVICE_APP_SPI_COMMAND_H #define DEVICE_APP_SPI_COMMAND_H #include @@ -13,6 +13,9 @@ * * 调试日志采用延迟输出:processCommand() 只写缓冲+置标志, * poll() 中再 printf,避免在帧处理路径上耗时。 + * + * 发送路径使用 DMA0_CH2 推 s_txBuf → SPI0_DATA, + * 与 BOOT 的 SpiOta 实现保持一致;接收仍用 RBNE 字节中断。 */ class AppSpiCommand { public: @@ -44,6 +47,7 @@ private: static void configureGpio(); static void configureSpi0(); + static void configureDma(); static void configureExti(); static void onCsAssert(); static void onCsRelease(); diff --git a/GD32E508_DCU_APP/device/app_spi_command.h.bak b/GD32E508_DCU_APP/device/app_spi_command.h.bak new file mode 100644 index 0000000..6c80865 --- /dev/null +++ b/GD32E508_DCU_APP/device/app_spi_command.h.bak @@ -0,0 +1,55 @@ +#ifndef DEVICE_APP_SPI_COMMAND_H +#define DEVICE_APP_SPI_COMMAND_H + +#include + +/** + * @brief APP 运行期 SPI 命令从机模块。 + * + * 复用 BOOT 的 SpiOta 协议帧格式(SOF+TYPE+LEN+PAYLOAD+CRC): + * - TYPE=0xB0 → 请求进入 Bootloader,回复 ACK 帧 + * - TYPE=0x01/0x02/0x03(OTA 命令)→ 回复 WAIT 帧(不在 BOOT 模式) + * - 旧式单字节 0xB0 命令仍兼容 + * + * 调试日志采用延迟输出:processCommand() 只写缓冲+置标志, + * poll() 中再 printf,避免在帧处理路径上耗时。 + */ +class AppSpiCommand { +public: + /** @brief 初始化 SPI0 从机命令通道。 */ + static void init(); + + /** @brief 主循环轮询:打印延迟日志。 */ + static void poll(); + + /** @brief SPI0 中断处理入口。 */ + static void onInterrupt(); + + /** @brief CS 边沿中断处理入口。 */ + static void onCsExtiInterrupt(); + +private: + static constexpr uint16_t kRxBufSize = 128U; + static constexpr uint16_t kTxBufSize = 64U; + static constexpr uint16_t kLogBufSize = 128U; + + static volatile bool s_csActive; + static volatile uint8_t s_rxLen; + static volatile uint8_t s_txLen; + static volatile uint8_t s_txPos; + static volatile bool s_logPending; + static uint8_t s_rxBuf[kRxBufSize]; + static uint8_t s_txBuf[kTxBufSize]; + static char s_logBuf[kLogBufSize]; + + static void configureGpio(); + static void configureSpi0(); + static void configureExti(); + static void onCsAssert(); + static void onCsRelease(); + static void processCommand(); + static void logDeferred(const char *msg); + static void logDeferredFmt(const char *fmt, uint8_t val); +}; + +#endif /* DEVICE_APP_SPI_COMMAND_H */ diff --git a/GD32E508_DCU_APP/sys/config.h b/GD32E508_DCU_APP/sys/config.h index e7ec3d6..e2b273f 100644 --- a/GD32E508_DCU_APP/sys/config.h +++ b/GD32E508_DCU_APP/sys/config.h @@ -116,4 +116,30 @@ #define CONFIG_FWDGT_RLD 1874U /** @brief FWDGT 超时近似值(毫秒),仅供文档参考。 */ #define CONFIG_FWDGT_TIMEOUT_MS 3000U + +/* ============================================================ + * APP 固件版本号 + * ============================================================ */ +/** @brief APP 主版本号。 */ +#define APP_FW_VERSION_MAJOR 1U +/** @brief APP 次版本号。 */ +#define APP_FW_VERSION_MINOR 0U +/** @brief APP 补丁版本号。 */ +#define APP_FW_VERSION_PATCH 0U +/** @brief APP 构建编号。 */ +#define APP_FW_VERSION_BUILD 1U + +/* ============================================================ + * BOOT 版本在 OTA 配置页中的 Flash 存储地址 + * 与 BOOT 工程 sys/config.h 保持一致 + * ============================================================ */ +/** @brief OTA 配置页基地址。 */ +#define CONFIG_OTA_CONFIG_PAGE_ADDR 0x0807E000U +/** @brief BOOT 版本存储地址(word 6,低 32 位:major|minor)。 */ +#define CONFIG_BOOT_VERSION_ADDR0 (CONFIG_OTA_CONFIG_PAGE_ADDR + 0x18U) +/** @brief BOOT 版本存储地址(word 7,低 32 位:patch|build)。 */ +#define CONFIG_BOOT_VERSION_ADDR1 (CONFIG_OTA_CONFIG_PAGE_ADDR + 0x1CU) +/** @brief OTA 配置页总 word 数(6 个 OTA 标志 + 2 个 BOOT 版本)。 */ +#define CONFIG_OTA_CONFIG_WORD_COUNT 8U + #endif /* SYS_CONFIG_H */ diff --git a/GD32E508_DCU_APP/sys/hardfault.cpp b/GD32E508_DCU_APP/sys/hardfault.cpp new file mode 100644 index 0000000..20b32b1 --- /dev/null +++ b/GD32E508_DCU_APP/sys/hardfault.cpp @@ -0,0 +1,150 @@ +/** + * @file hardfault.cpp + * @brief HardFault 诊断处理——通过 USART0 输出故障寄存器信息。 + * + * 当 APP 在 BOOT 跳转后立即死机时,标准 printf 不可用, + * 所以使用直接寄存器操作输出。 + */ + +#include "gd32e50x.h" +#include + +/** + * @brief 通过 USART0 直接发送一个字节(纯寄存器操作,不依赖库函数/宏)。 + */ +static void rawPutc(char ch) { + volatile uint32_t *stat0 = (volatile uint32_t *)0x40013800U; + volatile uint32_t *data = (volatile uint32_t *)0x40013804U; + while ((*stat0 & (1U << 7)) == 0U) {} /* 等待 TBE */ + *data = (uint32_t)ch; +} + +/** + * @brief 通过 USART0 直接发送字符串。 + */ +static void rawPuts(const char *s) { + while (*s) { rawPutc(*s++); } +} + +/** + * @brief 输出 32 位十六进制值。 + */ +static void rawHex32(uint32_t val) { + const char hex[] = "0123456789ABCDEF"; + rawPuts("0x"); + for (int i = 28; i >= 0; i -= 4) { + rawPutc(hex[(val >> i) & 0xF]); + } +} + +/** + * @brief 检测当前实际 APB2 时钟频率(Hz)。 + * + * 通过读取 RCU_CFG0 的 SCSS / AHBPSC / APB2PSC 字段, + * 实时计算 USART0 所在的 APB2 总线时钟频率, + * 而不依赖可能已过时的 SystemCoreClock 全局变量。 + * + * @param stack_frame 指向异常栈帧的指针(由汇编桩压入)。 + * 包含: R0, R1, R2, R3, R12, LR, PC, xPSR + * @param lr_value EXC_RETURN 值,用于判断使用 MSP 还是 PSP。 + */ +static uint32_t detectApb2Clock(void) { + volatile uint32_t *rcu_cfg0 = (volatile uint32_t *)0x40021000U; + uint32_t cfg0 = *rcu_cfg0; + + /* 1. 确定 SYSCLK 频率 */ + uint32_t scss = (cfg0 >> 2) & 0x3U; + uint32_t sysclk; + if (scss == 0x2U) { + sysclk = 180000000U; /* PLL = 180MHz(唯一配置的 PLL 目标频率) */ + } else { + sysclk = 8000000U; /* IRC8M = 8MHz 或 HXTAL(未倍频时视为 8MHz) */ + } + + /* 2. AHB 预分频 */ + uint32_t ahbpsc = (cfg0 >> 4) & 0xFU; + uint32_t ahb_div; + switch (ahbpsc) { + case 0x8: ahb_div = 2; break; + case 0x9: ahb_div = 4; break; + case 0xA: ahb_div = 8; break; + case 0xB: ahb_div = 16; break; + case 0xC: ahb_div = 64; break; + case 0xD: ahb_div = 128; break; + case 0xE: ahb_div = 256; break; + case 0xF: ahb_div = 512; break; + default: ahb_div = 1; break; + } + uint32_t ahb_clk = sysclk / ahb_div; + + /* 3. APB2 预分频 */ + uint32_t apb2psc = (cfg0 >> 11) & 0x7U; + uint32_t apb2_div; + switch (apb2psc) { + case 0x4: apb2_div = 2; break; + case 0x5: apb2_div = 4; break; + case 0x6: apb2_div = 8; break; + case 0x7: apb2_div = 16; break; + default: apb2_div = 1; break; + } + + return ahb_clk / apb2_div; +} + +extern "C" void HardFault_Handler_C(uint32_t *stack_frame, uint32_t lr_value) { + (void)lr_value; + + /* 确保 USART0 时钟可用 */ + volatile uint32_t *rcu_apb2en = (volatile uint32_t *)0x40021018U; + *rcu_apb2en |= (1U << 2) | (1U << 14); /* RCU_GPIOA | RCU_USART0 */ + + /* 确保 PA9 处于 AF_PP 50MHz 模式(可能被其他代码覆盖) */ + volatile uint32_t *gpioa_ctl1 = (volatile uint32_t *)0x40010804U; + uint32_t ctl = *gpioa_ctl1; + ctl &= ~(0xFU << 4); + ctl |= (0xBU << 4); /* PA9: AF_PP 50MHz */ + *gpioa_ctl1 = ctl; + + /* 根据实际 APB2 时钟计算波特率分频值 */ + uint32_t apb2_clk = detectApb2Clock(); + uint32_t baud_div = apb2_clk / 115200U; + if (baud_div == 0U) baud_div = 1U; + + volatile uint32_t *usart_baud = (volatile uint32_t *)0x40013808U; + volatile uint32_t *usart_ctl0 = (volatile uint32_t *)0x40013800U; + *usart_baud = baud_div; + *usart_ctl0 = (1U << 3) | (1U << 13); /* TE | USARTEN */ + + rawPuts("\r\n!!! HardFault !!!\r\n"); + + if (stack_frame) { + uint32_t pc = stack_frame[6]; + uint32_t lr = stack_frame[5]; + uint32_t psr = stack_frame[7]; + rawPuts("PC="); rawHex32(pc); rawPuts("\r\n"); + rawPuts("LR="); rawHex32(lr); rawPuts("\r\n"); + rawPuts("PSR="); rawHex32(psr); rawPuts("\r\n"); + } + + rawPuts("CFSR="); rawHex32(SCB->CFSR); rawPuts("\r\n"); + rawPuts("HFSR="); rawHex32(SCB->HFSR); rawPuts("\r\n"); + rawPuts("MMFAR="); rawHex32(SCB->MMFAR); rawPuts("\r\n"); + rawPuts("BFAR="); rawHex32(SCB->BFAR); rawPuts("\r\n"); + + /* 死循环 */ + while (1) {} +} + +/** + * @brief HardFault_Handler 汇编桩——保存栈帧后调用 C 诊断函数。 + */ +__attribute__((naked)) void HardFault_Handler(void) { + __asm volatile( + "tst lr, #4 \n" /* 检查 EXC_RETURN 位 2 */ + "ite eq \n" + "mrseq r0, msp \n" /* 位 2 = 0: 使用 MSP */ + "mrsne r0, psp \n" /* 位 2 = 1: 使用 PSP */ + "mov r1, lr \n" /* 保存 EXC_RETURN */ + "b HardFault_Handler_C \n" + ); +} diff --git a/GD32E508_DCU_APP/sys/hardfault.h b/GD32E508_DCU_APP/sys/hardfault.h new file mode 100644 index 0000000..9166c22 --- /dev/null +++ b/GD32E508_DCU_APP/sys/hardfault.h @@ -0,0 +1,6 @@ +#ifndef HARDFAULT_H +#define HARDFAULT_H + +void HardFault_Diagnostic(void); + +#endif diff --git a/GD32E508_DCU_APP/sys/ota_boot_request.cpp b/GD32E508_DCU_APP/sys/ota_boot_request.cpp index 02fc4cd..c1a5d79 100644 --- a/GD32E508_DCU_APP/sys/ota_boot_request.cpp +++ b/GD32E508_DCU_APP/sys/ota_boot_request.cpp @@ -48,8 +48,8 @@ static bool otaConfigSet(uint32_t targetAddr, uint32_t value) return writeWord(targetAddr, value); } - uint32_t words[6]; - for(uint32_t i = 0U; i < 6U; ++i) { + uint32_t words[CONFIG_OTA_CONFIG_WORD_COUNT]; + for(uint32_t i = 0U; i < CONFIG_OTA_CONFIG_WORD_COUNT; ++i) { words[i] = *((volatile uint32_t *)(kOtaConfigPageAddr + i * 4U)); } words[(targetAddr - kOtaConfigPageAddr) / 4U] = value; @@ -63,7 +63,7 @@ static bool otaConfigSet(uint32_t targetAddr, uint32_t value) } bool ok = true; - for(uint32_t i = 0U; i < 6U; ++i) { + for(uint32_t i = 0U; i < CONFIG_OTA_CONFIG_WORD_COUNT; ++i) { if(words[i] == kOtaNotAval) { continue; }