【问题标题】:Does this sound like a stack overflow?这听起来像堆栈溢出吗?
【发布时间】:2010-04-26 00:10:54
【问题描述】:

我认为我的嵌入式固件代码中可能存在堆栈溢出问题或类似问题。我是一名新程序员,从未处理过 SO,所以我不确定这是否正在发生。

固件通过一个轮子控制设备,该轮子周围均匀分布着磁铁,电路板上有一个霍尔效应传感器,当磁铁在其上方时,它会感应到。我的固件操作步进器并在监控磁传感器的同时计数步数,以检测车轮是否失速。

我在我的芯片上使用定时器中断(8 位,8057 acrh。)来设置输出端口来控制电机和失速检测。失速检测代码如下...

    //   Enter ISR
    //   Change the ports to the appropriate value for the next step
    //    ...

    StallDetector++;      // Increment the stall detector

    if(PosSensor != LastPosMagState)
    {
        StallDetector = 0;

        LastPosMagState = PosSensor;
    }
    else
    {
        if (PosSensor == ON) 
        {
            if (StallDetector > (MagnetSize + 10))
            {
                HandleStallEvent();
            }
        }
        else if (PosSensor == OFF) 
        {
            if (StallDetector > (GapSize + 10))
            {
                HandleStallEvent();
            }
        }
    }

每次触发 ISR 时都会调用此代码。 PosSensor 是磁传感器。 MagnetSize 是通过磁场所需的步进数。 GapSize 是两个磁铁之间的步数。所以我想检测车轮是否被传感器卡在磁铁上或没有卡在磁铁上。

这在很长一段时间内都很好用,但过了一会儿,第一个失速事件就会发生,因为“StallDetector > (MagnetSize + 10)”但是当我查看 StallDetector 的值时,它总是在 220 左右!这没有意义,因为 MagnetSize 总是在 35 左右。所以停止事件应该在 46 时触发,但不知何故它一直到 220?而且我没有在我的代码中的其他任何地方设置失速检测器的值。

您对我如何追查这个问题的根源有什么建议吗?

ISR 看起来像这样

void Timer3_ISR(void) interrupt 14
{
    OperateStepper();  // This is the function shown above
    TMR3CN &= ~0x80;   // Clear Timer3 interrupt flag        
}

HandleStallEvent 只是将一些变量设置回它们的默认值,以便它可以尝试另一个动作...

#pragma save
#pragma nooverlay
void HandleStallEvent()
{
///*
    PulseMotor = 0;                 //Stop the wheel from moving
    SetMotorPower(0);               //Set motor power low
    MotorSpeed = LOW_SPEED;
    SetSpeedHz();
    ERROR_STATE = 2;
    DEVICE_IS_HOMED = FALSE;
    DEVICE_IS_HOMING = FALSE;
    DEVICE_IS_MOVING = FALSE;
    HOMING_STATE = 0;
    MOVING_STATE = 0;
    CURRENT_POSITION = 0;
    StallDetector = 0;
    return;
//*/
}
#pragma restore

【问题讨论】:

    标签: c stack-overflow firmware


    【解决方案1】:

    PosSensor 是否易变?也就是说,你是在某处更新 PosSensor,还是直接读取一个 GPIO?

    我认为 GapSize 相当大(> 220?)在我看来,您可能有竞争条件。

    // PosSensor == OFF, LastPosMagState == OFF
        if(PosSensor != LastPosMagState)
        {
            StallDetector = 0;
    
            LastPosMagState = PosSensor;
        }
        else
        {
    // Race Condition: PosSensor turns ON here
    // while LastPosMagState still == OFF
            if (PosSensor == ON) 
            {
                if (StallDetector > (MagnetSize + 10))
                {
                    HandleStallEvent();
                }
            }
            else if (PosSensor == OFF) 
            {
                if (StallDetector > (GapSize + 10))
                {
                    HandleStallEvent();
                }
            }
        }
    

    您应该在执行 StallDetector++ 之后立即缓存一次 PosSensor 的值,以便在您的代码期间 PosSensor 发生变化时,您不会开始测试新值。

    【讨论】:

    • 看来这解决了问题。我仍然不确定 StallDetector 是如何达到 200+ 的,但我在这个函数的开头添加了一行代码来缓存 PosSensor 值,更改了几个变量名,现在它已经运行了超过 1500 次连续移动没有问题!
    • PosSensor 和 LastPosMagState 均处于关闭状态。这允许 StallDetector 接近该值 (GapSize + 10)。然后比赛条件发生; PosSensor 在 if(PosSensor != LastPosMagState) 之后打开,但在 if (PosSensor == ON) 之前打开,因此 StallDetector 不会在应该设置为 0 时设置为 0,从而允许将 GapSize 的 StallDetector 值与您的 MagnetSize 进行比较
    • 哦,有道理!谢谢你的经验。
    【解决方案2】:

    这绝对不是堆栈溢出。如果你破坏了堆栈(溢出它),你的应用程序就会崩溃。这听起来更像是我们在我的 C++ 时代曾经称之为内存踩踏的东西。您可能无法单独通过 StallDetector 变量访问 StallDetector 值占用的内存位置。您的代码的另一部分可能会错误地“踩踏”这个特定的内存位置。

    不幸的是,这类问题很难追查。您唯一能做的就是系统地隔离(从执行中删除)代码块,直到缩小范围并找到错误。

    【讨论】:

    • 是的,这就是我害怕的。
    【解决方案3】:

    您的系统上是否有嵌套 ISR?可能类似于启动 ISR 并增加计数,然后中断它并再次执行。这样做足够多次,您的中断堆栈可能会溢出。它也可以解释如此高的计数器变量。

    【讨论】:

      【解决方案4】:

      HandleStallEvent() 是在 ISR 中“查看”StallDetector 还是触发了主循环中的某些内容?如果它在主循环中,您是否清除了中断位?

      或者您是从 ISR 外部的调试器中查看 StallDetector 吗?然后,重新触发的中断每次都会使用正确的值,但执行次数过多,您只会看到最终的膨胀值。

      再想一想,您更有可能不必清除中断生成寄存器,而是中断引脚仍由传感器保持有效。您需要在第一次处理中断后忽略该中断,直到该行置低,例如通过让原始 ISR 自行禁用并将其重新安装到处理 1->0 转换的第二个 ISR 中。

      您可能还需要添加去抖动硬件或调整它(如果有)。

      【讨论】:

      • 我正在使用我的 IDE 调试器查看 StallDetector。我在 HandleStallEvent 上设置了一个断点,当它碰到它时,我会查看它
      • @Jordan: 堆栈溢出与否,如果StallDetector++ 是第一行并且你中断了它,那么每次你进入调试器时它应该加一个。如果没有,调试器不会向您展示整个故事。尝试在 ISR 之外进行调试,例如将 StallDetector 的值写入循环缓冲区和/或设置触发主循环断点的标志。
      • 它确实每次加一。我似乎无法在错误发生之前抓住它在做什么,因为在触发“失速”之前通常需要大约 5 分钟以正常速度运行。
      【解决方案5】:

      检查您的参数类型。如果您以与调用者期望的方式不同的方式定义参数,那么调用您的方法可能会覆盖存储变量的空间。(例如,如果您编写的函数需要一个 int 但它会将一个 long 推入堆栈。)

      【讨论】:

        【解决方案6】:

        您可以看到您的调试器支持哪些附加选项。例如,在 Visual Studio 中,可以设置“数据断点”,当内存位置发生变化(或设置为某个值,或高于阈值......)时,您会在该断点处中断。

        如果在您的情况下可能发生这样的事情,您可以看到数据更改的位置以及是否有其他人错误地写入内存。

        【讨论】:

          猜你喜欢
          • 2011-02-08
          • 2019-05-18
          • 1970-01-01
          • 1970-01-01
          • 2012-12-01
          • 2013-06-16
          • 2020-02-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多