c - __builtin_avr_delay_cycles 实现说明

标签 c assembly embedded delay avr

让我们考虑一下为 atmega32 编译的这个简单的 C 代码:

_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!

最佳答案

可在此处访问 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 次迭代。

循环的大多数迭代需要 5 个周期,因为 brne 在分支时需要两个周期。在循环的最后一次迭代中,brne 没有分支,因此最后一次迭代只需要 4 个循环。

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

将所有内容相加,我们看到总循环次数为:

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

由于您的 CPU 速度为 8 MHz,因此产生的延迟为 1 秒。

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

对于更短的延迟,编译器可能使用 8 位或 16 位计数器。对于非常短的延迟,它可能只使用 rjmp 和 nop。对于非常长的延迟,它可能使用超过 24 位的计数器。

在某些情况下,可以通过在循环内添加几个 nop 来节省程序空间,因为它可能会产生更小的循环计数器,或者可能让您在末尾删除多条指令。因此,真正明智的实现会考虑到这一点。

关于c - __builtin_avr_delay_cycles 实现说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54972524/

相关文章:

c - 非阻塞 TCP 接受器不从套接字读取

assembly - 纳斯姆错误: invalid combination of opcode and operands

c - 这个C union 的成员有什么区别吗?

windows - XP Embedded 启动时串行端口计数仪发出咔嗒声

c - 如何通过串行读取在 Arduino LCD 上存储两条单独的线

c - 从 g_variant_new () 获取结果

c++ - 将 double 的位模式显示为十六进制?

C - 如何使用变量作为 SQL 查询的一部分?

c - 创建一个头文件来扫描整数而不使用 stdio.h(scanf)

c - 程序集,无法将数组值从堆栈添加到寄存器