惊讶:使用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代码。

前夕

欢迎您的咨询