硬件环境: P89LPC917, DS1820温度芯片, VDD=3.3V, 调试串口;
软件环境: Keil uVision2, 串口通讯控制器(用来接收串口数据)
过程:
看了资料, 其有着许多C51单片机操作DS1820的实例,但是其晶振频率都是参照标准的51芯片的,为11.059MHz. 而P89LPC917的晶振是7372800Hz.由于单总线对时序十分的严谨,而且十分的短暂,所以其延时都是用的循环代码来延时的,这关键就是在7372800Hz晶振下的延时程序应该如何做?
看书了解了什么时钟周期,机器周期,指令周期以后打算算出一个精确的延时程序来,可是偶用C语言来写的,这实在是不好算. 最后,偶打算用示波器来测量延时的时间. 一番折腾后偶得到了下面的结论:
延时代码为:
// DELAY - with an 7.3728MHz crystal
// Calling the routine takes about 4μs, and then
// each count takes another 3.2μs
void delay( uint16 us )
{
volatile uint16 s;
for ( s = 0; s < us; s++ );
}
结论:
调用一次循环函数所用的时间 4us
没增加一次循环增加的时间 3.2us
一条赋值代码所需要的时间 280ns
之后参照其数据手册,严格按照其读写的时序来操作,最终正确取到了温度值(请注意,其温度是以0.5度为递增的,取道的值转换为十进制以后要除以2才是真正的温度值)
现把代码公布于下:
/*
*********************************************************************************************************
* ######有限公司
* 技术研发部
*
* (c) Copyright 2005-2006, kmajian
* All Rights Reserved
*
* 通过单总线协议从DS1820获取温度程序
*
* File : GETTMP.H
* By : kmajian
* Date : 2006-5-15
*********************************************************************************************************
*/
#include "config.h"
sbit DQ = P0^1;
void GetTempInit( void )
{
P0M1 = 0x00; // 将P0.1设置为准双向口
P0M2 = 0x00;
}
// DELAY - with an 7.3728MHz crystal
// Calling the routine takes about 4μs, and then
// each count takes another 3.2μs
void delay( uint16 us )
{
volatile uint16 s;
for ( s = 0; s < us; s++ );
}
// 复位
uint8 ow_reset( void )
{
unsigned char presence;
DQ = 0; // pull DQ line low
delay( 141 ); // leave it low for 480μs
DQ = 1; // allow line to return high
delay( 14 ); // wait for presence
presence = DQ; // get presence signal
delay( 110 ); // wait for end of timeslot
return( presence ); // presence signal returned
} // presence = 0, no part = 1
// 读位
uint8 read_bit( void )
{
DQ = 0; // pull DQ low to start timeslot
DQ = 0;
DQ = 0;
DQ = 0;
DQ = 1; // then return high
delay( 3 ); // delay 15μs from start of timeslot
return( DQ ); // return value of DQ line
}
// 写位
void write_bit( int8 bitval )
{
DQ = 0; // pull DQ low to start timeslot
DQ = 0; // 至少保持1us
DQ = 0;
DQ = 0;
if( bitval == 1 )
DQ = 1; // return DQ high if write 1
delay( 18 ); // hold value for remainder of timeslot,60us
DQ = 1;
}
// 读字节
uint8 read_byte( void )
{
uint8 i;
uint8 value = 0;
for ( i = 0; i < 8; i++ )
{
if( read_bit() )
value |= 0x01<<i; // reads byte in, one byte at a time and then
// shifts it left
delay( 18 ); // wait for rest of timeslot
}
return( value );
}
// 写字节
void write_byte( int8 val)
{
uint8 i;
uint8 temp;
for (i = 0; i < 8; i++) // writes byte, one bit at a time
{
temp = val>>i; // shifts val right ‘i’ spaces
temp &= 0x01; // copy that bit to temp
write_bit( temp ); // write bit in temp into
}
delay( 18 );
}
// 读取温度
uint8 GetTmp( uint8 *sym )
{
uint8 val1 = 0, val2 = 0;
ow_reset(); // 复位
write_byte( 0xCC ); // 跳过 ROM 命令
write_byte( 0x44 ); // Convert T 命令
TDelay( 100 ); // 延时一秒, 转换需要足够的时间
ow_reset(); // 复位
write_byte( 0xCC ); // 跳过 ROM 命令
write_byte( 0xBE ); // 发送读取命令
val1 = read_byte(); // 读取低字节
val2 = read_byte(); // 读取高字节
if( val2 > 0 ) // 温度为负值则做处理
{
*sym = 1; // 标示为负数
val1 = ~val1 + 1; // 取其补码
}
else
{
*sym = 0;
}
return val1;
}
/*
**********************************************************************************************************
* END
*********************************************************************************************************
*/