代码之家  ›  专栏  ›  技术社区  ›  Magix

__内置的avr_delay_cycles实现说明

  •  0
  • Magix  · 技术社区  · 7 年前

    _delay_ms(1000);
    

    它被转换为以下程序集:

    00000039  SER R18       Set Register 
    0000003A  LDI R24,0x69      Load immediate 
    0000003B  LDI R25,0x18      Load immediate 
    0000003C  SUBI R18,0x01     Subtract immediate 
    0000003D  SBCI R24,0x00     Subtract immediate with carry 
    0000003E  SBCI R25,0x00     Subtract immediate with carry 
    0000003F  BRNE PC-0x03      Branch if not equal 
    00000040  RJMP PC+0x0001        Relative jump 
    00000041  NOP       No operation 
    

    当这行:

    _delay_ms(500);
    

    编译为:

    00000039  SER R18       Set Register 
    0000003A  LDI R24,0x34      Load immediate 
    0000003B  LDI R25,0x0C      Load immediate 
    0000003C  SUBI R18,0x01     Subtract immediate 
    0000003D  SBCI R24,0x00     Subtract immediate with carry 
    0000003E  SBCI R25,0x00     Subtract immediate with carry 
    0000003F  BRNE PC-0x03      Branch if not equal 
    00000040  RJMP PC+0x0001        Relative jump 
    00000041  NOP       No operation 
    

    有人能解释一下生成的程序集背后的逻辑吗?内置开发人员如何确保 __builtin_avr_delay_cycles 功能?

    编辑:没有提到 #define F_CPU 8000000UL

    0 回复  |  直到 7 年前
        1
  •  6
  •   David Grayson    7 年前

    可在以下位置访问AVR指令集手册:

    http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf

    让我们看看你的代码 _delay_ms(1000);

    开头的ser、ldi和ldi指令执行3个周期,并将24位计数器设置为0x1869FF。

    然后我们有一个循环。此循环的每次迭代都会将24位计数器减少1,一旦计数器达到零,循环就终止。所以循环将有0x1869FF迭代。

    循环后的两条指令总共需要3个周期。

    3+0x1869FF*5-1+3=8000000

    编译器实现者的诀窍是选择循环要进行多少次迭代,循环之后要执行多少个RJMP,以及之后要执行多少个NOP。由于向循环中添加迭代在程序空间方面是免费的,所以您希望有尽可能多的循环迭代。由于一个rjmp占用的空间少于两个nop,所以您希望在循环之后有尽可能多的rjmp。然后你可能需要一个nop之后,使循环计数正确。

    对于较短的延迟,编译器可能使用8位或16位计数器。可能只是延后了,可能是短时间的。对于非常长的延迟,它可能使用超过24位的计数器。

    在某些情况下,可以通过在循环中添加几个nop来节省程序空间,因为这可能会使循环计数器变小,或者允许您在末尾删除多条指令。因此,一个真正聪明的实现应该考虑到这一点。