5种延迟代码执行的技术

嵌入式软件实现中经常会出现一个有趣的问题,即弄清楚如何延迟代码执行。有时,开发人员可能只希望有10微秒的延迟,以使I / O线在读取之前稳定下来,或者可能希望在两次读取之间指定的时间间隔使它反跳。在本文中,我们将探讨五种延迟代码执行的技术。

 

技术#1 –条件循环

第一种技术(可能是最常用和最简单的)是使用条件循环。有条件的循环延迟通常会使用for,while或do 而循环重复执行无操作(NOP)指令。例如:

for(int i = 0; i < 100000; i++)
{
   __NOP();
}

有条件的延迟在紧要关头可能会很有用,但很难准确或有效。如果开发人员要针对其他操作模式(例如低功耗操作)调整时钟频率,则延迟时间将完全不同。另外,总会有一个问题,那就是到底有多长时间?有人可能会认为这是100,000条指令,但是每次循环执行时,都会有其他指令检查循环变量并递增i。这些时序循环对于在任何生产代码中使用来说都是不可预测的。

 

技术#2 –使用计时器

可以使用的第二种技术是利用微控制器内置的硬件计时器。通常有几种不同的硬件计时器可用于跟踪系统时间,生成波形,捕获输入和通用。如果开发人员需要延迟(例如10微秒),则可以向硬件计时器加载表示10微秒的计数值。在这种情况下,计时器将设置为单次计时器。该代码将启动计时器并等待设置计时器溢出标志,这将指示时间已过。

该代码的抽象版本可能类似于以下内容:

Timer_Reload(DELAY_VALUE);

while(Timer_Expired() == false)
{
   __NOP();
}

这种技术比我们之前讨论的条件循环要强大得多。它还具有更高的便携性,并且可以在所需的延迟时间内更轻松地进行调整。实际上,可以在整个代码中重用该API,以允许单个计时器用于所需的任何数量的延迟。

 

技术#3 –使用系统刻度(HAL示例)

在某些情况下,可能没有专用的硬件计时器,或者不希望设置一键式计时器。在这种情况下,开发人员可以利用板载系统滴答声来创建延迟。即使是裸机系统,通常也具有作为系统时钟的后台计时器,以便从微控制器启动的那一刻起,软件就具有时间参考。通常,在典型系统中,将这些系统滴答声设置为每1或10毫秒发生一次。

系统通常使用一些API,这些API允许开发人员访问当前的系统刻度,例如SysTick_Get()。开发人员可以利用此延迟来创建类似于以下内容的延迟:

TimeStart = SysTick_Get();

do
{
   TimeNow = SysTick_Get();
   TimeDelta = TimeNow – TimeStart;
}while(TimeDelta < DelayTime);


开发人员只需要确保如果他们这样做,就不会陷入计算问题或其他潜在问题中,因此应该检查边界条件。

 

技术#4 –使用RTOS收益函数

在使用实时操作系统(RTOS)的更高级的系统中,开发人员可以利用内置的RTOS API调用来产生创建延迟的任务。例如,如果开发人员正在使用FreeRTOS,则在他们的任务中他们可以使用如下代码:

VTaskDelay(1);

 

此延迟功能将使任务产生一个RTOS滴答声的当前任务。取决于配置,RTOS滴答可以设置为1毫秒或10。使用这样的延迟机制可能会出现问题,因为任务将在该时间段内产生CPU,但不能保证一旦系统滴答时间到期,该任务将是最高优先级的任务!如果任务是准备运行的最高优先级任务,则该任务仅在延迟后立即运行,因此延迟时间可能会有些抖动。

 

技术#5 –使用RTOS对象

我们今天将讨论的最后一项技术是使用其他RTOS对象来延迟时间。如果您仔细查看自己喜欢的RTOS中的信号,互斥对象和队列等对象的API,您会注意到大多数等待等待的API调用也会包含延迟时间。此延迟时间也可导致应用程序延迟。

与RTOS对象相关的是,大多数RTOS都还包含软计时器。这些是基于软件的计时器,是从运行中的硬件计时器触发的。然后,可以将与技术#2和技术#3中显示的技术相似的技术与这些软计时器一起使用,以在代码执行中产生延迟。

 

结论

正如我们在今天的帖子中所看到的,想要延迟代码执行的开发人员可以使用几种不同的技术。使用的技术将取决于系统中可用的软件和硬件资源。然后,开发人员可以决定他们要使用的解决方案的复杂程度。归根结底,肯定有几种机制可以帮助将代码执行延迟一定的时间。

4 thoughts 上 “5种延迟代码执行的技术”

  1. 技术#1 –条件循环:“这些时序循环对于在任何生产代码中使用来说都是不可预测的。 ”

    究竟。编译器可能会(取决于-O级别)优化循环,因此总延迟将非常接近于零;-)。禁用所有编译器’在代码之前/之后通过#pragma进行的优化可能会派上用场。

    即使是简单的MCU也具有内置的缓存(1或2)级别,因此总体精度将低于假定的精度(取决于运行时间)。

    还有IRQ(有时’是一个统计过程,例如DMA,ADC,Systick等,甚至之前开始的与SD卡相关的转换)可能会同时出现,并且它们的处理时间将加到产生的总体延迟中。

    在MCU模型之间(即使在同一供应商/家族内)或编译器之间移动时,尤其需要通过示波器/探针检查功能。

    但是,当我们尚不知道或尚无有效的API时,它可能仍是新平台上led-blinker-demo的单行代码。

    1. 嗨,安德烈(Andrzej),你击败了我,我也劝阻延迟的使用。“if you’重新使用延迟,您可能做错了什么”. But when I’在开始新项目时,我总是为早期工作创建几个延迟函数(例如sysDelay_100us,sysDelay_1ms等),并使用示波器校准环路大小。您必须注意延迟循环值不要’由于编译器的优化(循环中的NOP通常会被删除,所以我在循环代码中增加了循环变量)或硬件的变化而导致的更改,但是现在您有了简单的测试函数来进行验证。用示波器测量代码从来没有什么坏处,因为我经常通过延迟功能学到一些意想不到的东西。

  2. In the MIPS / PIC32 world we have a core counter that runs at 1/2 the CPU clock speed. This counter can be read and used to make a delay down to very low levels of time. For instance 如果你r PIC32MZ clock is running at 200 MHz, the core counter will be ticking along at 100 MHz or 10 nS per tic. This counter is an excellent code profiler too since it has such fine time resolution. Too bad all MCU’s don’没有这种计数器…

  3. 带有“while” loop are horribly power inefficient, and a terrible idea 如果你 are trying to optimize for battery operation.
    而是将需要延迟的过程编写为状态机。当需要延迟时,设置一个计时器(或类似的东西)并退出状态。处理器可以进入睡眠模式或至少执行其他有用的操作。计时器触发时,它会调用状态机,然后状态机移至过程的下一步。假设您为每个状态使用明确的名称,例如,状态机非常易于调试并且几乎可以自我记录。‘WAIT_FOR_RELAY” or “WAIT_FOR_SERVO_COMPLETION”.

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *

该网站使用Akismet减少垃圾邮件。 了解如何处理您的评论数据.