LPC1768 / ARM Cortex-M3微秒延迟

时间:2011-06-25 22:35:19

标签: gcc arm delay

我正试图在裸机臂环境(LPC1768)/ GCC中实现微秒延迟。我已经看过使用SysTimer生成中断的示例,然后在C中进行一些计数,用作时基

https://bitbucket.org/jpc/lpc1768/src/dea43fb213ff/main.c

然而,在12MHz系统时钟,我认为这不会很好地扩展到微秒延迟。基本上,处理器将花费所有时间来处理中断。

是否有可能在一个循环中查询SYSTICK_GetCurrentValue的值,并确定一旦滴答数超过计算出的数量,以微秒为单位的滴答数并保释出循环次数?

我宁愿不为此使用单独的硬件计时器(但是如果没有其他选择的话)

3 个答案:

答案 0 :(得分:4)

一种方法是使用循环来创建延迟,如下所示。您需要校准您的因子。更通用的方法是根据一些已知的时基计算启动时的因子。

#define CAL_FACTOR ( 100 )

void delay (uint32_t interval)
{
  uint32_t iterations = interval / CAL_FACTOR;

  for(int i=0; i<iterations; ++i)
  {
    __asm__ volatile // gcc-ish syntax, don't know what compiler is used
    (
      "nop\n\t"
      "nop\n\t"
      :::
    );
  }
}

答案 1 :(得分:3)

这类事情不需要先关闭中断,你可以轮询定时器,不需要过度使用中断。是的,为什么这些例子使用中断,但这并不意味着这是使用计时器的唯一方法。

Guy Sirton的答案是合理的,但我更喜欢汇编程序,因为我可以完全控制它到时钟周期(只要没有中断或其他项目妨碍)。定时器通常更容易,因为代码更便携(更改处理器时钟频率,你必须重新调整循环,使用定时器,有时您只需要更改init代码以使用不同的预分频器,或改变一行寻找计算的计数),并允许系统中的中断等。

在这种情况下,虽然你说的是12mhz,还有1微秒,那12条指令是吗?投入12个。或者分支到一些装配器,类似10个或8个,无论它出来是为了补偿两个分支上的管道冲洗。定时器和中断将在开销中烧掉超过12个指令周期。即使在循环中轮询计时器也会很邋..计数器循环也可以工作,你需要了解分支成本并调整它:

delay_one_ms:
mov r0,#3
wait:
sub r0,#1 @cortex-m3 means thumb/thumb2 and gas complains about subs.
bne wait
nop  @might need some nops to tune the loop accurately
nop
bx lr

使用gpio led或uart输出和秒表调用此功能,循环3000万次,并看到闪烁间隔30秒。

ldr r4,=uart_tx_register_address
mov r5,#0x55
again:
ldr r6,=24000000
str r5,[r4]
top:
bl delay_one_ms
sub r6,#1
bne top
str r5,[r4]
b again

实际上,因为我假设每个分支有2个时钟,测试循环有3个时钟,假设延迟总共12个时钟,每个循环15个时钟,30秒是30,000,000微秒,理想情况下是3000万个循环,但我需要12个/ 15日补偿的循环次数。如果您的示波器的时基有些准确,或者至少与您想要的延迟一样准确,那么这就容易多了。

我自己没有研究过ARM的分支成本,否则我会对此发表评论。它可能是两三个时钟。所以mov是一个,sub是循环次数的一倍,bne就是循环次数的两倍。两个分支到这里两个返回。 5+(3个*循环)+的NOP = 12。 (3 *循环)+ nops = 7循环是2,nops是1,是吗?我认为将多个nop串在一起要容易得多:

delay_one_ms:
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    bx lr

如果您使用中断,则可能需要再刻录一些临时禁用中断的指令。如果你正在寻找“至少”一微秒,那就不用担心了。

答案 2 :(得分:3)

您可以在ARM处理器内部使用SYSTICK。如果您有足够的时钟速度,只需对其进行编程以计算每个1uS或更少的数量,然后执行循环直到延迟值到期。 像这样:

void WaitUs(int us) {

    unsigned int cnt; 

    while(us-- >0) {
       cnt = STK_VAL; // get systick counter, ticking each 500nS
       while( (STK_VAL-cnt) < 2); // repeat till 2 ticks
    }
}

请记住,这是一个例子,你需要调整它以反击其他事情。