#MSP430之DS18B20调试记录
###前言
说实话,对于DS18B20这种烂大街的温度传感器,我一开始真的没把它当回事,直到碰壁碰的让我怀疑人生,才知道无论什么东西,只要还有你不理解的地方,那里就隐藏着坑,等着你往里面跳.
话不多少,直接上代码:
#include "dls.h"
#include "GR_include.h"
UINT8 flag1 = 0;
UINT8 DS18B20Init(void)
{
UINT8 i;
UINT16 k;
_DINT();
for (i = 0; i < 1; i++)
{
U20_DQ_OUT;//设置为输出方向
U20_DQ_LOW;//拉低总线
Delayus(520);
U20_DQ_HIGH;//释放总线
Delayus(15); // 15-60us
U20_DQ_IN;
for (k = 0; k < 10; k++)
{
if (!READ_U20_DQ_IN)
{
break;
}
Delayus(10); /* 等待65us */
}
if(k >= 10)
{
continue;
}
/* 等待DS18B20释放DQ */
for (k = 0; k < 30; k++)
{
if (READ_U20_DQ_IN)
{
break;
}
Delayus(10); /* 等待65us */
}
if (k >= 30)
{
continue; /* 失败 */
}
break;
}
_EINT();
Delayus(5);
if (i >= 1)
{
return 0;
}
return 1;
}
UINT8 ReadU20DSByte(void)
{
UINT8 i;
UINT8 ReadData=0;
_DINT();
for(i=0;i<8;i++)
{
ReadData>>=1;
U20_DQ_OUT;
U20_DQ_LOW;
Delayus(3);
// P6DS |= BIT5;
U20_DQ_HIGH;
Delayus(3);
U20_DQ_IN;
if(READ_U20_DQ_IN)
{
ReadData|=0x80;
}
Delayus(60);
}
_EINT();
return ReadData;
}
//写一个字节
void WriteDSByte(UINT8 WriteData)
{
UINT8 i;
_DINT();
for(i=0;i<8;i++)
{
U20_DQ_OUT;
U20_DQ_LOW;
Delayus(2);
if(WriteData&0x01)
{
U20_DQ_HIGH;
}
else
{
U20_DQ_LOW;
}
Delayus(60);
U20_DQ_HIGH;
Delayus(2);
WriteData>>=1;
}
_EINT();
}
//温度计算程序
/* MSB 温度高字节 */
/*LSB 温度低字节 */
SINT16 GetT(SINT8 MSB, UINT8 LSB)
{
int t1=0; /*温度整数部分的值*/
UINT8 t2=0; /*温度小数部分的值*/
SINT16 wData=0;
UINT8 ucNeFlg=0; /*定义达拉斯测试是否为负温度标准*/
SINT8 ucTemph=MSB,ucTempl=LSB;
float ucfloat;
ucTemph=MSB;
ucTempl=LSB;
if((MSB&0xF0)>0)
{ //判断是否为负温度
ucNeFlg=1;
}
else
{
ucNeFlg=0;
}
if(ucNeFlg)
{ //如果为负温度取反加1
ucTempl=~ucTempl+1;
ucTemph=~ucTemph+(!ucTempl?1:0);
}
t1=ucTemph<<4; //得到温度整数部分
t1|=(ucTempl>>4);
ucfloat =(float)(ucTempl&0x0F);
ucfloat *=0.625;
t2 = (UINT8)ucfloat;
wData = t1*10+t2;
if(ucNeFlg)
{ //如果为负温度取反加1
wData=~wData+1;
}
return wData;
}
SINT16 ReadDasDataU20(void)
{
UINT8 MSB=0,LSB=0;
SINT16 wData = 0;
if(DS18B20Init() == 0)
{
return 0;
}
WriteDSByte(0xCC); //跳过ROM配置
WriteDSByte(0x44); //启动温度转换
Delayms(750); //等待转换完毕
flag1 = DS18B20Init();
WriteDSByte(0xCC);
WriteDSByte(0xBE); //读温度寄存器
LSB=ReadU20DSByte(); //读温度数据低字节
MSB=ReadU20DSByte(); //读温度数据高字节
wData=GetT(MSB,LSB); //计算温度
return wData;
}
以上这段驱动我是按照安富莱的DS18B20驱动程序改的,开发环境如下:
- 实验平台:MSP430F5529LanchPad
- IDE:IAR 7.11.3
如自己所预料的,代码移植一次成功,然后兴冲冲的将这段代码用到另一个平台:MSP430F4793上,然而同样的代码,却怎么也读不出正确的结果了,最初我以为是我焊接的时候一不小心把传感器给焊坏了?然而重新取下来换到LanchPad上又可以读到正确值,见了鬼了!
由于我坚信代码是没问题的,所以我将问题放到了硬件上:把上拉电阻改成一致,不行;难道供电有问题?祭上示波器,电源稳定的无可挑剔,DQ引脚上的波形也很完美,时序完全符合手册上的描述,除了读到的值不对,实在找不到硬件上哪里不对…
就这样弄了一天,问题没解决,到是看手册发现这玩意还能利用数据线供电,但是就我的问题而言并没有什么卵用…
第二天就在我要放弃了的时候,公司的前辈过来问我在做什么,一开始我想这么简单的一个传感器自己一天都没调出来实在很丢脸,但是这个问题自己又觉得确实很费解,想着前辈见多识广,说不定知道问题出在哪里;
于是我向他说明了情况,他先是给了我逻辑分析仪让我再试试,我接上逻辑分析仪,一样除了结果不对,一切都是OK的
后面他看了代码,然后让我试下其他项目用过的代码,一开始我的内心是拒绝的,但是想到自己一路上遇到过这么多看似不可思议却又再正常不过的问题,再加之我也没什么更好的解决办法,那就试试吧,结果就读出来了…
妈蛋,难道程序有问题?可是在MSPF5529上怎么就可以,
由于一直是读数据这地方有问题,我很自然的把问题定位到ReadU20DSByte(void)这个函数上,对比之后发现,有一个地方不太一样,
for(i=0;i<8;i++)
{
ReadData>>=1;
U20_DQ_OUT;
U20_DQ_LOW;
Delayus(3);
// P6DS |= BIT5;
U20_DQ_HIGH;
Delayus(3);
U20_DQ_IN;
if(READ_U20_DQ_IN)
{
ReadData|=0x80;
}
Delayus(60);
}
正常的程序没有下面这一句:
U20_DQ_HIGH;
Delayus(3);
于是我屏蔽掉这句,果然之前不行的程序在MSP430F4793上也可以读出来了;
可是为什么呢?难道IO结构不一样?
怀着疑惑翻看了一下手册,还真有不一样的地方:
MSP430F5529多了一个PxDS寄存器,默认是弱驱动输出,然后再看安富莱上的程序,他的IO是配置成开漏输出,开漏输出高其实是上拉电阻拉高的,与MSP430单片机的输出高是全然不同的;
于是我在输出高前面加了一句P6DS |= BIT5;(配置为强驱动输出),果然,这时候在MSP5529上读到的也是之前错误的值;
还是对DLS时序一知半解惹的祸,DLS在读状态拉低DQ1us就该DLS输出了,这时候MSP430的输出高并没有释放总线,而开漏输出高或者弱驱动输出则真正释放了总线,这样当然只有后者能读到正确的结果了.
对于STM32单片机的开漏输出高,其实有两层含义:
- 是IO上拉到高电平
- 是IO可由外部电平改变,单片机可以读取它的电平;
所以,对于STM32单片机,没有这句开漏输出高,读DLS,恐怕也只能读到一堆0了;
总结:
- 移植程序忽略了开漏输出和推挽输出的不同
- 没有认真读DLS的时序,没有理解释放总线的概念
ps:又犯了先入为主的错误,我以为同是430单片机,默认配置下的单片机IO不会有什么区别,我以为的同样的代码,同样的实验平台实则有很大的不同,一切都事出有因,忽略的地方就隐藏着答案.
2020-05-27
看到有人要带代码,翻了下硬盘,找到当初的代码上传了