在写DS18B20温度传感器的代码时,我们需要对各种操作写出对应的函数,再在其他函数中将这些函数调用,以此达成更复杂的目的。
例如,我们需要有“发送一位”函数:OneWire_SendBit()。
然后通过循环调用OneWire_SendBit()八次,即可实现“发送字节函数”:OneWire_SendByte()。
逻辑上十分清晰,但在具体写函数的时候,却可能因为unsigned char与bit的细节,导致错误的出现。
下面是具体说明:
先给出OneWire_SendByte()函数的写法:
1 | void OneWire_SendByte(unsigned char Byte){ |
我们可以看出,OneWire_SendByte()在循环体中调用了8次OneWire_SendBit(),并通过位运算将对应的字节传入OneWire_SendBit()函数中。
再来看一下OneWire_SendBit()函数的写法。首先我们给出OneWire_SendBit()函数需要实现的要求:
unsigned char与bit的陷阱/1-1.png)
下面给出四种不同的OneWire_SendBit()写法:
方法一:
1 | void OneWire_SendBit(unsigned char Bit){ |
方法二:
1 | void OneWire_SendBit(unsigned char Bit){ |
方法三:
1 | void OneWire_SendBit(unsigned char Bit){ |
方法四:
1 | void OneWire_SendBit(unsigned char Bit){ |
在上述四种方法中,仅有方法一和方法三是正确的,方法二和方法四均不能正常示数。
我们可以看到,方法一在总线拉低后30μs内将Bit赋值给OneWire_DQ,这样在30μs节点到来的时候从机正常读出OneWire_DQ是1还是0即可。
后三种方法则从需求本身出发,通过if函数来判断传入需要将总线总线拉低多少μs,理论上同样也能达到效果,但却只有第三种方法可以正常示数,这是为什么呢?
原因就在于,我们在OneWire_SendByte()函数中,通过调用OneWire_SendBit(),传入的参数为:Byte&(0x01<<i),这实际上是一个无符号八位字节型数据,如:0b 1010 1100。
而我们在写OneWire_SendBit()时,可能会由于书写习惯,将Bit形参定义为unsigned char类型,这就使得会导致if判断出现错误:方法二和方法四中均存在if(Bit==1)的判断,而实际上传入的参数并不一定是1,可能是其他的非0值,在逻辑上我们认为其应该与1等价,但程序由于已经预设了传入参数的类型为unsigned char,因此在传入非0非1值时不会执行对应的代码。
在修改传入参数后,方法二和方法四同样可以正常示数了,下面以方法二为例:
1 | void OneWire_SendBit(bit Bit){ |
这是因为当传入参数为Byte&(0x01<<i)时,预设的传入参数的类型为bit型,程序会自动执行类型转换,将unsigned char转换为bit,即将非0非1值转化为1,这样就不会出现问题了。