【自学嵌入式:51单片机】实现DS18B20温度报警器
目录
说明
本文是自学江科大51单片机的学习笔记,我利用I2C通信与AT24C02进行通信,存储温度的最高阈值和最低阈值,利用单总线协议与温度传感器DS18B20进行通信,利用定时器扫描按键,利用LCD1602显示当前温度和温度的阈值,超出最高温度或者低于最低温度的阈值就会在屏幕上显示OV:H或者OV:L,为了能够不干扰单总线通信,I2C通信,我把之前做的单总线通信和I2C通信的代码加入了通信时临时关闭中断的功能,按键扫描不需要精准控制时间,所以通信时关闭中断也问题不大,这样不会造成屏幕闪烁,代码开源到:https://gitee.com/qin-ruiqian/ds18b20-temperature-alarm
电路
对应普中开发板的电路如下:

代码
代码思路全在注释中,现展示如下:
main.c
#include <REGX52.H>
#include "DS18B20.h"
#include "LCD1602.h"
#include "AT24C02.h"
#include "Key.h"
float T,Tshow; //温度
char TLow, THigh; //报警的最低温度和最高温度
unsigned char KeyNum; // 按键键码值
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
//初始化定时器0
void initTimer0()
{
TMOD = 0x01;
TH0 = 0xFC; //11.0592Mhz,高8位
TL0 = 0x66; //低8位,注意大小端模式别写反了
EA = 1; //允许定时器中断
ET0 = 1;
TF0 = 0; //清除TF0标志
TR0 = 1; // 开启定时器0
}
//定时器0中断,只单纯中断按键,那么单总线通信这种微秒级别的会被打断
//此时要在单总线发送比特位的时候,加入中断的关与开
//发信息的时候,中断必须关闭
//同理I2C那里也是
//因为是按键扫描,按键扫描没有那么严格的时间要求
void Timer0_Rountine() interrupt 1
{
static unsigned int T0Count1; //轮询检测按键值的变量
TH0 = 0xFC; //11.0592Mhz
TL0 = 0x66;
T0Count1++;
if(T0Count1>=20)
{
T0Count1 = 0;
Key_Loop(); //每次定时器检测按键的键码
//中断函数里调用的函数在main中不能调用
}
}
void main()
{
LCD1602_Init();
//上电还是加个提醒吧,提示加载中
LCD1602_ShowString(0,0,"Now Loading");
//定时器初始化
initTimer0();
//上电读取温度,延迟1s,否则读取默认值85
DS18B20_ConvertT();
Delay(1000);
//上电的时候默认从EEPROM读取最高温度和最低温度
THigh = readByteE2PROM(0);
TLow = readByteE2PROM(1);
//如果存的是非法数据,自动修正,并存进去
if(THigh > 125 || TLow < -55 || THigh <= TLow)
{
THigh = 20;
TLow = 15;
writeByteE2PROM(0, THigh);
Delay(5);
writeByteE2PROM(0, TLow);
Delay(5);
}
LCD1602_ShowString(0,0," ");
LCD1602_ShowString(0,0,"T:");
LCD1602_ShowString(1,0,"TH:");
LCD1602_ShowString(1,8,"TL:");
//按键按下之前提前显示一下
LCD1602_ShowSignedNum(1, 3, THigh, 3);
LCD1602_ShowSignedNum(1, 11, TLow, 3);
while(1)
{
KeyNum = GetKey(); //用定时器扫描按键
//温度读取及显示
DS18B20_ConvertT();
T=DS18B20_ReadT();
Tshow = T;
if(T<0)
{
LCD1602_ShowChar(0, 2, '-');
Tshow = -Tshow;
}
else
{
LCD1602_ShowChar(0, 2, '+');
}
LCD1602_ShowNumWithLength(0,3, Tshow, 3);
LCD1602_ShowChar(0,6, '.');
//显示2位小数
LCD1602_ShowNumWithLength(0,7, (unsigned int)(Tshow*100) % 100, 3);
//阈值判断及显示
//判断按键是否按下
if(KeyNum)
{
if(KeyNum==1)
{
THigh++;
if(THigh >125)
{
THigh = 125;
}
}
if(KeyNum==2)
{
THigh--;
//不能让最高温度比最低温度小
if(THigh <= TLow)
{
THigh++;
}
}
if(KeyNum==3)
{
TLow++;
//不能让最低温度超过最高温度
if(TLow>=THigh)
{
TLow--;
}
}
if(KeyNum==4)
{
TLow--;
if(TLow<-55)
{
TLow = -55;
}
}
LCD1602_ShowSignedNum(1, 3, THigh, 3);
LCD1602_ShowSignedNum(1, 11, TLow, 3);
//把最高温度和最低温度写入AT24C02芯片中
writeByteE2PROM(0, THigh); //向0号地址EEPROM写THigh
Delay(5); //等5ms写入
writeByteE2PROM(1, TLow); //向1号地址写TLow
Delay(5); //等5ms写入
}
if(T>THigh)
{
LCD1602_ShowString(0, 12, "OV:H");
}
else if (T<TLow)
{
LCD1602_ShowString(0, 12, "OV:L");
}
else
{
LCD1602_ShowString(0, 12, " ");
}
}
}
OneWire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
unsigned char OneWire_Init(void); // 初始化单总线通信
void OneWire_SendBit(bit Bit); //发送一位
bit OneWire_ReceiveBit(); //接收一位
void OneWire_SendByte(unsigned char Byte); //发送一个字节
unsigned char OneWire_ReceiveByte(void); //接收一个字节
#endif
OneWire.c
#include <REGX52.H>
#include <intrins.h> // 延时空语句
sbit OneWire_DQ = P3^7;
void Delay500us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 227;
while (--i);
}
void Delay70us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 29;
while (--i);
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Delay50us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 20;
while (--i);
}
void Delay5us() //@11.0592MHz
{
}
unsigned char OneWire_Init(void)
{
bit EA_State = EA; //获取当前中断状态
bit AckBit = 0; //确认帧
EA = 0; //关闭中断
OneWire_DQ = 1;
OneWire_DQ = 0;
Delay500us(); //根据单总线通信协议,主机拉低至少480微秒,这里用500微秒
OneWire_DQ = 1;//然后再释放
Delay70us(); //等待15-60微秒后,存在的从机会拉低总线60-240微秒以响应主机,所以要延迟60~(60+240)微秒
//延迟70微秒,包括了等待,和从机响应主机的时间
AckBit = OneWire_DQ;
//要求主机将总线拉低至少480微秒,继续延迟500微秒
Delay500us();
EA = EA_State;
return AckBit;
}
//发送一位
void OneWire_SendBit(bit Bit)
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断
//主机将总线拉低60~120us,然后释放总线,表示发送0
//主机将总线拉低1~15us,然后释放总线,表示发送1
//从机将在总线拉低30us后(典型值)读取电平
//整个时间片应大于60us
OneWire_DQ = 0; //先拉低
//在10us的时候检测一下,如果是0,那就一直是0
//如果是1,那就拉高(释放总线)
//直接写OneWire_DQ = Bit;
//Bit是0就是0,Bit为1,就在1-15us(也就是我设定的10us)拉高为1
//但是考虑到调用一个函数是4us
//江科大版本在这里直接把函数体的内容拿过来
//比如软件延时功能写出来的函数,延迟10us
//是算上了调用函数的时间,实际函数体6us,调用函数6us
//江科大这里是把函数体的内容拿过来
//这样不能用生成的延迟10us的函数(因为函数体6us)
//所以他在视频中用延迟14us的函数的函数体
//但是我这里还是正常调用延迟10us函数,包括函数体和调用过程
Delay10us();
OneWire_DQ = Bit;
//整个时间片应大于60us(至少60us)
//所以再延迟个50us,这个时序就结束了(50+10=60)us
Delay50us();
//通信结束,弱上拉拉回去
OneWire_DQ = 1;
EA = EA_State;
}
//接收一位
bit OneWire_ReceiveBit()
{
bit EA_State = EA; //获取当前中断状态
//主机将总线拉低1~15us,然后释放总线
//并在拉低后15us内读取总线电平(尽量贴近15us的末尾)
//读取为低电平则为接收0
//读取为高电平则为接收1
//整个时间片应大于60us
bit Bit;
EA = 0; //关闭中断
//先拉低
OneWire_DQ = 0;
Delay5us();
//再释放
OneWire_DQ = 1; //拉高就是释放总线,弱上拉,从机传来1就是1,从机传来是0瞬间拉低
Delay5us();
//15us内,目前是总共延迟10us内,读取总线电平
Bit = OneWire_DQ;
//时间片至少60us,之前有10us,还需要延迟50us
Delay50us();
//通信结束后,不用主机释放总线,从机释放总线
EA = EA_State;
return Bit;
}
//发送一个字节
void OneWire_SendByte(unsigned char Byte)
{
bit EA_State = EA; //获取当前中断状态
unsigned char i;
EA = 0; //关闭中断
for(i = 0; i <8 ;i++)
{
OneWire_SendBit(Byte & (0x01<<i)); //依次左移取位,从最低位开始取
}
EA = EA_State;
}
//接收一个字节
unsigned char OneWire_ReceiveByte(void)
{
bit EA_State = EA; //获取当前中断状态
unsigned char Byte = 0x00;
unsigned char i;
EA = 0; //关闭中断
for(i = 0; i <8 ;i++)
{
//如果传过来1位1,就和0x00做或运算,把1写入每一位
if(OneWire_ReceiveBit())
{
Byte |= (0x01 << i);
}
}
EA = EA_State;
return Byte;
}
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
#define uint8 unsigned char
#define uint16 unsigned int
void LCD1602_WriteCommand(uint8 command); //写指令到LCD1602
void LCD1602_WriteData(uint8 in_data); //LCD1602写数据
void LCD1602_Init(); //LCD1602初始化
void LCD1602_WritePos(uint8 Line,uint8 Column); //设置光标位置
void LCD1602_ShowChar(uint8 Line,uint8 Column,char Char); //显示字符
void LCD1602_ShowString(uint8 Line,uint8 Column,char* String); //显示字符串
void LCD1602_ShowNum(uint8 Line, uint8 Column, uint8 number); //显示数字
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); //显示16进制数
void LCD1602_ShowNumWithLength(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); //在LCD1602指定位置开始显示所给数字
void LCD1602_ClearPos(uint8 Line, uint8 Column); //清空指定位置
void LCD1602_ClearPosLength(uint8 Line, uint8 Column, uint8 Length); //从指定位置清空这一行一定长度的屏幕
void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length); //指定长度显示有符号数
#endif
LCD1602.c
#include <REGX52.H>
void LCD1602_Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
//写指令到LCD1602
void LCD1602_WriteCommand(unsigned char command)
{
P2_6 = 0; // RS端低电平,指令模式
P2_5 = 0; // WR端低电平,写模式
P0 = command; // 传输对应指令到端口
P2_7 = 1; // 使能端高电平,LCD屏幕开始工作
LCD1602_Delay(1); // 延迟1ms,让指令送进去
P2_7 = 0; // 使能端低电平,LCD不接收任何信号
LCD1602_Delay(1); // 延迟1ms,让LCD稳定进入不接收信号状态
}
//LCD1602写数据
void LCD1602_WriteData(unsigned char in_data)
{
P2_6 = 1; // RS端高电平,数据模式
P2_5 = 0; // WR端低电平,写模式
P0 = in_data; // 传输对应数据到端口
P2_7 = 1; // 使能端高电平,LCD屏幕开始工作
LCD1602_Delay(1); // 延迟1ms,让指令送进去
P2_7 = 0; // 使能端低电平,LCD不接收任何信号
LCD1602_Delay(1); // 延迟1ms,让LCD稳定进入不接收信号状态
}
//LCD1602初始化
void LCD1602_Init()
{
LCD1602_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD1602_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD1602_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD1602_WriteCommand(0x01);//光标复位,清屏
}
// 1xxx xxxx设定要存入数据的地址,其中xxx xxxx是地址
//设置光标位置
void LCD1602_WritePos(unsigned char Line,unsigned char Column)
{
//第一行地址从00H到27H
//若要将光标设置在第一行第一列,则发送0x80(1000 0000)指令;
//其中最高位1代表设置光标位置,剩下的七个0代表实际地址
if(Line==0)
{
//0x80
LCD1602_WriteCommand(0x80|Column); //用或,把实际地址放在后7位
}
else if(Line==1) //第二行地址从40H到67H
{
LCD1602_WriteCommand(0x80|(Column+0x40));
}
}
//显示字符
void LCD1602_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD1602_WritePos(Line, Column);
LCD1602_WriteData(Char);
}
//显示字符串
void LCD1602_ShowString(unsigned char Line,unsigned char Column,char* String)
{
unsigned char i = 0; //循环用变量
LCD1602_WritePos(Line, Column);
for(; String[i] != '\0'; i++)
{
LCD1602_WriteData(String[i]);
}
}
//显示数字
void LCD1602_ShowNum(unsigned char Line, unsigned char Column, unsigned char number)
{
//8位无符号整数,最大255,考虑百位
//如果百位不为0,先写百位
unsigned char hundred = number / 100 % 10;
unsigned char ten = number / 10 % 10;
unsigned char one = number % 10;
LCD1602_WritePos(Line, Column);
//从'0'代表的ASCII码开始的,所以要加0,才能找到对应数字的ASCII码
if(hundred != 0)
{
LCD1602_WriteData(hundred+'0');
}
if(!(ten == 0 && hundred == 0))
{
LCD1602_WriteData(ten+'0');
}
LCD1602_WriteData(one+'0');
}
//取x的y次方
int LCD_Pow(int x,int y)
{
unsigned char i;
int Result = 1;
for(i=0;i<y;i++)
{
Result *=x;
}
return Result;
}
//显示16进制数
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
unsigned char SingleNumber;
LCD1602_WritePos(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber = Number/LCD_Pow(16,i-1)%16; //从高位到低位,取出每一个十六进制数字
if(SingleNumber < 10)
LCD1602_WriteData('0'+SingleNumber);
else
LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x
}
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD1602_ShowNumWithLength(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD1602_WritePos(Line,Column);
for(i=Length;i>0;i--)
{
LCD1602_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
//清空指定位置
void LCD1602_ClearPos(unsigned char Line, unsigned char Column)
{
LCD1602_WritePos(Line, Column);
LCD1602_WriteData(0x20);
}
//从指定位置清空这一行一定长度的屏幕
void LCD1602_ClearPosLength(unsigned char Line, unsigned char Column, unsigned char Length)
{
unsigned char i = 0;
if((Column + Length) > 16)
{
Length = 16 - Column;
}
for(; i<Length; i++)
{
LCD1602_ClearPos(Line, Column + i);
}
}
//指定长度显示有符号数
void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD1602_WritePos(Line,Column);
if(Number>=0)
{
LCD1602_WriteData('+');
Number1=Number;
}
else
{
LCD1602_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD1602_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
key.h
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key(); //获取按键的键码
void Key_Loop(void); //中断时候获取按键的循环
unsigned char GetKey(); ////获取按键,每次按下后,按键键码值清零
#endif
key.c
#include <REGX52.H>
unsigned Key_KeyNumber;
void Delay20ms() //@11.0592MHz
{
unsigned char i, j;
i = 36;
j = 217;
do
{
while (--j);
} while (--i);
}
//直接获取按键状态
unsigned char Key_GetState()
{
unsigned char KeyNumber = 0;
if(P3_1==0){KeyNumber=1;}
if(P3_0==0){KeyNumber=2;}
if(P3_2==0){KeyNumber=3;}
if(P3_3==0){KeyNumber=4;}
return KeyNumber;
}
//消抖获取按键状态
unsigned char Key()
{
unsigned char KeyNumber = 0;
if(P3_1==0){Delay20ms();while(P3_1==0);Delay20ms();KeyNumber=1;}
if(P3_0==0){Delay20ms();while(P3_0==0);Delay20ms();KeyNumber=2;}
if(P3_2==0){Delay20ms();while(P3_2==0);Delay20ms();KeyNumber=3;}
if(P3_3==0){Delay20ms();while(P3_3==0);Delay20ms();KeyNumber=4;}
return KeyNumber;
}
//获取按键,每次按下后,按键键码值清零
unsigned char GetKey()
{
unsigned char Temp = 0;
Temp = Key_KeyNumber;
Key_KeyNumber = 0;
return Temp;
}
//用定时器扫描按键
void Key_Loop(void)
{
static unsigned char NowState, LastState; //当前按键状态,上一个按键状态
LastState = NowState;
NowState = Key_GetState();
//上一个状态按键1按下,现在没有按键按下,此时属于刚按下1后松手状态
if(LastState == 1 && NowState ==0)
{
Key_KeyNumber = 1;
}
if(LastState == 2 && NowState ==0)
{
Key_KeyNumber = 2;
}
if(LastState == 3 && NowState ==0)
{
Key_KeyNumber = 3;
}
if(LastState == 4 && NowState ==0)
{
Key_KeyNumber = 4;
}
}
I2C.h
#ifndef __I2C_H__
#define __I2C_H__
void delayI2C(); //I2C专用延时程序
void initI2C(); //初始化I2C通信
void closeI2C(); //关闭I2C通信
void writeByteI2C(unsigned char I2C_Byte); //写入一个字节
unsigned char readByteI2C(); //读一个字节
bit readAckI2C(); //读取I2C总线应答信号
void writeAckI2C(bit Ack_bit); //发送应答信号
#endif
I2C.c
#include <REGX52.H>
#include <intrins.h> // 延时空语句
sbit I2C_SCL = P2^1;
sbit I2C_SDA = P2^0;
//I2C通信专用延时程序
void delayI2C()
{
_nop_();
_nop_();
_nop_();
_nop_();
}
//初始化I2C通信
void initI2C()
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断,防止通信被中断打断
//先都拉高成高电平
I2C_SCL = 1;
I2C_SDA = 1;
//下降沿
delayI2C();
I2C_SDA = 0;
delayI2C();
I2C_SCL = 0;
EA = EA_State; //结束后恢复原来的状态
}
//关闭I2C通信
void closeI2C()
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断,防止通信被中断打断
//先都拉低
I2C_SCL = 0;
I2C_SDA = 0;
delayI2C();
I2C_SCL = 1; //先拉高I2C_SCL
delayI2C();
I2C_SDA = 1; //I2C_SCL高电平期间,再拉高I2C_SDA,这样就产生了一个上升沿
delayI2C();
EA = EA_State; //结束后恢复原来的状态
}
//I2C写入一个字节,I2C_Byte是待传送的数据
void writeByteI2C(unsigned char I2C_Byte)
{
bit EA_State = EA; //获取当前中断状态
unsigned char i = 0;
EA = 0; //关闭中断,防止通信被中断打断
for(i = 0; i<8; i++)
{
//I2C_Byte & 0x80(1000 0000)
//获取最高位,看是1还是0
if(I2C_Byte & 0x80)
{
I2C_SDA = 1;
}
else
{
I2C_SDA = 0;
}
delayI2C();
I2C_SCL = 1; //拉高电平,发送数据
delayI2C();
I2C_SCL = 0; //拉低,发送完成
I2C_Byte <<= 1; //左移,循环读取每一位,让每一位都成为首位
}
EA = EA_State; //结束后恢复原来的状态
}
// I2C读一个字节
unsigned char readByteI2C()
{
bit EA_State = EA; //获取当前中断状态
unsigned char i, Byte = 0x00;
EA = 0; //关闭中断,防止通信被中断打断
for(i = 0; i<8; i++)
{
I2C_SDA = 1; //拉高I2C_SDA,释放I2C总线
//读总线之前一定要将I2C_SDA拉高
I2C_SCL = 1; //然后将I2C_SCL拉高,准备接收I2C_SDA状态
delayI2C();
if(I2C_SDA)
{
Byte |= (0x80>>i); //依次循环右移,得到一个字节(0x00分别或每一位得到,如果是低电平,不需要操作,该位就是0)
}
I2C_SCL = 0;
}
EA = EA_State; //结束后恢复原来的状态
return Byte;
}
//读取I2C总线应答信号
bit readAckI2C()
{
bit EA_State = EA; //获取当前中断状态
bit Ack_bit; //应答信号
EA = 0; //关闭中断,防止通信被中断打断
I2C_SDA = 1; //拉高I2C_SDA,释放I2C
delayI2C();
I2C_SCL = 1; // 把I2C_SCL拉高,第9个时钟信号
delayI2C();
Ack_bit = I2C_SDA; //读取应答信号
delayI2C();
I2C_SCL = 0; //拉低I2C_SCL,结束接收应答
EA = EA_State; //结束后恢复原来的状态
return Ack_bit;
}
//I2C发送应答位
void writeAckI2C(bit Ack_bit)
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断,防止通信被中断打断
//根据应答状态变量来确定I2C_SDA是1还是0
if(Ack_bit)
{
I2C_SDA = 1;
}
else
{
I2C_SDA = 0;
}
//第9个时钟周期
//拉高I2C_SCL,再拉低I2C_SCL,完成一次发送应答信号
I2C_SCL = 1;
I2C_SCL = 0;
EA = EA_State; //结束后恢复原来的状态
}
DS18B20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
void DS18B20_ConvertT(void); // 转换温度
float DS18B20_ReadT(void); // 读取温度
#endif
DS18B20.c
#include <REGX52.H>
#include "OneWire.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
//转换温度
void DS18B20_ConvertT(void)
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断,防止通信被中断打断
OneWire_Init(); //不做确认帧处理
OneWire_SendByte(DS18B20_SKIP_ROM); //发送SKIP ROM,因为就一个设备,简单测试一下
OneWire_SendByte(DS18B20_CONVERT_T);
EA = EA_State;
}
//读取温度,4位小数
//51单片机对浮点数运算特别慢
//但是此处只是读取温度,这点时间还是耗得起
float DS18B20_ReadT(void)
{
bit EA_State = EA; //获取当前中断状态
unsigned char TLSB, TMSB;
int Temp; //有符号16位int做中间变量
float T; //最终的温度
EA = 0; //关闭中断,防止通信被中断打断
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
//发送完这个指令,就交给从机
//开始接收从机发来的指令
//传温度的数据,两个字节,16位
TLSB = OneWire_ReceiveByte(); //Temperature LSB
TMSB = OneWire_ReceiveByte(); //Temperature MSB
Temp = (TMSB<<8)|TLSB; //强制转换为有符号的16位int,把TMSB左移8位到高位,然后TLSB和低8位0做或运算,放到低位
//除16,向右移4位,见本文的温度存储格式章节
T = Temp / 16.0;
EA = EA_State;
return T;
}
AT24C92.h
#ifndef __AT24C02_H__
#define __AT24C02_H__
void writeByteE2PROM(unsigned char addr, unsigned char dat);
unsigned char readByteE2PROM(unsigned char addr);
#endif
AT24C02.c
#include <REGX52.H>
#include "I2C.h"
//向EEPROM中写入一个字节,addr是地址
void writeByteE2PROM(unsigned char addr, unsigned char dat)
{
bit EA_State = EA; //获取当前中断状态
EA = 0; //关闭中断,防止通信被中断打断
//启动信号
initI2C();
//写入设备地址和写数据位
writeByteI2C(0x50<<1); //寻址器件,后续为写操作,左移1位,右侧低位多出来的0是读写位的写入0
//接收应答位
readAckI2C();
//写入寄存器地址
writeByteI2C(addr);
//接收应答位
readAckI2C();
//写入数据
writeByteI2C(dat);
//接收应答位
readAckI2C();
//停止信号
closeI2C();
EA = EA_State;
}
//读取EEPROM中的一个字节,addr是地址
unsigned char readByteE2PROM(unsigned char addr)
{
bit EA_State = EA; //获取当前中断状态
unsigned char dat = 0;
EA = 0; //关闭中断,防止通信被中断打断
//启动信号
initI2C();
//写入设备地址和写数据位
writeByteI2C(0x50<<1);
//接收应答位
readAckI2C();
//写入寄存器地址
writeByteI2C(addr);
//接收应答位
readAckI2C();
//这里不要有停止信号,看上面的示意图
//closeI2C();
//启动信号
initI2C();
//写入设备地址和写数据位(最低位为1,也就是读写位为1,读数据)
writeByteI2C((0x50<<1)|0x01);
//接收应答位
readAckI2C();
dat = readByteI2C(); //读取一个字节数据
//发送应答位
writeAckI2C(1);//这里应答位要发送1
//停止信号
closeI2C();
///////
EA = EA_State;
return dat;
}
浙公网安备 33010602011771号