我们如何计算嵌入式 C 中以下 for 循环给出的延迟?

How we can calculate the delay given by the following for loop in embedded C?

我试图将 LCD (NHD-0240AZ-FL-YBW) 连接到 TM4C123GH6PMI。这样做时,我应该以毫秒为单位进行延迟,所以我在 Google 上进行了搜索。一个人用下面的循环给出了以毫秒为单位的延迟。谁能解释一下它是如何工作的?

void DelayMilis(unsigned long ulMilliSeconds)
{
   unsigned long i = 0, j = 0;

   for (i = 0; i < ulMilliSeconds; i++)
   {
     for (j = 0; j < 2000; j++);
   }
}

您似乎在“这家伙”的平台上提到了一个从 0 计数到 1999 (for (j = 0; j < 2000; j++);) 的空 for 循环大约需要一毫秒。因此,如果您重复此 ulMilliSeconds 次,程序将执行 ulMilliSeconds 毫秒的延迟。

在您的平台上,这可能有所不同,因此您可能需要测量和调整内部 for 循环,如果您的平台比“这个人”的平台快两倍,您可能需要 for (j = 0; j < 4000; j++);

请注意:

for (j = 0; j < 2000; j++);

与此相同:

for (j = 0; j < 2000; j++)
{
   // do nothing
}

OTOH 这种创建延迟的方式可能会失败,因为编译器可能只是优化空循环。通常延迟是使用你的微控制器可能有的定时器来编程的。

恕我直言,“这家伙”在延迟实施方面做得很糟糕。

人们认为它是如何工作的:
由于嵌套循环,会浪费 2000 * ulMilliSeconds CPU 个循环,延迟下一次执行。

为什么它可能无法那样工作:
因为,编译器会感觉到嵌套循环没有做任何事情,它们很可能会被优化并且永远不会执行。如果你只是做一个 var = var 来愚弄编译器,情况仍然会保持不变。

我肯定您知道 TM4C123GH6PMI 是一个 32 位 ARM Cortex-M4F 微控制器,查看数据表有六个 16/32 位定时器模块和六个 32/64 位定时器模块,除了SysTick 计时器。

当我是新手时(我认为我是!),我会按以下方式实现延迟。

  • 初始化定时器
  • 设置一个标志and/or在定时器中断处理程序中增加一个计数器
  • 在适当的阶段检查应用程序中的flag/counter

如果我阻止执行,除非计数器达到所需的值,这是一个 delay-based 实现。如果我让其他应用程序代码在计数器未达到所需值时执行,则它是 time-out 实现。超时通常优于延迟,但可以根据要求更改。

使用您的解决方案,控制器大部分时间都在一个非生产性循环中,只会浪费 CPU 周期和能量。 更好的解决方案是通过 timer-interrupt 以频率 t/2(例如 5ms)驱动 LCD,将要写入的数据放入 ring-buffer 或类似的并在每个周期发送它们. 可以肯定的是,如果电路没有发出准备就绪的信号,请不要管它,并在下一个周期写入。 通过这种方法,cpu 可以用于计算,如果什么都不做,它可以简单地闲置。 顺便说一句:通常这种循环会被优化掉。

@Yunnosch:谢谢你的建议。我希望我的观点现在更 objective 更清楚了。

虽然这不是编写延迟循环的更好方法,但它肯定是设计起来最简单的方法,尤其是对于非常短的时间;另一方面,由于执行时间的不确定性,这些循环通常需要一些调整。时间取决于编译器、处理器中的管道和缓存的优化。

一些编译器,特别是为嵌入式世界设计的,可能有特殊的#pragmas 或其他特性来优化掉空循环。例如,某些编译器以特殊方式处理 NOP 指令(插入一些内部函数,如 __nop() 等);有时一个NOP也有一定的执行时间。