|
楼主 |
发表于 2010-4-28 20:35:14
|
显示全部楼层
好几天没有更新了,呵呵~~今天我把咱们常用的传感器DS1320 DS18B20给大家介绍下。
对于市面上的大多数51单片机开发板来说。ds1302和ds18b20应该是比较常见的两种外围芯片。ds1302是具有SPI总线接口的时钟芯片。ds18b20则是具有单总线接口的数字温度传感器。下面让我们分别来认识并学会应用这两种芯片。
首先依旧是看DS1302的datasheet中的相关介绍。
(原文件名:1.jpg)
上面是它的一些基本的应用介绍。
下面是它的引脚的描述。
(原文件名:2.jpg)
下面是DS1302的时钟寄存器。我们要读取的时间数据就是从下面这些数据寄存器中读取出来的。当我们要想调整时间时,可以把时间数据写入到相应的寄存器中就可以了。
(原文件名:3.jpg)
这是DS1302内部的31个RAM寄存器。在某些应用场合我们可以应用到。如我们想要做一个带定时功能的闹钟。则可以把闹钟的时间写入到31个RAM寄存器中的任意几个。当单片机掉电时,只要我们的DS1302的备用电池还能工作,那么保存在其中的闹钟数据就不会丢失~~
(原文件名:4.jpg)
由于对于这些器件的操作基本上按照数据手册上面提供的时序图和相关命令字来进行操作就可以了。因此在我们应用这些器件的时候一定要对照着手册上面的要求来进行操作。如果觉得还不够放心的话。可以到网上下载一些参考程序。对着手册看别人的程序,看别人的思路是怎么样的。
DS1302和单片机的连接很简单。只需一根复位线,一根时钟线,一根数据线即可。同时它本身还需要接一个32.768KHz的晶振来提供时钟源。对于晶振的两端可以分别接一个6PF左右的电容以提高晶振的精确度。同时可以在第8脚接上一个3.6V的可充电的电池。当系统正常工作时可以对电池进行涓流充电。当系统掉电时,DS1302由这个电池提供的能量继续工作。
下面让我们来驱动它。
sbit io_DS1302_RST = P2^0 ;
sbit io_DS1302_IO = P2^1 ;
sbit io_DS1302_SCLK = P2^2 ;
//-------------------------------------常数宏---------------------------------//
#define DS1302_SECOND_WRITE 0x80 //写时钟芯片的寄存器位置
#define DS1302_MINUTE_WRITE 0x82
#define DS1302_HOUR_WRITE 0x84
#define DS1302_WEEK_WRITE 0x8A
#define DS1302_DAY_WRITE 0x86
#define DS1302_MONTH_WRITE 0x88
#define DS1302_YEAR_WRITE 0x8C
#define DS1302_SECOND_READ 0x81 //读时钟芯片的寄存器位置
#define DS1302_MINUTE_READ 0x83
#define DS1302_HOUR_READ 0x85
#define DS1302_WEEK_READ 0x8B
#define DS1302_DAY_READ 0x87
#define DS1302_MONTH_READ 0x89
#define DS1302_YEAR_READ 0x8D
//-----------------------------------操作宏----------------------------------//
#define DS1302_SCLK_HIGH io_DS1302_SCLK = 1 ;
#define DS1302_SCLK_LOW io_DS1302_SCLK = 0 ;
#define DS1302_IO_HIGH io_DS1302_IO = 1 ;
#define DS1302_IO_LOW io_DS1302_IO = 0 ;
#define DS1302_IO_READ io_DS1302_IO
#define DS1302_RST_HIGH io_DS1302_RST = 1 ;
#define DS1302_RST_LOW io_DS1302_RST = 0 ;
/******************************************************
* 保存时间数据的结构体 *
******************************************************/
struct
{
uint8 Second ;
uint8 Minute ;
uint8 Hour ;
uint8 Day ;
uint8 Week ;
uint8 Month ;
uint8 Year ;
}CurrentTime ;
/******************************************************************************
* Function: static void v_DS1302Write_f( uint8 Content ) *
* Description:向DS1302写一个字节的内容 *
* Parameter:uint8 Content : 要写的字节 *
* *
******************************************************************************/
static void v_DS1302Write_f( uint8 Content )
{
uint8 i ;
for( i = 8 ; i > 0 ; i-- )
{
if( Content & 0x01 )
{
DS1302_IO_HIGH
}
else
{
DS1302_IO_LOW
}
Content >>= 1 ;
DS1302_SCLK_HIGH
DS1302_SCLK_LOW
}
}
/******************************************************************************
* Function: static uint8 v_DS1302Read_f( void ) *
* Description: 从DS1302当前设定的地址读取一个字节的内容 *
* Parameter: *
* Return: 返回读出来的值(uint8) *
******************************************************************************/
static uint8 v_DS1302Read_f( void )
{
uint8 i, ReadValue ;
DS1302_IO_HIGH
for( i = 8 ; i > 0 ; i-- )
{
ReadValue >>= 1 ;
if( DS1302_IO_READ )
{
ReadValue |= 0x80 ;
}
else
{
ReadValue &= 0x7f ;
}
DS1302_SCLK_HIGH
DS1302_SCLK_LOW
}
return ReadValue ;
}
/******************************************************************************
* Function: void v_DS1302WriteByte_f( uint8 Address, uint8 Content ) *
* Description: 从DS1302指定的地址写入一个字节的内容 *
* Parameter: Address: 要写入数据的地址 *
* Content: 写入数据的具体值 *
* Return: *
******************************************************************************/
void v_DS1302WriteByte_f( uint8 Address, uint8 Content )
{
DS1302_RST_LOW
DS1302_SCLK_LOW
DS1302_RST_HIGH
v_DS1302Write_f( Address ) ;
v_DS1302Write_f( Content ) ;
DS1302_RST_LOW
DS1302_SCLK_HIGH
}
/******************************************************************************
* Function: uint8 v_DS1302ReadByte_f( uint8 Address ) *
* Description:从DS1302指定的地址读出一个字节的内容 *
* Parameter:Address: 要读出数据的地址 *
* *
* Return: 指定地址读出的值(uint8) *
******************************************************************************/
uint8 v_DS1302ReadByte_f( uint8 Address )
{
uint8 ReadValue ;
DS1302_RST_LOW
DS1302_SCLK_LOW
DS1302_RST_HIGH
v_DS1302Write_f( Address ) ;
ReadValue = v_DS1302Read_f() ;
DS1302_RST_LOW
DS1302_SCLK_HIGH
return ReadValue ;
}
/******************************************************************************
* Function: void v_ClockInit_f( void ) *
* Description:初始化写入DS1302时钟寄存器的值(主程序中只需调用一次即可) *
* Parameter: *
* *
* Return: *
******************************************************************************/
void v_ClockInit_f( void )
{
if( v_DS1302ReadByte_f( 0xc1) != 0xf0 )
{
v_DS1302WriteByte_f( 0x8e, 0x00 ) ; //允许写操作
v_DS1302WriteByte_f( DS1302_YEAR_WRITE, 0x08 ) ; //年
v_DS1302WriteByte_f( DS1302_WEEK_WRITE, 0x04 ) ; //星期
v_DS1302WriteByte_f( DS1302_MONTH_WRITE, 0x12 ) ; //月
v_DS1302WriteByte_f( DS1302_DAY_WRITE, 0x11 ) ; //日
v_DS1302WriteByte_f( DS1302_HOUR_WRITE, 0x13 ) ; //小时
v_DS1302WriteByte_f( DS1302_MINUTE_WRITE, 0x06 ) ; //分钟
v_DS1302WriteByte_f( DS1302_SECOND_WRITE, 0x40 ) ; //秒
v_DS1302WriteByte_f( 0x90, 0xa5 ) ; //充电
v_DS1302WriteByte_f( 0xc0, 0xf0 ) ; //判断是否初始化一次标识写入
v_DS1302WriteByte_f( 0x8e, 0x80 ) ; //禁止写操作
}
}
/******************************************************************************
* Function: void v_ClockUpdata_f( void ) *
* Description:读取时间数据,并保存在结构体CurrentTime中 *
* Parameter: *
* *
* Return: *
******************************************************************************/
void v_ClockUpdata_f( void )
{
CurrentTime.Second = v_DS1302ReadByte_f( DS1302_SECOND_READ ) ;
CurrentTime.Minute = v_DS1302ReadByte_f( DS1302_MINUTE_READ ) ;
CurrentTime.Hour = v_DS1302ReadByte_f( DS1302_HOUR_READ ) ;
CurrentTime.Day = v_DS1302ReadByte_f( DS1302_DAY_READ ) ;
CurrentTime.Month = v_DS1302ReadByte_f( DS1302_MONTH_READ ) ;
CurrentTime.Week = v_DS1302ReadByte_f( DS1302_WEEK_READ ) ;
CurrentTime.Year = v_DS1302ReadByte_f( DS1302_YEAR_READ ) ;
}
有了上面的这些函数我们就可以对DS1302进行操作了。当我们想要获取当前时间时,只需要调用v_ClockUpdata_f( void )这个函数即可。读取到的时间数据保存在CurrentTime这个结构体中。至于如何把时间数据在数码管或者是液晶屏上显示出来我相信大家应该都会了吧^_^.
看看显示效果如何~~
(原文件名:5.jpg)
下面再让我们看看DS18B20吧。
DS18B20是单总线的数字温度传感器。其与单片机的接口只需要一根数据线即可。当然连线简单意味着软件处理上可能要麻烦一点。下面来看看它的优点:
(原文件名:1.jpg)
看看它的靓照。外形和我们常用的三极管没有什么两样哦。
(原文件名:2.jpg)
DS18B20的内部存储器分为以下几部分
ROM:存放该器件的编码。前8位为单线系列的编码(DS18B20的编码是19H)后面48位为芯片的唯一序列号。在出场的时候就已经设置好,用户无法更改。最后8位是以上56位的CRC码。
(原文件名:3.jpg)
RAM:DS18B20的内部暂存器共9个字节。其中第一个和第二个字节存放转换后的温度值。第二个和第三个字节分别存放高温和低温告警值。(可以用RAM指令将其拷贝到EEPROM中)第四个字节为配置寄存器。第5~7个字节保留。第9个字节为前8个字节的CRC码。
DS18B20的温度存放如上图所示。其中S位符号位。当温度值为负值时,S = 1 ,反之则S = 0 。我们把得到的温度数据乘上对应的分辨率即可以得到转换后的温度值。
DS18B20的通讯协议:
在对DS18B20进行读写编程时,必须严格保证读写的时序。否则将无法读取测温结果。根据DS18B20的通讯协议,主机控制DS18B20完成温度转换必须经过3个步骤:每一次读写之前都要对DS18B20进行复位,复位成功后发送一条ROM指令,最后发送RAM指令。这样才能对DS18B20进行预定的操作。
复位要求主机将数据线下拉500us,然后释放,DS18B20收到信号后等待16~160us然后发出60~240us的存在低脉冲,主机收到此信号表示复位成功。
(原文件名:4.jpg)
上图即DS18B20的复位时序图。
下面是读操作的时序图
(原文件名:5.jpg)
这是写操作的时序图
(原文件名:6.jpg)
下面让我们来看看它的驱动程序如何写吧。
sbit io_DS18B20_DQ = P2^3 ;
#define DS18B20_DQ_HIGH io_DS18B20_DQ = 1 ;
#define DS18B20_DQ_LOW io_DS18B20_DQ = 0 ;
#define DS18B20_DQ_READ io_DS18B20_DQ
/*******************************************************************
* 保存温度值的数组.依次存放正负标志,温度值十位,个位,和小数位 *
*******************************************************************/
uint8 Temperature[ 4 ] ;
void v_Delay10Us_f( uint16 Count )
{
while( --Count )
{
_nop_();
}
}
/**************************************************************************
* Function: uint8 v_Ds18b20Init_f( void ) *
* Description: 初始化DS18B20 *
* Parameter: *
* *
* Return: 返回初始化的结果(0:复位成功 1:复位失败) *
**************************************************************************/
uint8 v_Ds18b20Init_f( void )
{
uint8 Flag ;
DS18B20_DQ_HIGH //稍作延时
v_Delay10Us_f( 3 ) ;
DS18B20_DQ_LOW //总线拉低
v_Delay10Us_f( 80 ) ; //延时大于480us
DS18B20_DQ_HIGH //总线释放
v_Delay10Us_f( 15 ) ;
Flag = DS18B20_DQ_READ ; //如果Flag为0,则复位成功,否则复位失败
return Flag ;
}
/******************************************************************************
* Function: void v_Ds18b20Write_f( uint8 Cmd ) *
* Description: 向DS18B20写命令 *
* Parameter: Cmd: 所要写的命令 *
* *
* Return: *
******************************************************************************/
void v_Ds18b20Write_f( uint8 Cmd )
{
uint8 i ;
for( i = 8 ; i > 0 ; i-- )
{
DS18B20_DQ_LOW //拉低总线,开始写时序
DS18B20_DQ_READ = Cmd & 0x01 ; //控制字的最低位先送到总线
v_Delay10Us_f( 5 ) ; //稍作延时,让DS18B20读取总线上的数据
DS18B20_DQ_HIGH //拉高总线,1bit写周期结束
Cmd >>= 1 ;
}
}
/******************************************************************************
* Function: uint8 v_Ds18b20Read_f( void ) *
* Description: 向DS18B20读取一个字节的内容 *
* Parameter: *
* *
* Return: 读取到的数据 *
******************************************************************************/
uint8 v_Ds18b20Read_f( void )
{
uint8 ReadValue, i ;
for( i = 8 ; i > 0 ; i-- )
{
DS18B20_DQ_LOW
ReadValue >>= 1 ;
DS18B20_DQ_HIGH
if( DS18B20_DQ_READ == 1 )
ReadValue |= 0x80 ;
v_Delay10Us_f( 3 ) ;
}
return ReadValue ;
}
/******************************************************************************
* Function: uint16 v_Ds18b20ReadTemp_f( void ) *
* Description: 读取当前的温度数据(只保留了一位小数) *
* Parameter: *
* *
* Return: 读取到的温度值 *
******************************************************************************/
uint16 v_Ds18b20ReadTemp_f( void )
{
uint8 TempH, TempL ;
uint16 ReturnTemp ;
/* if( v_Ds18b20Init_() ) return ; //复位失败,在这里添加错误处理的代码 */
v_Ds18b20Init_f() ; /复位DS18B20
v_Ds18b20Write_f( 0xcc ) ; //跳过ROM
v_Ds18b20Write_f( 0x44 ) ; //启动温度转换
v_Ds18b20Init_f() ;
v_Ds18b20Write_f( 0xcc ) ; //跳过ROM
v_Ds18b20Write_f( 0xbe ) ; //读取DS18B20内部的寄存器内容
TempL = v_Ds18b20Read_f() ; //读温度值低位(内部RAM的第0个字节)
TempH = v_Ds18b20Read_f() ; //读温度值高位(内部RAM的第1个字节)
ReturnTemp = TempH ;
ReturnTemp <<= 8 ;
ReturnTemp |= TempL ; //温度值放在变量ReturnTemp中
return ReturnTemp ;
}
/******************************************************************************
* Function: void v_TemperatureUpdate_f( void ) *
* Description:读取当前的温度数据并转化存放在数组Temperature(只保留了一位小数) *
* Parameter: *
* *
* Return: *
******************************************************************************/
void v_TemperatureUpdate_f( void )
{
uint8 Tflag = 0 ;
uint16 TempDat ;
float Temp ;
TempDat = v_Ds18b20ReadTemp_f() ;
if( TempDat & 0xf000 )
{
Tflag = 1 ;
TempDat = ~TempDat + 1 ;
}
Temp = TempDat >> 4; (TempDat * 0.0625 ) 请大家不要用乘以,不知道为什么可以看我上面的贴子
TempDat = Temp * 10 ; ;小数部用可以用查表法,大家有什么好办法来讨论下,呵呵
Temperature[ 0 ] = Tflag ; //温度正负标志
Temperature[ 1 ] = TempDat / 100 + '0' ; //温度十位值
Temperature[ 2 ] = TempDat % 100 / 10 + '0' ; //温度个位值
Temperature[ 3 ] = TempDat % 10 + '0' ;//温度小数位
}
如果想获取当前的温度数据,在主函数中调用v_TemperatureUpdate_f( void )就可以了。温度数据就保存到Temperature中去了。至于如何显示,就不用多说了吧~__@~
时间和温度一起显示出来看看
(原文件名:7.jpg)
OK,至此ds18b20和ds1302的应用告一段落。如果有不懂的,记得多看datasheet,多交流。
原文地址:http://www.eehome.cn/read.php?tid=14139 |
|