You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32S3裸机下setjmp/longjmp时间片调度中断失效问题求助

问题描述

我用C语言实现了一个轮询调度器,循环调度3个任务,希望通过时间片机制实现:当任务运行时长超过预设超时值x时,将其中断并切换至下一个任务。

调度器循环中,启动任务前调用setjmp()保存上下文:

while(1) {
    int jump = setjmp(jmpbuf);
    if (jump == 1) {
        printf("TASK TOOK TOO LONG, SCHEDULING NEXT ONE INSTEAD.\n");
    } else {
        taskRun(sched->current);
    }
    sched->current = sched->current->next;
}

使用ESP32S3的定时器组0设置闹钟,触发中断后执行中断处理函数my_isr,该函数重置中断和闹钟寄存器,并调用longjmp跳转至之前保存的上下文:

void my_isr() {
    printf("Panic!\n");

    TIMG_INT_CLR_TIMERS_REG |= BIT(0);

    reset(); // 重置定时器组0的寄存器并重新启用闹钟

    longjmp(jmpbuf, 1);
}

首次任务超时后,闹钟触发且中断正常执行,任务成功切换。但后续闹钟再次触发(已通过闹钟位翻转确认)时,中断却不再触发。修改my_isr仅调用reset()并返回(回到中断前的任务)时,后续中断能正常触发,因此怀疑longjmp是问题根源,但也可能是其他原因。必须使用longjmp,求解决思路。

解决思路
  • 恢复全局中断状态
    ESP32进入ISR时会自动禁用同级或低优先级中断,longjmp跳转回任务上下文后不会自动恢复中断使能。需在调度器循环中手动启用全局中断,确保后续中断能正常触发:

    while(1) {
        int jump = setjmp(jmpbuf);
        __enable_irq(); // 手动启用全局中断
        if (jump == 1) {
            printf("TASK TOOK TOO LONG, SCHEDULING NEXT ONE INSTEAD.\n");
        } else {
            taskRun(sched->current);
        }
        sched->current = sched->current->next;
    }
    
  • 完善定时器重置逻辑
    你的reset()函数可能仅清除了中断标志,未重新加载定时器超时值或完整启用闹钟。需重置定时器的计数周期并确保中断使能位正确设置:

    void reset() {
        // 停止定时器
        TIMG_T0_CTRL_REG &= ~TIMG_T0_EN;
        // 重新加载预设超时值对应的定时器计数(需根据时钟频率转换)
        uint32_t timeout_count = x; // 替换为实际计算的计数
        TIMG_T0_LOAD_LO_REG = timeout_count & 0xFFFF;
        TIMG_T0_LOAD_HI_REG = timeout_count >> 16;
        // 清除中断标志
        TIMG_INT_CLR_TIMERS_REG |= BIT(0);
        // 重新启用定时器和闹钟中断
        TIMG_T0_CTRL_REG |= TIMG_T0_EN | TIMG_T0_ALARM_EN;
        TIMG_INT_ENA_TIMERS_REG |= BIT(0);
    }
    

    同时将reset()调用移至调度器循环的任务启动前,确保每次任务运行前定时器都处于正确配置状态:

    while(1) {
        int jump = setjmp(jmpbuf);
        __enable_irq();
        if (jump == 1) {
            printf("TASK TOOK TOO LONG, SCHEDULING NEXT ONE INSTEAD.\n");
        } else {
            reset(); // 任务启动前重新配置定时器
            taskRun(sched->current);
        }
        sched->current = sched->current->next;
    }
    
  • 移除ISR中的不安全操作
    ESP32的printf依赖任务上下文和锁机制,在ISR中调用可能导致栈混乱或状态异常,干扰后续中断触发。建议替换为ISR安全的日志输出(如esp_log_write),或暂时注释掉printf测试。

  • 确认jmpbuf的作用域
    确保jmpbuf是全局变量或静态变量,避免因栈帧销毁导致上下文失效。如果是局部变量,longjmp后栈帧已被覆盖,会导致后续上下文保存/恢复出错。

  • 检查中断优先级配置
    若定时器中断优先级设置过高,可能与ESP32的上下文切换机制冲突。确认定时器中断优先级未超过系统临界优先级,且未禁用其他必要中断源。

内容的提问来源于stack exchange,提问作者UserBoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:30:23