zoologist 发表于 2015-10-19 16:47:49

用示波器“看” arduino (2) ----精确的延时

有一个网友提出一个问题“现在要进行一个0.4us的延时,发现不管怎么调都只能调到15us,根本达不到要求,我用的芯片是MEGA32U4-AU,外部晶振是16MHz,求见解!!!!!”
我试验了一下,最终的程序如下:
const int PinA =13;      
void setup() {
pinMode(PinA, OUTPUT);
digitalWrite(PinA,LOW);
}

void loop() {
PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000; //digitalWrite(PinA,LOW);
    for (long zdelay=0;zdelay<9; zdelay++) {
    __asm__("nop\n\t");
}
PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000; //digitalWrite(PinA,LOW);
PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000;//digitalWrite(PinA,LOW);
}


这个程序运行之后的波形如下

可以看到中间的延时差不多有4.1331us(我用游标对齐,右下角显示x=4.1331). 让然这个值中还包括了一个拉GPIO的指令周期,大约会有 62.1ns的影响。此外,如果要求特别精确,在使用时还要考虑周期性中断的影响。这里就不说了…….

下面我们继续实验,尝试找到循环次数和实际delay时间的关系(因为涉及到编译器优化, long int的计算和判断,直接尝试计算机器周期不可行)。
首先尝试zdelay<8,测量结果是3.7013us

根据上述值结合循环简单猜测一下,对于这个循环体,固定部分耗时0.2469us (比如给变量赋初始值),循环部分每次耗时0.4318us
就是 T= 0.2469 + n *0.4318
根据这个计算循环zdelay<5应该是 2.4059us测量结果是2.3722us
加入我们打算delay 100us 根据上述公式应该循环 231次
代码:

const int PinA =13;      
void setup() {
pinMode(PinA, OUTPUT);
digitalWrite(PinA,LOW);
}

void loop() {
PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000; //digitalWrite(PinA,LOW);

for (long zdelay=0;zdelay<231; zdelay++) {
    __asm__("nop\n\t");
}

PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000; //digitalWrite(PinA,LOW);
PORTB = B100000; //digitalWrite(PinA,HIGH);
PORTB = B000000;//digitalWrite(PinA,LOW);
}


实际测试结果是 101us, 符合理论…….


提示:如果你打算用上面的方法来做精确的延时,那么首先你要确保你有一台好用的示波器.

叶斌远行 发表于 2015-10-19 20:44:25

你好,可以关于这句话,问一下双引号里面的内容的作用__asm__("nop\n\t");

Ansifa 发表于 2015-10-19 21:08:55

我等穷人一般用几十块的USB SALEAE解决这个问题尤其是做遥控

sanyouhi 发表于 2015-10-19 21:09:45

本帖最后由 sanyouhi 于 2015-10-19 21:39 编辑

有点南辕北辙了,你的程序直接写成这样就行#define F_CPU 16000000
#include <util/delay.h>

void setup()
{
DDRB = 0X20;
}

void loop()
{
PORTB = 0X20;
_delay_us(0.4);
PORTB = 0;
}实际测量是438ns左右,因为他必定是62.5的整数倍,不可能得到400ns(不禁让我想起茶啊二中)。这样从大循环里用for延时一般很难得到准确的延时结果,不如用现成的函数。

sanyouhi 发表于 2015-10-19 21:15:58

叶斌远行 发表于 2015-10-19 20:44 static/image/common/back.gif
你好,可以关于这句话,问一下双引号里面的内容的作用__asm__("nop\n\t");

那个语句应该是__asm__("nop");这是一个在C语言里调用汇编语言的语句,调用的是汇编语言的NOP语句,NOP的作用就是让机器空跑一个时钟周期。

叶斌远行 发表于 2015-10-20 07:30:19

sanyouhi 发表于 2015-10-19 21:15 static/image/common/back.gif
那个语句应该是__asm__("nop");这是一个在C语言里调用汇编语言的语句,调用的是汇编语言的NOP语句,NOP的 ...

哦,原来是这样子啊!

zoologist 发表于 2015-10-20 07:59:11

sanyouhi 发表于 2015-10-19 21:09 static/image/common/back.gif
有点南辕北辙了,你的程序直接写成这样就行实际测量是438ns左右,因为他必定是62.5的整数倍,不可能得到400 ...

不错啊受教了~

ianon 发表于 2015-10-20 17:25:04

:):)好办法
页: [1]
查看完整版本: 用示波器“看” arduino (2) ----精确的延时