RT-Thread论坛
直播中

岳臻俊

9年用户 1170经验值
私信 关注
[问答]

GD32F527添加以太网后程序仿真器烧录启动成功,手动上电卡死是怎么回事?

rt 5.2.2 版本,添加以太网功能后,仿真器烧录可以启动,但是手动上电启动失败,调试信息如下,
object->type = type | RT_Object_Class_Static;
这行代码执行卡死,这行上面的调试信息可以出来,这行下面的调试信息不出来,这行前面添加调试信息卡死位置不变。

发现卡死时,系统tick中断只进了一次,

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    rt_tick_increase();
    /* leave interrupt */
    rt_interrupt_leave();
#if 0
    static uint16_t cnt;
    if(++cnt > 5000) {
        cnt = 0;
        rt_kprintf("\n+\n");
    }
#endif
}
大神指导一下。

    /* leave critical */
    rt_spin_unlock_irqrestore(&(information->spinlock), level);
#endif /* RT_DEBUGING_ASSERT */
    rt_kprintf("%s %s L%d\n", __FILE__, __FUNCTION__, __LINE__);
    rt_kprintf("object:%p type:0x%x\n", object, type);
    rt_kprintf("%s %s L%d\n", __FILE__, __FUNCTION__, __LINE__);
    /* initialize object's parameters */
    /* set object type to static */
    object->type = type | RT_Object_Class_Static;
    rt_kprintf("%s %s L%d\n", __FILE__, __FUNCTION__, __LINE__);



../../../../src/scheduler_up.c rt_system_scheduler_start...
do components initialization.
initialize rti_board_end
:0 done
initialize rt_work_sys_workqueue_init
../../../../components/drivers/ipc/workqueue.c rt_work_sys_workqueue_init L474
../../../../components/drivers/ipc/workqueue.c rt_workqueue_create L221
../../../../components/drivers/ipc/workqueue.c rt_workqueue_create L224
../../../../components/drivers/ipc/workqueue.c rt_workqueue_create L227
../../../../components/drivers/ipc/workqueue.c rt_workqueue_create L230
../../../../components/drivers/ipc/workqueue.c rt_workqueue_create L232
../../../../src/ipc.c rt_sem_init L381
../../../../src/ipc.c rt_sem_init L385
../../../../src/ipc.c rt_sem_init L388
../../../../src/ipc.c rt_sem_init L391
../../../../src/object.c rt_object_init L364
../../../../src/object.c rt_object_init L376
../../../../src/object.c rt_object_init L380
../../../../src/object.c rt_object_init L397
../../../../src/object.c rt_object_init L404
object:0x20011444 type:0x2
../../../../src/object.c rt_object_init L408
仿真器烧录后正常信息如下:

thread     pri  status      sp     stack size max used left tick   error  tcb addr   usage
---------- ---  ------- ---------- ----------  ------  ---------- ------- ---------- -----
tshell      20  running 0x00000080 0x00001000    12%   0x00000009 OK      0x20004b68  N/A
tidle0      31  ready   0x00000068 0x00000100    40%   0x0000001d OK      0x20000ddc  N/A
timer        4  suspend 0x00000078 0x00000200    23%   0x00000009 EINTRPT 0x2000064c  N/A
main        10  suspend 0x00000068 0x00000800    23%   0x0000000d EINTRPT 0x20001c28  N/A
device             type         ref count
---------- -------------------- ----------
sdb        Block Device         1      
sda        Block Device         1      
norflash0  Block Device         0      
spi10      SPI Device           0      
spi1       SPI Bus              0      
pin        Pin Device           0      
uart4      Character Device     0      
uart3      Character Device     0      
uart0      Character Device     2

回帖(1)

倪山骋

2025-11-7 17:10:32

根据问题描述,使用GD32F527芯片,RT-Thread版本5.2.2,添加以太网功能后,通过仿真器烧录程序可以正常启动,但手动上电启动时卡死在`object->type = type | RT_Object_Class_Static;`这行代码。同时,系统tick中断只进入了一次。

首先,我们需要分析这个现象的可能原因:
1. **时钟初始化问题**:手动上电时,时钟可能没有正确初始化,导致系统运行速度异常。
2. **内存初始化问题**:手动上电时,内存(如SRAM)可能未完全准备好,导致数据访问错误。
3. **中断配置问题**:手动上电后,中断系统可能未正确配置,导致无法响应中断(如SysTick中断)。
4. **电源管理或复位问题**:手动上电的电源稳定性和复位电路可能影响启动。
5. **以太网驱动初始化问题**:添加以太网后,可能在初始化过程中有依赖关系或时序问题,导致手动上电时出现异常。

从调试信息看,卡死在对象初始化代码处,这通常是RT-Thread内核初始化阶段。具体位置在设置对象类型的地方,可能是在创建静态对象时。同时,SysTick中断只进入一次,说明系统时钟中断在第一次触发后,后续没有再次触发,可能是由于系统卡死导致中断无法继续,或者中断被禁用。

**可能的原因分析:**
- **时钟问题**:手动上电时,系统时钟(如HXTAL)可能未稳定,导致后续操作(包括以太网PHY初始化)失败。而仿真器下载时,由于之前已经有过上电,时钟可能已经稳定。
- **堆栈溢出**:手动上电时,堆栈可能设置不当,导致在初始化过程中栈溢出,破坏了关键数据。
- **中断优先级问题**:SysTick中断可能被其他中断阻塞,或者中断优先级配置有误。
- **硬件初始化顺序**:以太网模块的初始化可能依赖于某些外设(如时钟、GPIO)的初始化,而这些在手动上电时没有正确完成。

**解决思路:**
1. **检查时钟配置**:确保在系统启动时,时钟树配置正确,特别是外部高速晶振(HXTAL)的启动时间是否足够,可以通过增加时钟稳定延时来解决。
2. **检查复位原因**:在启动时读取复位标志,确定手动上电时的复位原因(如电源复位、外部复位等),并针对性地处理。
3. **检查以太网PHY的复位**:以太网PHY芯片可能需要一个复位信号,并且复位后需要等待一段时间。在代码中,确保PHY复位引脚的操作和延时足够。
4. **调整初始化顺序**:将一些关键外设的初始化提前,或者将以太网初始化放到系统完全启动之后(例如,在`main`线程中初始化)。
5. **调试手段**:在卡死前增加更多的调试输出,或者使用LED闪烁来指示程序执行到哪个阶段,从而定位卡死的位置。
6. **检查内存区域**:确认堆和栈的大小设置是否合理,特别是在添加以太网后,系统资源消耗增加,可能需要调整栈大小。

**具体步骤:**

### 1. 增加时钟稳定延时
在系统时钟配置之后,增加一段延时,确保外部晶振稳定。例如,在`system_gd32f5xx.c`中的`system_clock_config`函数中,在使能外部晶振后,增加一个循环等待外部晶振就绪,并适当增加延时。

```c
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or not */
while ((RCU_CTL & RCU_CTL_HXTALSTB) == 0)
{
}
// 增加额外的延时
for (volatile int i = 0; i < 10000; i++);
```

### 2. 检查以太网PHY复位
以太网PHY芯片通常有一个复位引脚,需要在初始化前进行复位操作,并保持一段时间的低电平,然后释放。在释放后,需要等待一段时间(例如100ms)让PHY芯片完成内部初始化。

在以太网驱动初始化代码中,确保复位操作正确且延时足够。例如:

```c
/* PHY reset */
rt_pin_write(ETH_PHY_RST_PIN, PIN_LOW);
rt_thread_mdelay(100);
rt_pin_write(ETH_PHY_RST_PIN, PIN_HIGH);
rt_thread_mdelay(100);  // 等待PHY芯片启动
```

### 3. 调整初始化顺序
尝试将以太网初始化放在系统完全启动之后,例如在`main`线程中初始化,而不是在启动阶段(如在`rtthread_startup`的`components_init`中)。这样可以避免在系统初始化关键阶段出现阻塞。

### 4. 检查堆栈大小
在`rtconfig.h`中,增加主栈大小(`HEAP`)和线程栈大小(特别是`main`线程栈)。添加以太网后,可能增加了栈的使用量。

### 5. 使用调试工具
- 在卡死的位置前添加更多的日志输出,定位具体卡死的位置。
- 使用JTAG/SWD调试器在手动上电后连接,查看寄存器和堆栈信息,分析卡死的原因。

### 6. 检查中断配置
在系统初始化时,确保中断优先级配置正确。SysTick中断优先级应该设置为最低,以避免被其他中断阻塞。在RT-Thread中,SysTick中断优先级在`rt_hw_board_init`中配置。

### 7. 电源和复位电路检查
检查硬件设计,确保手动上电时电源稳定,复位信号正常。可以尝试在复位引脚上增加外部上拉电阻,或者增加电源稳定延时。

**代码修改示例:**

针对时钟初始化延时,可以在`system_clock_config`中增加延时。同时,在以太网驱动中增加PHY复位的延时。

另外,在RT-Thread的启动代码中,可以在`rtthread_startup`函数的不同阶段添加日志,观察启动过程。

```c
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* board level initialization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();
    rt_kprintf("Board init done.n");

    /* show RT-Thread version */
    rt_show_version();
    rt_kprintf("RT-Thread version shown.n");

    /* timer system initialization */
    rt_system_timer_init();
    rt_kprintf("Timer init done.n");

    /* scheduler system initialization */
    rt_system_scheduler_init();
    rt_kprintf("Scheduler init done.n");

    /* create init_thread */
    rt_application_init();
    rt_kprintf("Application init done.n");

    /* timer thread initialization */
    rt_system_timer_thread_init();
    rt_kprintf("Timer thread init done.n");

    /* idle thread initialization */
    rt_thread_idle_init();
    rt_kprintf("Idle thread init done.n");

    /* start scheduler */
    rt_system_scheduler_start();
    rt_kprintf("Scheduler started.n");

    /* never reach here */
    return 0;
}
```

通过以上日志,可以定位到在哪一步初始化后卡死。

**总结:**
手动上电启动失败而仿真器烧录后启动成功,通常与硬件初始化时序、电源稳定性、复位电路有关。重点检查时钟初始化、以太网PHY复位、堆栈大小以及初始化顺序。通过添加调试日志和适当增加延时,逐步定位问题。

针对GD32F527在添加以太网功能后,仿真器烧录可启动但手动上电卡死的问题,结合系统卡在object->type = type | RT_Object_Class_Static;且SysTick中断仅触发一次的现象,以下是详细分析和解决方案:




根本原因分析




  1. 时钟初始化时序问题  



    • 手动上电时,外部高速晶振(HXTAL)未完全稳定,但系统已开始执行以太网初始化(依赖稳定时钟)。

    • 仿真器调试时因目标板已预先上电,晶振已稳定,故无此问题。




  2. 以太网PHY复位不充分  



    • PHY芯片复位后需要足够时间初始化(通常需100ms以上),手动上电时若未等待PHY就绪即访问寄存器,导致总线锁死或硬件异常。




  3. 中断冲突或优先级配置错误  



    • 以太网中断(如ETH)与SysTick中断优先级冲突,或中断使能过早导致关键初始化流程被中断打断。




  4. 内存访问异常  



    • 堆栈溢出或内存未初始化区域被访问(如以太网DMA描述符地址未对齐或越界)。






解决方案及步骤


1. 确保时钟稳定后再初始化以太网


rt_hw_board_init() 中增加外部晶振稳定延时:


// 在 system_clock_config() 后添加
void rt_hw_board_init()
{
    // ... 其他初始化
    system_clock_config(); // 配置系统时钟

    /* 增加HXTAL稳定延时 */
    volatile uint32_t delay = 1000000;
    while (delay--); // 简单延时循环

    /* 初始化以太网 */
    rt_hw_eth_init();
    // ...
}



2. 完善以太网PHY复位与检测


在PHY复位代码中增加硬件复位和状态检测:


void rt_hw_eth_init()
{
    /* 硬件复位PHY (假设RST引脚连接PE2) */
    rt_pin_mode(GET_PIN(E, 2), PIN_MODE_OUTPUT);
    rt_pin_write(GET_PIN(E, 2), PIN_LOW);
    rt_thread_mdelay(100); // 保持复位100ms
    rt_pin_write(GET_PIN(E, 2), PIN_HIGH);
    rt_thread_mdelay(200); // 等待PHY启动完成

    /* 检测PHY ID确保通信正常 */
    uint32_t phy_id = 0;
    for (int retry = 0; retry < 10; retry++) {
        phy_id = eth_device_phy_read(ETH_PHY_ADDR, PHY_ID1_REG);
        if (phy_id != 0xFFFF && phy_id != 0) break;
        rt_thread_mdelay(10);
    }
    if (phy_id == 0xFFFF || phy_id == 0) {
        rt_kprintf("ETH: PHY init failed!n");
        return;
    }
    // ... 后续初始化
}



3. 调整中断优先级


drv_eth.c 中配置以太网中断优先级低于SysTick:


void gd32_eth_init(void)
{
    // ... 以太网外设初始化

    /* 设置ETH中断优先级 (低于SysTick) */
    nvic_irq_enable(ETH_IRQn, 1, 0); // 优先级1 (数值越大优先级越低)

    /* 确保SysTick优先级最低 */
    NVIC_SetPriority(SysTick_IRQn, 0xF);
}



4. 检查内存与堆栈配置




  • 增大堆栈大小

    rtconfig.h 中调整:


    #define RT_MAIN_THREAD_STACK_SIZE 4096 // 原值若为2048则增大
    #define RT_THREAD_STACK_SIZE 1024      // 默认线程栈
    #define ETH_RXBUFNB 4                  // 减少接收缓冲区数量以节省内存



  • 确认DMA描述符地址对齐

    确保描述符位于非缓存区且64字节对齐:


    __attribute__((section(".noncache"), aligned(64))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB];





5. 关键排查手段




  • 调试日志定位

    在卡死代码前添加详细日志:


    rt_kprintf("Init eth, step1n");
    object->type = type | RT_Object_Class_Static; // 卡死行
    rt_kprintf("Init eth, step2n"); // 此句不会执行



  • 检查复位状态寄存器

    在启动时读取复位标志,确定复位原因:


    uint32_t reset_flags = RCU_RSTSCK;
    rt_kprintf("Reset flags: 0x%08Xn", reset_flags);





验证结果


完成上述修改后:



  1. 重新编译烧录程序。

  2. 断开仿真器,手动重启开发板。

  3. 观察串口日志:

    • 若看到 PHY ID: 0xXXXX 表示PHY初始化成功。

    • 系统应正常启动至命令行界面。


  4. 测试网络功能:
    ping 192.168.1.100  # 根据实际网络配置



提示:若仍卡死,使用JTAG在手动上电后立刻暂停CPU,检查 PC 指针位置和堆栈内容,定位异常指令。



通过以上步骤,可解决手动上电卡死问题,确保以太网功能稳定运行。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分