前面两篇文章有点水,我也是这么认为的。所以这一篇我想写得好点:)
DS18B20是一个比较热门的器件,想当初学51,PIC,STC,STM32的时候都做过这个一线器件的实验,现在玩树莓派的gpio,自然的就想起了这个,wiringPi没有直接的一线器件相关的函数,所以我们就只有自己写。
先说下电路连接,我用的是一般接法。
原理等内容不再累述,直接进入根据时序图写程序的环节。
int oneWireReset(int pin)
{
pinMode(pin,OUTPUT);
digitalWrite(pin,HIGH);
digitalWrite(pin,LOW);
delayMicroseconds(480);
digitalWrite(pin,HIGH);
delayMicroseconds(30);
pinMode(pin,INPUT);
if(digitalRead(pin) == LOW)
{
ack = 1;
}
else
{
ack = 0;
}
delayMicroseconds(450);
return ack;
}
void writeBit(int pin,int bit)
{
pinMode(pin,OUTPUT);
digitalWrite(pin,LOW);
delayMicroseconds(15);
digitalWrite(pin,bit);
delayMicroseconds(45);
digitalWrite(pin,HIGH);
}
再包装一下,写八个字节(就是写命令)
void oneWireSendComm(int pin,int byte)
{
for(int i=0;i<8;i++)
{
int sta = byte & 0x01;
writeBit(pin,sta);
byte >>= 1;
}
}
int readBit(int pin)
{
int tmp;
pinMode(pin,OUTPUT);
digitalWrite(pin,HIGH);
digitalWrite(pin,LOW);
delayMicroseconds(15);
digitalWrite(pin,HIGH);
pinMode(pin,INPUT);
tmp = digitalRead(pin);
delayMicroseconds(45);
return tmp;
}
包装一下,读取八个字节。
int oneWireReceive(int pin)
{
int i,j,k;
for(i=0;i<8;i++)
{
j = readBit(pin);
k = (j << 7) | (k >> 1);
}
return k;
}
嗯,看上去很不错,随便画画窗体,先测试一下先。
大概流程如下:
if(oneWireReset(7))//我接的GPIO4,
{
oneWireSendComm(7,0xcc);//忽略ROM
oneWireSendComm(7,0x44);//开始温度变换
}
if(oneWireReset(7))
{
oneWireSendComm(7,0xcc);//忽略ROM
oneWireSendComm(7,0xbe);//读暂存
int a = oneWireReceive(7);//读LSB
int b = oneWireReceive(7);//读MSB
}
what?什么鬼?怎么也看不像啊,MSB:29339 = 0x729B LSB: 7511038 = 0x729BFE。根本就不可能啊,程序有误,(bug排查ing...)
int oneWireReceive(int pin)
{
int i,j,k;
for(i=0;i<8;i++)
{
j = readBit(pin);
k = (j << 7) | (k >> 1);
}
k = k & 0x00FF;//把没有操作过的其余8bit排除掉
return k;
}
这么修改过后其实我的程序还是没有正常的运行,然后就只能怀疑是总线通讯的问题了。这下怎么办?我们就只有借助工具的力量了,一台可以存储波形片段的示波器或者是一个逻辑分析仪。推荐逻辑分析仪。
嗯,初始化不错。
int oneWireReset(int pin)
{
pinMode(pin,OUTPUT);//输出模式
digitalWrite(pin,HIGH);//先高
digitalWrite(pin,LOW);//再低
delayMicroseconds(480);//延时
digitalWrite(pin,HIGH);//输出高电平释放总线,为啥?看电路图,4.7k上拉电阻到高电平
delayMicroseconds(30);//延时
pinMode(pin,INPUT);//转为输入模式
if(digitalRead(pin) == LOW)//检测18b20的相应(低电平)
{
delayMicroseconds(450);
return true;
}
else
{
return false;
}
}
呃,接下来第一个命令的发送就出问题了,逻辑分析仪已经帮我们分析出来了,Invalid ROM command:[0x2C],我们不是发的0xCC么。仔细看波形,再分析相关发送的程序(箭头所指就是问题所在),高低电平转换太快,再协调一下延时参数的设置。
void writeBit(int pin,int bit)
{
pinMode(pin,OUTPUT);
digitalWrite(pin,LOW);
delayMicroseconds(2);//调节了此延时
digitalWrite(pin,bit);
delayMicroseconds(30);//调节了此延时
digitalWrite(pin,HIGH);
delayMicroseconds(1);//让高低电平转换没有那么快
}
这下波形就对了。
然后这是读取暂存的波形,可以看到已经读出LSB:0xB2,MSB:0x01.这里我的读取函数也是有问题的,经修改如下:
int readBit(int pin)
{
int tmp;
pinMode(pin,OUTPUT);
digitalWrite(pin,HIGH);
digitalWrite(pin,LOW);
delayMicroseconds(2);//调节了此延时
digitalWrite(pin,HIGH);
pinMode(pin,INPUT);
delayMicroseconds(2);//这个小延时很关键,没有它很容易误读上两行的高电平
tmp = digitalRead(pin);
delayMicroseconds(40);//调节了此延时
return tmp;
}
ok,现在就对了LSB: 191 = 0xBF, MSB: 1 = 0x01。组合:0x01BF = 447
那么温度就是:447 * 0.0625 = 27.9375.
float tempchange(int lsb, int msb)
{
float temp;
int tem;
if (msb >= 0xF0) //负温度
{
msb = 255 - msb;
lsb = 256 - lsb;
tem = -(msb * 16 * 16 + lsb);
}
else
{
tem = (msb * 16 * 16 + lsb); //正温度
}
temp = tem * 0.0625;
return temp;
}
好了,现在我们已经可以正确读取温度值了。
程序下载;
https://github.com/partx/Qt_Raspi-GPIO.git