Gcc LPC1768/ARM Cortex-M3微秒延迟

Gcc LPC1768/ARM Cortex-M3微秒延迟,gcc,arm,delay,Gcc,Arm,Delay,我试图在裸机arm环境(LPC1768)/GCC中实现微秒延迟。我看过一些例子,这些例子使用SysTimer生成一个中断,然后用C进行一些计数,C用作时基 然而,在12MHz的系统时钟下,我认为这种延迟不会很好地扩展到微秒级。基本上,处理器将花费所有的时间为中断服务 是否可以查询循环中SYSTICK_GetCurrentValue的值,并确定一微秒内有多少滴答声,一旦滴答声的数量超过计算的数量,就退出循环 我不想为此使用单独的硬件计时器(但如果没有其他选择,我会这样做)一种方法就是使用循环来创

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

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

是否可以查询循环中SYSTICK_GetCurrentValue的值,并确定一微秒内有多少滴答声,一旦滴答声的数量超过计算的数量,就退出循环


我不想为此使用单独的硬件计时器(但如果没有其他选择,我会这样做)

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

#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"
      :::
    );
  }
}
定义校准系数(100) 无效延迟(uint32\t间隔) { uint32\u t迭代次数=间隔/校准系数;
对于(int i=0;i来说,一种方法就是使用一个循环来创建延迟,如下所示。您需要校准您的因数。更通用的方法是在启动时根据一些已知的时基计算因数

#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"
      :::
    );
  }
}
定义校准系数(100) 无效延迟(uint32\t间隔) { uint32\u t迭代次数=间隔/校准系数;
对于(int i=0;i这类事情不需要先中断,你可以轮询计时器,不需要用中断过度杀戮。是的,这些示例使用中断的原因是,但这并不意味着这是使用计时器的唯一方法

Guy Sirton的答案是合理的,但我更喜欢汇编程序,因为我可以精确地控制时钟周期(只要没有中断或其他阻碍)。计时器通常更容易,因为代码更易于移植(更改处理器时钟频率,您必须使用计时器重新调整循环,有时您所要做的就是更改init代码以使用不同的预分频器,或者更改查找计算计数的一行),并允许系统中出现中断等情况

在这种情况下,虽然你说的是12mhz,1微秒,也就是12条指令,是吗?输入12个NOP。或者用10个NOP或8个NOP分支到某个汇编器,不管结果如何,以补偿两个分支上的管道刷新。一个定时器和中断将消耗超过12个指令周期的开销。甚至polli在循环中取消计时器将是草率的。计数器循环也会起作用,但您需要了解分支成本并为此进行调整:

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秒是30000000微秒,理想情况下是3000万个环路,但我需要12/15个环路来补偿。如果你有一个时基比较精确的示波器,这要容易得多,或至少与您希望的延迟一样准确

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

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

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

这类事情不需要先中断,你可以轮询计时器,不需要用中断过多。是的,这些示例使用中断的原因是肯定的,但这并不意味着这是使用计时器的唯一方法

Guy Sirton的答案是合理的,但我更喜欢汇编程序,因为我可以精确地控制时钟周期(只要没有中断或其他阻碍)。计时器通常更容易,因为代码更易于移植(更改处理器时钟频率,您必须使用计时器重新调整循环,有时您所要做的就是更改init代码以使用不同的预分频器,或者更改查找计算计数的一行),并允许系统中出现中断等情况

在这种情况下,虽然你说的是12mhz,1微秒,也就是12条指令,是吗?输入12个NOP。或者用10个NOP或8个NOP分支到某个汇编器,不管结果如何,以补偿两个分支上的管道刷新。一个定时器和中断将消耗超过12个指令周期的开销。甚至polli在循环中取消计时器将是草率的。计数器循环也会起作用,但您需要了解分支成本并为此进行调整:

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秒是30000000微秒,理想情况下是3000万个环路,但我需要12/15个环路来补偿。如果你有一个时基比较精确的示波器,这要容易得多,或至少与您希望的延迟一样准确

我自己没有研究过ARM的分支成本,否则我会对此进行评论。它可能是两个或三个时钟。所以mov是一个,sub是循环数的一倍,bne是循环数的两倍。两个分支到这里,两个用于返回