首页 > 惊讶:使用Keil C编写51个单片机延迟程序实际上是如此精致
当使用单片机时,我们经常遇到需要短时间延迟的情况。
所需的延迟时间非常短,通常为数十到数百微秒(us)。
有时需要高精度。
例如,当使用单片机驱动DS18B20时,允许的误差范围在十几码之内,否则很容易出错。
在这种情况下,使用计时器通常很麻烦。
在极端情况下,计时器甚至被用于其他目的。
这时,我们需要考虑其他方式。
以前使用汇编语言编写MCU程序时,此问题相对容易解决。
例如,使用12MHz的晶体振荡器51,打算延迟20us,只要以下代码可以满足一般需要:51单片机的指令周期是晶体振荡器频率的1/12,即, 1us的周期。
mov r0,#09h需要2个极限周期,而djnz也需要2个极限周期。
那么,存储在r0中的数字为(20-2)/ 2。
使用这种方法,可以非常方便地实现小于256us的延迟。
如果需要更长的时间,则可以使用两个级别的嵌套。
而且精度可以达到2us,一般来说,这就足够了。
现在,毫无疑问,Keil的C编译器得到了更广泛的使用。
与汇编相比,C当然具有许多优点,例如程序易于维护,易于理解,适用于大型项目。
但是缺点(我认为这是C的唯一缺点)是无法保证实时性能,并且无法预测代码执行的指令周期。
因此,在实时性要求较高的场合,还需要组合件和C的联合应用。
但是是否需要使用汇编程序来实现这样的延迟程序?为了找到这个答案,我做了一个实验。
要使用C语言实现延迟程序,首先想到的是C中常用的loop语句。
以下代码是我在Internet上经常看到的代码:该代码的准确性如何?为了直接衡量此代码的效果,我发现了Keil C基于此代码生成的汇编代码:我真的不知道~~~我可以看到这个延迟程序有多不准确~~~在这四个主要语句中,需要6个机器周期。
换句话说,它的准确度最高为6us,其中不包括lcall和ret。
如果我们列出调用函数时分配的i值的根延迟长度,则为:因为函数调用需要2个时钟周期的lcall,所以延迟时间比从函数代码的执行时间长2倍。
顺便说一句,有些朋友写了这段代码:有些人可能认为它将生成更长的汇编代码,但事实证明生成的代码是相同的。
但这确实不是一个好习惯。
因为实际上没有必要在此处引入额外的变量。
我们继续讨论这个话题。
一些朋友甚至使用此代码来获得更长的延迟:此代码生成的汇编代码是什么?实际上,您不必考虑它就知道$#^%&%$有多可怕。
....让我们看看:呵呵,这确实可以延迟很长时间~~~但是根本没有精度。
那么,使用C可以实现准确的延迟吗?我对代码进行了一些更改:因为根据经验,更简洁的C代码通常可以导致更简洁的机器代码。
这是什么结果?取出它生成的汇编代码并对其进行查看。
希望我按了“构建目标”按钮,结果是沉重的打击:尽管生成的代码与使用for语句不同,但是我可以毫无疑问地说这两种方法的效率是相同的。
。
似乎到此结束,因为我真的想不出任何简化源程序的空间。
看来我即将得出这个结论:“如果您需要我们级别的延迟精度,请在需要时使用汇编语言”。
但这是真的吗?我仍然不甘心。
因为我不相信著名的Keil C编译器甚至不能使用djnz? ? ?因为实际上在程序主体中仅需要一个句子循环:djnz r7,循环。
几乎是绝望的(在这种情况下人们通常会爆发,哦哈哈哈~~~),我只是更改了它:不在意地编译,请看源代码:天堂~~~出现了奇迹。
.....我认为该程序应该已经能够满足一般需求。
如果创建表:计算延迟时间时,已计算调用函数的lcall语句的2个时钟周期。
最后,结果很明显。
只要合理使用,C仍然可以达到意想不到的结果。
许多朋友抱怨C的效率比汇编的要差得多。
实际上,如果您对Keil C的编译原理有更深入的了解,则可以通过适当的语法用法来优化生成的C代码。
前夕