基于C51的温控报警系统
1 环境
开发板:HC6800-ES
MCU:STC89C52
温度传感器:DS18B20
蜂鸣器:ULN2003
2 设备驱动原理
2.1 温度传感器DS18B20
电路图如下:
DS18B20的功能很强大,包含64位ROM,9字节RAM(暂存器),有下面两种驱动模式。
a 多片驱动
意思就是多个DS18B20并联在一起,通过一根数据总线和所有的传感器通信,传感器的光刻ROM里包含64位的数据,可以当成传感器的物理地址,MCU每次发送数据都要先和对应的传感器进行握手,也就是发送传感器的物理地址。
b 单片驱动
只需要对一个传感器进行控制,这时的流程就可以忽略握手,直接进行控制命令的发送。
传感器读到的温度数据被保存到9位RAM的前两个字节,低字节在前,高字节在后,低位在前,高位在后。每一位的数据含义可以用下图展示。
举几个例子:
对传感器进行控制的命令如下:
我这次使用单片驱动模式,主要流程如下:
2.2 蜂鸣器
蜂鸣器包含两种,一种无源蜂鸣器,一种有源蜂鸣器。
无源蜂鸣器:无震荡源,需要外部施加震荡电平才能正常工作。
有源蜂鸣器:有震荡源,外部只需要施加控制电平就可以改变音调和音高。
本次实验用到的ULN为无源蜂鸣器,但是因为报警只需要发出简单的提示音,所以只需要往ULN2003的数据端发送一定频率的高低电平即可。
电路如下
2.3 ULN2003高耐压、大电流复合晶体管阵列
驱动蜂鸣器、电机等设备需要较大的电流,直接使用C51的端口无法有效驱动。所以加上了一个ULN2003器件,ULN2003本质上是级联了多个三极管进行放大的放大电路,包含7个输入和7个输出,能对7个输入进行放大。
电路图如下所示
2.4 数码管、74HC573
数码管用来显示温度,也可以直接将C51输出端口接数码管输入端,这里用到74HC573锁存器进行驱动,他有两个主要作用。
1、 锁存作用,也就是缓存作用,当LE为低电平的时候,输出端口不随输入端口变化,输入端数据全部被缓存,可以有效避免输入变化导致数码管闪烁。
2、 驱动作用,74HC573可以输出更大的电流,也就可以驱动更多的设备。
74LS138也就是38译码器,将三位数据转码成8位数据,数据的含义不同,3位输入数据可以是1和0的任意组合,8位输出只有一位能为0,其他都为1,这里用于选中8位数码管中的一位,配合74HC573进行一位数据的准确显示。.
3 代码
#include"reg52.h"
#include"intrins.h"
//#define ENABLE_AUDIO
typedef unsigned char U8;
typedef unsigned int U16;
sbit bzClock=P1^5;
typedef unsigned char u8;
typedef char c8;
typedef unsigned int u16;
typedef int i16;
u8 num_code[18]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0};//显示0~F,-1,无的值
sbit L_A=P2^2;
sbit L_B=P2^3;
sbit L_C=P2^4;
sbit P_Tempe=P3^7;
#ifdef DEBUG
sbit Point1=P2^0;
sbit Point2=P2^1;
sbit Point3=P2^2;
#endif
u8 hdata_,ldata_;
u8 isDisplay=1;
u8 isDangerous=0;
u16 timeCount=0;
//10us
void delay(u16 size)
{
while(size--);
}
void delay10us(void) //误差 0us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
void delay15us(void) //误差 0us
{
unsigned char a;
for(a=6;a>0;a--);
}
void Delay1ms(i16 y)
{
i16 x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}
void delay45us(void) //误差 0us
{
unsigned char a;
for(a=21;a>0;a--);
}
void select(c8 index)
{
if(index<0 || index> 8)
{
return;
}
L_C=(index-1)/4;
L_B=(index-1)%4/2;
L_A=(index-1)%2;
}
void display(c8 num,u8 isPot)
{
if(num<0 || num>16)
{
P0=num_code[17];
return;
}
if(!isPot)
{
P0=num_code[num];
}
else{
P0=num_code[num] | 0x80;
}
delay(100);
}
//最多小数点后4位,最多3位整数,十进制输出,有符号,暂时省略范围检查
void display_all(u8 isPositive,u16 int_part,u16 decimal_part)
{
c8 num_display[8]={0};
u8 index_pot=4;
c8 i=0;
#ifdef DEBUG
while(1);
return;
#endif
#ifdef ENABLE_AUDIO
if(int_part>=40 && isPositive)
{
isDangerous=1;
}else
{
isDangerous=0;
}
#endif
if(decimal_part>=10000)
{
return;//error
}
if(int_part>=10000)
{
return;//error
}
if(isPositive)
{
num_display[7]=-1;
}else{
num_display[7]=16;
}
num_display[6]=int_part/100;
num_display[5]=int_part%100/10;
num_display[4]=int_part%10;
if(decimal_part>=1000 && decimal_part<10000)
{
num_display[3]=decimal_part/1000;
num_display[2]=decimal_part%1000/100;
num_display[1]=decimal_part%1000%100/10;
num_display[0]=decimal_part%10;
}
else if(decimal_part>=100 && decimal_part<1000)
{
num_display[3]=decimal_part%1000/100;
num_display[2]=decimal_part%1000%100/10;
num_display[1]=decimal_part%10;
num_display[0]=-1;
}
else if(decimal_part>=10 && decimal_part<100)
{
num_display[3]=decimal_part%1000%100/10;
num_display[2]=decimal_part%10;
num_display[1]=-1;
num_display[0]=-1;
}
else if(decimal_part>=0 && decimal_part<10)
{
num_display[3]=decimal_part%10;
num_display[2]=-1;
num_display[1]=-1;
num_display[0]=-1;
}
while(isDisplay)
{
for(i=7;i>=0;i--)
{
if(i>7 || i<0)
{
i=7;
}
display(-1,0);
select(i+1);
if(i==index_pot)
{
display(num_display[i],1);
}
else
{
display(num_display[i],0);
}
}
//关键问题:为什么后面的循环不行,错误原因在于i的类型为u8,改为c8后没问题,所以可能是i--后i还大于0.所以不停循环了。
//不知道为什么,几十在循环里面做出判断如果i不在区间时强制设为7,也不行
#ifdef ENABLE_AUDIO
if(isDangerous)
{
bzClock=~bzClock;
}
#endif
}
return;
}
u8 getDataByte()
{
u8 retValue=0;
c8 i=8;
u8 ubit;
while(i--)
{
i16 threshold=0;
P_Tempe=0;
_nop_;//不能浮动,10um就无法测出有效值
P_Tempe=1;
delay10us();//不能浮动,2um就无法测出有效值
ubit=P_Tempe;
retValue>>=1;
retValue=retValue | (ubit<<7);
delay45us();//时间不需要准确,可以浮动(测试100us也行),应该是DS18B20会判断电平升降再去返回值
}
return retValue;
}
u8 initTempe()
{
i16 threshold=0;
u8 isSucess=0;
P_Tempe=0;
delay(72);//480-960
P_Tempe=1;
delay(8);
//detect 0
threshold=6;
while(threshold--)
{
delay(1);
if(P_Tempe==0)
{
#ifdef DEBUG
Point1=0;
#endif
isSucess=1;
break;
}
}
delay(48);
return isSucess;
#ifdef DEBUG
Point1=1;
#endif
return isSucess;
}
void setmode()
{
//default mode,
}
void sendCommand(u8 command)
{
u8 i=0;
for(i=0;i<8;i++)
{
P_Tempe=0;
delay15us();//必须准确,如果为20us则无法发出有效值
P_Tempe=command&0x1;
command>>=1;
delay(6);//可以变化,到100us也行,注意和初始化区别,初始化至少480us,这里如果到20us就不行了
P_Tempe=1;
}
}
void begainCheck()
{
initTempe();//重要,每次发命令前都要初始化
Delay1ms(1);
//44H -> 1000100
sendCommand(0xCC);//重要,每次发命令都要判断是否制定器件发送
sendCommand(0x44);
}
void getData()
{
initTempe();
Delay1ms(1);
sendCommand(0xCC);
sendCommand(0xBE);
ldata_=getDataByte();//从低位字节开始读取,读到的是原始数据,需要解析每一位的含义,转换成十进制数据
hdata_=getDataByte();
}
void displayTempe()
{
u8 isPst=0,ldata=0,hdata=0;//局部变量一定要初始化,因为可能有意想不到的后果,我在这里没有初始化,结果第二次得到的值是第一次的两倍。
u16 intPart=0,decPart=0;
u16 compose=0,u16hdata=0;
hdata=hdata_;
ldata=ldata_;
#ifdef DEBUG
#ifdef DEBUG_PROC
return;
#endif
P2=hdata_;
#endif
if((hdata&0xf8)==0
//&& !(ldata&0xf0)
)//+
{
#ifdef DEBUG
//Point2=0;
#endif
isPst=1;
intPart+=ldata >> 4;
intPart+=hdata << 5 >>1;
decPart+=(ldata&0x08?5000:0);
decPart+=(ldata&0x04?2500:0);
decPart+=(ldata&0x02?1250:0);
decPart+=(ldata&0x01?625:0);
display_all(isPst,intPart,decPart);
}else if((hdata&0xf8)==0xf8
//&& (ldata&0xf0)
)
{
#ifdef DEBUG
//Point3=0;
#endif
u16hdata=hdata;
compose=u16hdata>>8+ldata;
compose=-1;
compose=~compose;
isPst=0;
hdata=(u8)compose>>8;//强制转换
ldata=(u8)compose;
intPart+=ldata >> 4;
intPart+=hdata << 5 >>1;
decPart+=(ldata&0x08?5000:0);
decPart+=(ldata&0x04?2500:0);
decPart+=(ldata&0x02?1250:0);
decPart+=(ldata&0x01?625:0);
display_all(isPst,intPart,decPart);
}else
{
display_all(1,0,0);
}
}
void initTimer()//定时5s
{
EA=1;
TR0=1;
ET0=1;
TMOD|=0x01;
TH0=0xFC;
TL0=0x18;
}
void onInterrupt() interrupt 1
{
timeCount++;
TH0=0xFC;
TL0=0x18;
if(timeCount>=5000)
{
isDisplay=0;//C51单线程,不需要线程同步
timeCount=0;
}
}
int main()
{
initTimer();
while(1)
{
begainCheck();
Delay1ms(1000);//750ms
getData();
isDisplay=1;
displayTempe();
}
return 0;
}
4 展示