【问题标题】:Global variable doesn't update prior to next loop全局变量在下一个循环之前不更新
【发布时间】:2021-02-14 02:42:19
【问题描述】:

我正在尝试用 C++ 为我的 ESP32 构建一个转速计。当我在条件之外取消注释 Serial.printf("outside rev: %d \n", rev); 时,它可以工作,但是当我评论它时,我得到的值比它们应该的值大几个数量级(没有 700 转,有 7 转)。我最好的猜测是 print 语句正在减慢 loop() 的速度,足以让 incrementRevolutions() 在下一个循环之前将全局变量 passedMagnet 从 true 切换为 false。这是有道理的,因为延迟更新passedMagnet 将允许newRevCount++; 被多次触发。但鉴于竞态条件的时间敏感性,这显然是我无法使用打印语句或逐步调试来调试的。

bool passedMagnet = true;
int incrementRevolutions(int runningRevCount, bool passingMagnet)
{
//    Serial.printf("passedMagnet: %d ,  passingMagnet %d , runningRevCount: %d \n", passedMagnet, passingMagnet, runningRevCount);
    int newRevCount = runningRevCount;
    if (passedMagnet && passingMagnet)
    { //Started a new pass of the magnet
        passedMagnet = false;
        newRevCount++;
    }
    else if (!passedMagnet && !passingMagnet)
    { //The new pass of the magnet is complete
        passedMagnet = true;
    }
    return newRevCount;
}

unsigned long elapsedTime = 0;
unsigned long intervalTime = 0;
int rev = 0;
void loop()
{
    intervalTime = millis() - elapsedTime;
    rev = incrementRevolutions(rev, digitalRead(digitalPin));

//    Serial.printf("outside rev: %d \n", rev);
    if (intervalTime > 1000)
    {
        Serial.printf("rev: %d \n", rev);
        rev = 0;
        elapsedTime = millis();
    }
}

这是 Arduino 或 C++ 编程的一个已知问题吗?我该怎么做才能解决它?

【问题讨论】:

  • 是的,这是一个带有“C 编程”的已知“陷阱”,特别是:未定义的行为。代码中的某个地方存在一个您没有显示的错误,它最终会破坏内存,它表现为随机行为,当添加像 print 语句这样无辜的东西时,这种行为会发生变化。您需要在代码中未显示的某处找到错误并修复它。
  • 完整的代码库没有更多内容。它可以在这里找到:github.com/jamesjmtaylor/esp32-ftms-server 你对我如何找到这个错误有什么建议吗?有没有推荐的分析器或其他调试工具?
  • 为什么不使用 micros() 对其进行分析?看看有没有Serial.printf() 需要多长时间,你应该知道这是否是瓶颈
  • 使用 micros() 我发现循环用了 33334 微秒,Serial.printf() 和 17708 没有,所以几乎是两倍长。
  • 您使用的语言不是 C。它是与自定义标准库结合使用的 C++ 编译器

标签: c++ arduino esp32 arduino-ide arduino-c++


【解决方案1】:

我认为应该归咎于测试。我不得不重命名并移动一些东西以可视化逻辑,对此感到抱歉。

bool magStateOld = false;  // initialize to digitalRead(digitalPin) in setup()

int incrementRevolutions(int runningRevCount, bool magState)
{
    int newRevCount = runningRevCount;

    // detect positive edge.
    if (magState && !magStateOld)      // <- was eq. to if (magState && magStateOld)
                                       // the large counts came from here.  
    { 
        newRevCount++;
    }
    magStateOld = magState; // record last state unconditionally

    return newRevCount;
}

你也可以写成...

int incrementRevolutions(int n, bool magState)
{
    n += (magState && !magStateOld);
    magStateOld = magState;
    return n;
}

但是做你想做的最经济(和最快)的方法是:

bool magStateOld;

inline bool positiveEdge(bool state, bool& oldState)
{
    bool result = (state && !oldState);
    oldState = state;
    return result;
}  

void setup()
{
  // ...

  magStateOld = digitalRead(digitalPin);
}

void loop()
{
    // ...

    rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);

    // ...
}

它是可重用的,并且节省了堆栈空间和不必要的分配。

如果您无法从传感器获得干净的转换(正边缘和负边缘的噪声,则需要使用计时器稍微消除信号抖动。

例子:

constexpr byte debounce_delay = 50; // ms, you may want to play with
                                    // this value, smaller is better.
                                    // but must be high enough to 
                                    // avoid issues on expected
                                    // RPM range. 
                                    // 50 ms is on the high side.
    
byte debounce_timestamp;            // byte is large enough for delays 
                                    // up to 255ms. 

// ...

void loop()
{
    // ...

    byte now = (byte)millis();

    if (now - debounce_timestamp >= debounce_delay)
    {
        debounce_timestamp = now;
        rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);
    }

    // ...
}

【讨论】:

  • 我实施了您的第一个建议,并得到了与我之前的类似的日志记录语句:rev: 796 , instantaneousRpm: 7016.470588235 。这每秒发生一次。
  • @JamesJordanTaylor 你在测量什么?手裂,还是马达?别忘了!在测试中
  • @JamesJordanTaylor 也许你的传感器太敏感了。用手在传感器前面慢慢挥动磁铁,您的预期读数至少应该接近 1。这就是为什么我怀疑它可能是一个电气问题。您使用的传感器的部件号是什么?我会调查的。
  • 好的。它应该工作。你有示波器还是电压表?最好是一个范围,但首先要做的是:我想你的输入连接到 D0,而不是 A0。您可以调整传感器的灵敏度。如果您可以安装某种设备,使 magent 与传感器保持可重复的距离,那将是最好的选择。你可以把它粘在一根棍子上,找一些东西可以在你转动它的时候把它固定在适当的位置,最好是一些 Meccano 或乐高 pafts,一个转台,任何可以转动或滑动的东西,并且可以使磁铁保持可重复的距离形成传感器。
  • 磁铁不要用太大,越小越好。然后您可以通过将磁铁放置在传感器正下方(或上方)来调整模块,距离传感器大约 3 到 5 毫米。您可以通过调整 VR1 来调整传感器。理想情况下,当磁铁离传感器最近时,您需要 +5V 信号,而当它远离该位置时,尽快没有信号。您将不得不再次调整您的最终装配。制作一个坚固的装备,因为您将在项目期间使用它。如果您有示波器,来自 D0 的信号应该显示干净、快速的转换。当磁铁通过时
猜你喜欢
  • 2021-12-10
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-14
  • 2016-10-26
相关资源
最近更新 更多