关于测温,常用热敏电阻及专用测温芯片两种办法。热敏电阻法:用555形成振荡,测脉宽得温度;用电压比较器控制充放电,测充电时间得温度;采取电阻比率法,借助标准电阻,通过充放电时间之比,测电阻得温度。以上均须借助热敏电阻。我验收过几批热敏电阻,军级,成本是天价,姑无论也,准确度是多少呢?±10%,民用级或更而下之云。这几批热敏电阻仅用在要求不高的控温场合,总之,用热敏电阻测温,准确度不能令人满意。 18B20准确度是很好的,但它对时序要求极严。如是系统测温是首要任务,其它任务可暂时屏蔽,亦不失为一种办法,但如系统有不可屏蔽中断,18B20便高才不第,落落寡合了。 综合比较,LM75是较好的。精度0.5℃,准确度一般应有1℃(指标是2℃)。 工程师们似不太喜欢75,比起18B20的盛名,75就要黯然得多,资料也不易求得。我在网上下了一点资料,手中暂无芯片,根据资料先拟了一个驱动,(也借用了一些I2C的常用编程办法),不知是否行得通,贴出来向大家讨教。
/* 全局符号定义 */ #include <reg52.h> #include <intrins.h>
#define HIGH 1 #define LOW 0 #define FALSE 0 #define TRUE ~FALSE #define Uchar unsigned char #define Uint unsigned int #define WRITE 0x90 /* 定义LM75的器件地址SLA和方向位W ,A2 A1 A0接地*/ #define READ 0x91 /* 定义LM75的器件地址SLA和方向位R ,A2 A1 A0接地*/
Uchar EAROMImage[2]; /* 在RAM中定义存储映象单元 */ bit s_flag; Uchar temp; sbit SCL = 0x96 ; sbit SDA = 0x97 ;
/******************************************************************** * 函数原型: void delay(void); * 功 能: 本函数实际上只有一条返回指令, 在具体应用中可视具体 * 要求增加延时指令。 ********************************************************************/ void delay( void ) { ; }
/******************************************************************** * 函数原型: void I_start(void); * 功 能: 提供I2C总线工作时序中的起始位。 ********************************************************************/ void I_start( void ) { SCL = HIGH ; delay() ; SDA = LOW ; delay() ; SCL = LOW ; delay() ; }
/******************************************************************** * 函数原型: void I_stop(void); * 功 能: 提供I2C总线工作时序中的停止位。 ********************************************************************/ void I_stop( void ) { SDA = LOW ; delay() ; SCL = HIGH ; delay() ; SDA = HIGH ; delay() ; SCL = LOW ; delay() ; }
/******************************************************************** * 函数原型: void I_init(void); * 功 能: I2C总线初始化。在main()函数中应首先调用本函数, 然后再 * 调用其它函数。 ********************************************************************/ void I_init( void ) { SCL = LOW ; I_stop() ; }
/******************************************************************** * 函数原型: bit I_clock(void); * 功 能: 提供I2C总线的时钟信号, 并返回在时钟电平为高期间SDA 信 * 号线上状态。本函数可用于数据发送, 也可用于数据接收。 ********************************************************************/ bit I_clock( void ) { bit sample ; SCL = HIGH ; delay() ; sample = SDA ; SCL = LOW ; delay() ; return ( sample ) ; }
/******************************************************************** * 函数原型: bit I_send(Uchar I_data); * 功 能: 向I2C总线发送8位数据, 并请求一个应答信号ACK。如果收到* ACK应答则返回1(TRUE), 否则返回0(FALSE)。 ********************************************************************/ bit I_send( Uchar I_data ) { Uchar i ; /* 发送8位数据 */ for ( i=0 ; i<8 ; i++ ) { SDA = (bit)( I_data & 0x80 ) ; I_data = I_data << 1 ; I_clock() ; } /* 请求应答信号ACK */ SDA = HIGH ; return ( ~I_clock() ); }
/******************************************************************** * 函数原型: Uchar I_receive(void); * 功 能: 从I2C总线上接收8位数据信号, 并将接收到8位数据作为一个 * 字节返回, 不回送应答信号ACK。主函数在调用本函数之前应 * 保证SDA信号线处于浮置状态, 即使8051的P1.7脚置1。 ********************************************************************/ Uchar I_receive( void ) { Uchar I_data = 0 ; register Uchar i ; for ( i=0 ; i<8 ; i++ ) { I_data *= 2 ; if (I_clock()) I_data++ ; } return ( I_data ) ; }
/******************************************************************** * 函数原型: void I_Ack(void); * 功 能: 向I2C总线发送一个应答信号ACK, 一般用于连续数据读取时。 ********************************************************************/ void I_Ack( void ) { SDA = LOW ; I_clock() ; SDA = HIGH ; }
/******************************************************************** * 函数原型: bit E_address(Uchar Address); * 功 能: 向LM75写入器件地址和一个指定的字节地址。 ********************************************************************/ bit E_address( Uchar Address ) { I_start() ; if ( I_send( WRITE ) ) return ( I_send( Address ) ) ; else return ( FALSE ) ; }
/******************************************************************** * 函数原型: bit E_read(Uchar Address); * 功 能: 从LM75中读取两个字节的数据并转存于RAM存储映象单元, 采 * 用序列读操作方式从片内指定地址开始连续读取数据。如果 * LM75不接受指定的地址则返回0(FALSE)。 ********************************************************************/ bit E_read( Uchar Address ) { if ( E_address( Address ) ) { //如果从机地址及片内地址都写入成功 I_start() ; //发重复起始信号 if ( I_send( READ ) ) { //发读命令 EAROMImage[0] = ( I_receive() ) ; I_Ack() ;//主机应答,通知从机继续发出数据 EAROMImage[1] = ( I_receive() ) ; I_clock() ; I_stop() ; //主机不再应答并停止总线 return ( TRUE ) ; } else { I_stop() ; return ( FALSE ) ; } } else I_stop() ; return ( FALSE ) ; }
/******************************************************************** * 函数原型: void wait_5ms(void); * 功 能: 提供5ms延时(时钟频率为12MHz)。 ********************************************************************/ void wait_5ms( void ) { int i ; for ( i=0 ; i<1000 ; i++ ) { ; } }
/******************************************************************** * 函数原型: bit E_write(Uchar Address); * 功 能: 将RAM存储映象单元中的两个数据写入到LM75的指定地址单 * 元。采用字节写操作方式。如果LM75不接受指定的地址或某个* 传送的字节未收到应答信号ACK, 则返回0(FALSE)。 ********************************************************************/ bit E_write(Uchar Address) { if ( E_address(Address) && I_send( EAROMImage[0])) { //如果从机地址及片内地址都写入正确且成功发送了一个数据 if(I_send(EAROMImage[1])){ //则继续发送下一个数据 I_stop() ; wait_5ms(); return (TRUE); } else{ I_stop(); return ( FALSE ) ; } } else{ I_stop(); return (FALSE); } }
void main() { I_init(); /* I2C 总线初始化 */ E_read(0x00); //读温度寄存器的内容 if(EAROMImage[0]&0x80) s_flag=1; else s_flag=0; //符号判断 temp=EAROMImage[0]<<1|EAROMImage[1]>>7; //数据拼装 if(s_flag) temp=~temp+1; //如果是负温度则取其绝对值 while(1); }
|