不要繁琐!使用bitSet提高代码质量

使用Arduino我们可以做出很炫的作品,它把枯燥的底层代码都封装成了一个个的函数,当我们使用时直接调用相应的函数即可,而不用知道它们是怎么实现的。可是小编认为,非专业人士这么做无可厚非,可是如果作为专业人士也不去关注这些函数的底层代码是如何实现的就有点差强人意了。写作本文的目的就是通过两个函数被转换成机器码之后的差异,阐述一下了解底层代码的重要性。好了,不费话了,直接上图:

 

 

 

 

图一 使用pinMode函数编译后代码的大小

 

 

 

 

图二 使用bitSet函数替换pinMode函数编译后代码的大小

通过上面两幅图片可以看到,使用pinMode函数编译后代码的大小是1116字节,而使用bitSet函数编译后代码的大小是982字节,两者之间差了134字节,这仅仅是一条语句的置换,如果我们使用多个传感器,需要设置多个端口的输入输出状态,那么效果就会立马体现出来了!

究其原因是因为bitSet函数的实现只是一个宏定义,

#define bitSet(value,bit) ((value) |= (1UL << (bit)))

对输入到其中的量做了一个替换(至于pinMode函数那繁琐的代码大家可以到Arduino官网上了解一下,小编就不在文中敲出了),但是却需要我们了解AtmelAVR与Arduino之间被复用的引脚之间的关系。

在AtmelAVR中,每个I/O端口的输入输出状态是由数据方向寄存器(data direction register,即DDRx)里相对应的位决定的,以Arduino UNO(或者其他使用ATmega328的板子)为例,数字引脚13对应的就是PB5(即DDRB,位5);而在Arduino Mega 2560中就是PB7(即DDRB,位7)。

进一步优化

在Blink程序中,实现闪烁的方法就是使引脚产生高低电平的变化,形成电流的通断,使用的函数是digitalWrite。现在我们就要对它下手了,当初AVR的设计工程师打算给新的芯片加入引脚翻转的能力的时候,发现已经没有多余的I/O地址可以用了,于是他们重用了之前用于输入的地址。向PINx寄存器的一个位写入1会逆转或者说翻转那个端口对应的输出引脚的电平(这个方法在ATmega8、16或者32上是不能用的)。不需要保存寄存器中其他位的值,因为理论上它们是不存在的。用此方法我们就可以将两条digitalWrite函数调用都换成bitSet(PINB,5),效果如下图所示:

 

 

 

 

图三 使用bitSet函数替换digitalWrite函数编译后代码的大小

从图中我们看到代码的大小变成了706字节,比原始的1018字节浓缩了410字节之多,只是因为我们改了三个函数就起到了如此的效果。那能不能变得更小呢?答案当然是YES!

在上图中我们可也看到bitSet和delay被调用了两次,可是起到的作用都是一样的——翻转13脚的电平状态。我们知道loop()函数本质上是一个循环函数,由此我们就可以将重复的语句去掉,如下图所示:

 

 

 

 

图四 进一步精简之后的程序

程序的大小变为692字节了,比之前的706字节又减少了14字节,总体减少了424字节。虽然原始的程序被我们改的面目全非,但是我觉得这是有意义的,比起只是简单的调用函数,这可以让我们学到更多的东西,了解控制器的工作原理。

posted @ 2020-03-17 17:27  chenlife  阅读(1246)  评论(0)    收藏  举报