例如,在1MHZ工作频率下需要延时10us,就需要嵌入10句“空操作”指令,显然在书写上比较难堪。本文提出一种简化书写的延时方案,使用带参数的宏构来造微小时间片,可以实现完全精确的软件延时,大大方便了驱动程序及软件模拟通信协议的编写。
说明:以下皆为ICC AVR平台下的讨论,对AVR系列所有型号的单片机皆有效。至于其他平台,可据此方案自行修改和移值。
该方案的实现方法其实很简单:
首先定义N个宏,分别调用 1 ~ N 个汇编“空操作”指令,如:
#define NOP_1 asm("nop") //延时一个时钟周期
#define NOP_2 NOP_1; asm("nop") //延时两个时钟周期
#define NOP_3 NOP_2; asm("nop") //延时三个时钟周期
#define NOP_4 NOP_3; asm("nop") //延时四个时钟周期
……
#define NOP_40 NOP_40; asm("nop") //延时40个时钟周期
然后利用“##”操作符,实现带参数宏的延时:
#define NOP(N) NOP_##N //延时 N个时钟周期
操作符的作用是把两个部分的内容连成一个内容。就是说,NOP(3)展开后成为NOP_3,NOP(4)展开后成为NOP_4,等等。因此,定义上述宏之后,就可通过调用NOP(N)语句实现精确软件延时。例如:
NOP(4); //延时4个时钟周期
上述语句展开过程如下:
NOP_3 ; asm("nop");
NOP_2; asm("nop"); asm("nop");
NOP_1; asm("nop"); asm("nop"); asm("nop");
asm("nop"); asm("nop"); asm("nop"); asm("nop");
正好延时4个时钟周期
不过,上面的宏还不够完善,如果试图使用下面的语句,程序将会出现漏洞。
if(表达式)
NOP(3);
else
NOP(4);
这是因为NOP(N)宏展开之后,不是一个语句,而是变成N个语句。故必须用花括号括起来,程序才能运行正确。即应该改为:
if(表达式)
{
NOP(3);
}
else
{
NOP(4);
}
如果把NOP(N)宏的定义改为:
#define NOP(N) do{ NOP_##N ; }while(0)
则NOP(N)宏展开之后只形成一个语句,将不会出现上面的问题。
但是要注意,“##”操作符只能按照原样把两边的内容连在一起。故NOP(N)的参数必须是具体的常量,即只能是数字,并且,与该数字相对应的宏NOP_N已必须已经定义。
例如:
“NOP(3+4);”语句展开之后,将将变成“NOP_3+4;”,出现语法错误;
又如:
“NOP(a);”语句展开之后,将将变成“NOP_a;”,而“NOP_a;”未定义。
只有这样的语句才是正确的调用:
NOP(20); //延时20个时钟周期
将上述方案整理成一个头文件,以后就可以任意调用了。下面是整理好的头文件:
注意:该文件不宜作长时间的延时。长时间的延时将会调用大量的“空操作”指令,占用大量的程序指令空间。这个问题将在V2.0版本中解决。
NOP.H
#ifndef _NOP_H_
#define _NOP_H_
#define NOP(N) do{ NOP_##N(); }while(0)
#define NOP_0()
#define NOP_1() asm("nop")
#define NOP_2() NOP_1();asm("nop")
#define NOP_3() NOP_2();asm("nop")
#define NOP_4() NOP_3();asm("nop")
#define NOP_5() NOP_4();asm("nop")
#define NOP_6() NOP_5();asm("nop")
#define NOP_7() NOP_6();asm("nop")
#define NOP_8() NOP_7();asm("nop")
#define NOP_9() NOP_8();asm("nop")
#define NOP_10() NOP_9();asm("nop")
#define NOP_11() NOP_10();asm("nop")
#define NOP_12() NOP_11();asm("nop")
#define NOP_13() NOP_12();asm("nop")
#define NOP_14() NOP_13();asm("nop")
#define NOP_15() NOP_14();asm("nop")
#define NOP_16() NOP_15();asm("nop")
#define NOP_17() NOP_16();asm("nop")
#define NOP_18() NOP_17();asm("nop")
#define NOP_19() NOP_18();asm("nop")
#define NOP_20() NOP_19();asm("nop")
#define NOP_21() NOP_20();asm("nop")
#define NOP_22() NOP_21();asm("nop")
#define NOP_23() NOP_22();asm("nop")
#define NOP_24() NOP_23();asm("nop")
#define NOP_25() NOP_24();asm("nop")
#define NOP_26() NOP_25();asm("nop")
#define NOP_27() NOP_26();asm("nop")
#define NOP_28() NOP_27();asm("nop")
#define NOP_29() NOP_28();asm("nop")
#define NOP_30() NOP_29();asm("nop")
#define NOP_31() NOP_30();asm("nop")
#define NOP_32() NOP_31();asm("nop")
#define NOP_33() NOP_32();asm("nop")
#define NOP_34() NOP_33();asm("nop")
#define NOP_35() NOP_34();asm("nop")
#define NOP_36() NOP_35();asm("nop")
#define NOP_37() NOP_36();asm("nop")
#define NOP_38() NOP_37();asm("nop")
#define NOP_39() NOP_38();asm("nop")
#define NOP_40() NOP_39();asm("nop")
#endif