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:
@@ -1,4 +1,4 @@
|
|||||||
#ifndef REGIONS_GD32E508VE_H
|
#ifndef REGIONS_GD32E508VE_H
|
||||||
#define REGIONS_GD32E508VE_H
|
#define REGIONS_GD32E508VE_H
|
||||||
|
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
#define __ROM0_BASE 0x08048000
|
#define __ROM0_BASE 0x08048000
|
||||||
// <o> Region size [bytes] <0x0-0xFFFFFFFF:8>
|
// <o> Region size [bytes] <0x0-0xFFFFFFFF:8>
|
||||||
// <i> Defines size of memory region. Default: 0x00080000
|
// <i> Defines size of memory region. Default: 0x00080000
|
||||||
#define __ROM0_SIZE 0x00036000
|
#define __ROM0_SIZE 0x00035FFC
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
// <h> __ROM1 (unused)
|
// <h> __ROM1 (unused)
|
||||||
|
|||||||
@@ -175,6 +175,48 @@ Reset_Handler PROC
|
|||||||
EXPORT Reset_Handler [WEAK]
|
EXPORT Reset_Handler [WEAK]
|
||||||
IMPORT SystemInit
|
IMPORT SystemInit
|
||||||
IMPORT __main
|
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
|
LDR R0, =SystemInit
|
||||||
BLX R0
|
BLX R0
|
||||||
LDR R0, =__main
|
LDR R0, =__main
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ static void _soft_delay_(uint32_t time)
|
|||||||
*/
|
*/
|
||||||
void SystemInit (void)
|
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 */
|
/* Enable AFIO + GPIOA + USART0 clocks via raw register */
|
||||||
*((volatile uint32_t *)0x40021018U) |= (1U << 14) | (1U << 2) | (1U << 0);
|
*((volatile uint32_t *)0x40021018U) |= (1U << 14) | (1U << 2) | (1U << 0);
|
||||||
/* PA9 = AF_PP, 50MHz (CTL=0b11, MD=0b11) -> CTL1 bit[4..7]=0b1011 */
|
/* 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 */
|
ctl |= (0xBU << 4); /* AF_PP 50MHz */
|
||||||
*((volatile uint32_t *)0x40010804U) = ctl;
|
*((volatile uint32_t *)0x40010804U) = ctl;
|
||||||
}
|
}
|
||||||
/* USART0 baud: BAUD = 8000000/115200 = 69 */
|
/* Detect current system clock: if PLL is selected, assume 180MHz; otherwise IRC8M=8MHz.
|
||||||
*((volatile uint32_t *)0x40013808U) = 69U;
|
* 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 */
|
/* USART0 CTL0: TE + EN = bit3 + bit13 */
|
||||||
*((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13);
|
*((volatile uint32_t *)0x40013800U) = (1U << 3) | (1U << 13);
|
||||||
/* Send "APP" via USART0 DATA register */
|
/* Send "APP" via USART0 DATA register */
|
||||||
@@ -647,8 +657,18 @@ static void system_clock_hxtal(void)
|
|||||||
|
|
||||||
/* if fail */
|
/* if fail */
|
||||||
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
|
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 */
|
/* AHB = SYSCLK */
|
||||||
@@ -693,8 +713,18 @@ static void system_clock_72m_hxtal(void)
|
|||||||
|
|
||||||
/* if fail */
|
/* if fail */
|
||||||
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
|
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);
|
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(1);
|
||||||
@@ -801,8 +831,18 @@ static void system_clock_120m_hxtal(void)
|
|||||||
|
|
||||||
/* if fail */
|
/* if fail */
|
||||||
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
|
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);
|
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(3);
|
||||||
@@ -910,8 +950,18 @@ static void system_clock_168m_hxtal(void)
|
|||||||
|
|
||||||
/* if fail */
|
/* if fail */
|
||||||
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
|
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);
|
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4);
|
||||||
@@ -1019,8 +1069,18 @@ static void system_clock_180m_hxtal(void)
|
|||||||
|
|
||||||
/* if fail */
|
/* if fail */
|
||||||
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
|
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);
|
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT(4);
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ extern "C" {
|
|||||||
#include "gd32e50x_gpio.h"
|
#include "gd32e50x_gpio.h"
|
||||||
#include "gd32e50x_misc.h"
|
#include "gd32e50x_misc.h"
|
||||||
#include "gd32e50x_rcu.h"
|
#include "gd32e50x_rcu.h"
|
||||||
|
#include "gd32e50x_dma.h"
|
||||||
#include "gd32e50x_spi.h"
|
#include "gd32e50x_spi.h"
|
||||||
#include "sys/config.h"
|
#include "sys/config.h"
|
||||||
#include "sys/ota_boot_request.h"
|
#include "sys/ota_boot_request.h"
|
||||||
#include "sys/task_manager.h"
|
#include "sys/task_manager.h"
|
||||||
|
#include "sys/systick.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -44,6 +46,19 @@ static constexpr uint16_t kRxBufSize = 128U;
|
|||||||
static constexpr uint16_t kTxBufSize = 64U;
|
static constexpr uint16_t kTxBufSize = 64U;
|
||||||
static constexpr uint16_t kLogBufSize = 128U;
|
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)
|
static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
|
||||||
{
|
{
|
||||||
if (!data || len == 0U) {
|
if (!data || len == 0U) {
|
||||||
@@ -57,6 +72,24 @@ static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
|
|||||||
return true;
|
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_init(SPI0, &spi_init_struct);
|
||||||
|
|
||||||
spi_nss_internal_low(SPI0);
|
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_RBNE);
|
||||||
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_ERR);
|
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_ERR);
|
||||||
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
|
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);
|
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()
|
void AppSpiCommand::configureExti()
|
||||||
{
|
{
|
||||||
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15);
|
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));
|
memset(s_logBuf, 0, sizeof(s_logBuf));
|
||||||
|
|
||||||
configureGpio();
|
configureGpio();
|
||||||
|
configureDma();
|
||||||
configureSpi0();
|
configureSpi0();
|
||||||
configureExti();
|
configureExti();
|
||||||
}
|
}
|
||||||
@@ -249,17 +306,47 @@ void AppSpiCommand::processCommand()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case kTypeAppPing:
|
case kTypeAppPing:
|
||||||
/* APP 存活探测 — 回复 ACK 携带状态摘要 */
|
/* APP 存活探测 — 回复 ACK 携带带标签的版本号
|
||||||
logDeferred("[SPI-APP] PING → APP_ALIVE");
|
*
|
||||||
|
* 因 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;
|
extern volatile uint32_t g_app_uptime_sec;
|
||||||
uint8_t alive_payload[4] = {
|
uint8_t alive_payload[19];
|
||||||
0xA1U,
|
memset(alive_payload, 0, sizeof(alive_payload));
|
||||||
(uint8_t)(g_app_uptime_sec & 0xFFU),
|
alive_payload[0] = 0xA1U; /* magic */
|
||||||
(uint8_t)((g_app_uptime_sec >> 8) & 0xFFU),
|
alive_payload[1] = 0x55U; /* APP version tag */
|
||||||
(uint8_t)(TaskManager::state().task_flags & 0xFFU)
|
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);
|
spi_build_frame(kRspAck, alive_payload, sizeof(alive_payload), s_txBuf, &tx_len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -335,11 +422,39 @@ void AppSpiCommand::poll()
|
|||||||
|
|
||||||
void AppSpiCommand::onCsAssert()
|
void AppSpiCommand::onCsAssert()
|
||||||
{
|
{
|
||||||
|
if (s_csActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
s_csActive = true;
|
s_csActive = true;
|
||||||
s_rxLen = 0U;
|
s_rxLen = 0U;
|
||||||
s_txPos = 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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
s_csActive = false;
|
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;
|
s_rxLen = 0U;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -373,23 +499,7 @@ void AppSpiCommand::onInterrupt()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_TBE) != RESET) {
|
/* TBE 已由 DMA 替代,不再处理 */
|
||||||
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) {
|
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
|
||||||
(void)spi_i2s_data_receive(SPI0);
|
(void)spi_i2s_data_receive(SPI0);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#ifndef DEVICE_APP_SPI_COMMAND_H
|
#ifndef DEVICE_APP_SPI_COMMAND_H
|
||||||
#define DEVICE_APP_SPI_COMMAND_H
|
#define DEVICE_APP_SPI_COMMAND_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -13,6 +13,9 @@
|
|||||||
*
|
*
|
||||||
* 调试日志采用延迟输出:processCommand() 只写缓冲+置标志,
|
* 调试日志采用延迟输出:processCommand() 只写缓冲+置标志,
|
||||||
* poll() 中再 printf,避免在帧处理路径上耗时。
|
* poll() 中再 printf,避免在帧处理路径上耗时。
|
||||||
|
*
|
||||||
|
* 发送路径使用 DMA0_CH2 推 s_txBuf → SPI0_DATA,
|
||||||
|
* 与 BOOT 的 SpiOta 实现保持一致;接收仍用 RBNE 字节中断。
|
||||||
*/
|
*/
|
||||||
class AppSpiCommand {
|
class AppSpiCommand {
|
||||||
public:
|
public:
|
||||||
@@ -44,6 +47,7 @@ private:
|
|||||||
|
|
||||||
static void configureGpio();
|
static void configureGpio();
|
||||||
static void configureSpi0();
|
static void configureSpi0();
|
||||||
|
static void configureDma();
|
||||||
static void configureExti();
|
static void configureExti();
|
||||||
static void onCsAssert();
|
static void onCsAssert();
|
||||||
static void onCsRelease();
|
static void onCsRelease();
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef DEVICE_APP_SPI_COMMAND_H
|
||||||
|
#define DEVICE_APP_SPI_COMMAND_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 */
|
||||||
@@ -116,4 +116,30 @@
|
|||||||
#define CONFIG_FWDGT_RLD 1874U
|
#define CONFIG_FWDGT_RLD 1874U
|
||||||
/** @brief FWDGT 超时近似值(毫秒),仅供文档参考。 */
|
/** @brief FWDGT 超时近似值(毫秒),仅供文档参考。 */
|
||||||
#define CONFIG_FWDGT_TIMEOUT_MS 3000U
|
#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 */
|
#endif /* SYS_CONFIG_H */
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* @file hardfault.cpp
|
||||||
|
* @brief HardFault 诊断处理——通过 USART0 输出故障寄存器信息。
|
||||||
|
*
|
||||||
|
* 当 APP 在 BOOT 跳转后立即死机时,标准 printf 不可用,
|
||||||
|
* 所以使用直接寄存器操作输出。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gd32e50x.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef HARDFAULT_H
|
||||||
|
#define HARDFAULT_H
|
||||||
|
|
||||||
|
void HardFault_Diagnostic(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -48,8 +48,8 @@ static bool otaConfigSet(uint32_t targetAddr, uint32_t value)
|
|||||||
return writeWord(targetAddr, value);
|
return writeWord(targetAddr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t words[6];
|
uint32_t words[CONFIG_OTA_CONFIG_WORD_COUNT];
|
||||||
for(uint32_t i = 0U; i < 6U; ++i) {
|
for(uint32_t i = 0U; i < CONFIG_OTA_CONFIG_WORD_COUNT; ++i) {
|
||||||
words[i] = *((volatile uint32_t *)(kOtaConfigPageAddr + i * 4U));
|
words[i] = *((volatile uint32_t *)(kOtaConfigPageAddr + i * 4U));
|
||||||
}
|
}
|
||||||
words[(targetAddr - kOtaConfigPageAddr) / 4U] = value;
|
words[(targetAddr - kOtaConfigPageAddr) / 4U] = value;
|
||||||
@@ -63,7 +63,7 @@ static bool otaConfigSet(uint32_t targetAddr, uint32_t value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ok = true;
|
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) {
|
if(words[i] == kOtaNotAval) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user