
关键词:STCC52、LCD1602、DS18B20
简易数字温度计设计
1系统设计
1.1设计任务
设计一数字温度测量系统,能自动实现实际温度的测量与显示。
1.2设计要求
1.2.1基本功能
(1)测温范围-30℃~+120℃。
(2)测量误差在±0.5℃之内。
(3)能正常显示测量的温度。
1.2.2扩展功能
(1)增加温控功能,并可修改设置温控的上下限。
(2)增加温控报警功能。
2系统方案论证
本系统主要由主控模块、显示模块、按键模块、蜂鸣器模块、LED模块、传感器模块组成,下面分别论证这几个模块的选择。
2.1主控模块的论证与选择
方案一:ATC52单片机是一种高性能8位单片微型计算机。内核本身具有丰富的指令集,足够实现本次作品的全部基本功能和部分拓展功能,相比Atmega16我们对ATC52更为熟悉,且芯片价格较低,性价比高。
方案二:采用ATmega16芯片作为主控芯片。由于对芯片的不熟悉,导致如果想要实现温度计全部基本功能和部分拓展功能,较为困难。
综上所述,选择方案一。
2.2显示模块的论证与选择
方案一:采用LCD1602显示。LCD1602相对便宜,虽然其本身不能显示中文,但是温度计只需显示数字和一些英文,符合这次设计要求。
方案二:采用LCD128显示。LCD128屏幕显示细腻直观,且可以显示中文,但是相对昂贵。
综上所述,选择方案一。
2.3按键模块的论证与选择
方案一:采用4X4矩阵按键。尽管按键一目了然,但连线复杂,扫描过程烦琐,会耗费大量的系统资源。
方案二:按键,我们只使用了两个按键来调节上限,大大节省了系统硬件资源,便于系统扩展。
综上所述,选择方案二。
3系统理论分析与计算
3.1 DS18B20的理论分析与计算
DS18B20 通过编程,可以实现最高 12 位的温度存储值,在寄存器中,以补码的格式存储,如图1所示。
图1 DS18B20 温度数据格式
寄存器一共 2 个字节,LSB 是低字节,MSB 是高字节,其中 MSb 是字节的高位,LSb 是字节的低位。大家可以看出来,二进制数字,每一位代表的温度的含义,都表示出来了。其中 S表示的是符号位,低 11 位都是 2 的幂,用来表示最终的温度。DS18B20 的温度测量范围是从-55 度到+125 度,而温度数据的表现形式,有正负温度,寄存器中每个数字如同卡尺的刻度一样分布,如图 2所示。
图2 DS18B20 温度值
二进制数字最低位变化 1,代表温度变化 0.0625 度的映射关系。当 0 度的时候,那就是0x0000,当温度 125 度的时候,对应十六进制是 0x07D0,当温度是零下 55 度的时候,对应的数字是 0xFC90。反过来说,当数字是 0x0001 的时候,那温度就是 0.0625 度了。
4电路与程序设计
4.1电路的设计
4.1.1系统总体框图
系统总体框图如图3所示
图3 系统总体框图
4.1.2控制按键&LED电路
按下S1能增加温度报警上限,按下S2能减少温度报警上限。测试温度低于上限时,绿灯LED1亮。当温度超过设定的上限时,红灯LED2亮。
图4 控制按键&LED
4.1.3蜂鸣器电路
当测试温度超过设定温度时,蜂鸣器开启;当测试温度低于设定温度时,蜂鸣器关闭。蜂鸣器电流相对较大,因此需要用三极管驱动,并且加了一个 1K欧的电阻作为限流电阻。此外还加了一个 D1二极管,这个二极管叫做续流二极管。
图5 蜂鸣器电路
4.1.4显示电路
显示电路使用LCD1602,第一行显示”Temperature”第二行显示测试温度和温度上限,两者中间显示一个”>”或”<”。RP1为上拉排阻。
图6 显示电路
4.1.5传感器电路
图7 传感器电路
4.1.6总体电路
图8 整体电路
4.1.7电源
使用5V直流电源。
4.2程序的设计
4.2.1程序功能描述
(1)能够测试-30℃~+120℃的温度。
(2)按下S1能增加温度报警上限,按下S2能减少温度报警上限。
(3)测试温度低于上限时,绿灯亮,蜂鸣器关闭。当温度超过设定的上限时,红灯亮且蜂鸣器响起。
4.2.2程序流程图
图9 程序流程图
5测试方案与测试数据
5.1测试方案
5.1.1软件仿真测试
用Proteus 7.5软件画出电路图,模拟硬件对程序进行调试。
5.1.2硬件软件联调
将编写的单片机C语言程序下载到实际硬件中,进行硬件软件联调。
5.2 测试条件与仪器
测试条件:检查多次,仿真电路和硬件电路必须与系统原理图完全相同,并且检查无误,硬件电路保证无虚焊。
测试仪器:数字示波器,数字万用表。
5.3 测试结果
系统上电运行后,LCD能正常显示测试温度且绿灯点亮,测试温度每秒更新。按下S1能使报警温度上限 +1,按下S2能使报警温度上限-1。当测试温度超过上限温度时,绿灯关闭,红灯点亮且蜂鸣器开启;当测试温度低于上限温度时,红灯关闭,绿灯点亮且蜂鸣器关闭。
6 总结
本系统以单片机STCC52芯片为核心部件,利用LCD1602、按键、蜂鸣器、DS18B20并配合C语言算法实现了简易数字温度计设计,完成此次设计题目中的全部基本功能和部分拓展功能。在系统设计过程中,力求硬件线路简单,充分发挥软件编程方便灵活的特点,来满足系统设计要求。
在本次设计的过程中,遇到了许多突发事件和困难,设计制作曾一度止步不前,但通过仔细分析和调整后解决了一个又一个的问题。在整个过程中我们深刻的体会到团队精神的重要性,并提高了自己解决问题的能力。
附录 主要源程序
main程序:
#include sbit KEY1 = P1^0; // 上调报警温度 sbit KEY2 = P1^1; // 下调报警温度 sbit LED1 = P1^2; // 正常时绿灯亮 sbit LED2 = P1^3; // 报警时红灯亮 sbit BUZZ = P1^6; bit enBuzz = 0; //蜂鸣器使能标志 bit flag1s = 0; //1s定时标志 unsigned int warnT = 30; //报警温度值 unsigned char T0RH = 0; //T0重载值的高字节 unsigned char T0RL = 0; //T0重载值的低字节 void ConfigTimer0(unsigned int ms); unsigned char IntToString(unsigned char *str, int dat); extern bit Start18B20(); extern bit Get18B20Temp(int *temp); extern void InitLcd1602(); extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str); extern void LcdWriteCmd(unsigned char cmd); void main() { bit res; bit backup = 1; int temp; //读取到的当前温度值 int intT, decT; //温度值的整数和小数部分 unsigned char len,len1; unsigned char str[12],str1[12]; EA = 1; //开总中断 P1 = 0XF3; ConfigTimer0(1); //T0定时1ms Start18B20(); //启动DS18B20 InitLcd1602(); //初始化液晶 while (1) { if (flag1s) //每秒更新一次温度 { flag1s = 0; res = Get18B20Temp(&temp); //读取当前温度 if (res) //读取成功时,刷新当前温度显示 { intT = temp >> 4; //分离出温度值整数部分 decT = temp & 0xF; //分离出温度值小数部分 len = IntToString(str, intT); //整数部分转换为字符串 len1 = IntToString(str1, warnT); //报警温度转换为字符串 str[len++] = '.'; //添加小数点 decT = (decT*10) / 16; //二进制的小数部分转换为1 //位十进制位 str[len++] = decT + '0'; //十进制小数位再转换为 //ASCII字符 str[len++] = 0xdf; //添加字符串“℃” str[len++] = 'C'; str[len++] = ' '; str[len] = '\\0'; //添加字符串结束符 str1[len1++] = 0xdf; //添加字符串“℃” str1[len1++] = 'C'; str1[len1] = '\\0'; //添加字符串结束符 if (intT < warnT) //超过报警温度时执行报警 { enBuzz = 0; //关闭蜂鸣器 LcdWriteCmd(0x01); //清屏 LcdShowStr(2, 0, "Temperature"); //显示第一行 LcdShowStr(2, 1, str); //显示实时温度 LcdShowStr(9, 1, "<"); LcdShowStr(11, 1, str1); //显示报警温度 } else { enBuzz = 1; //启动蜂鸣器发声 LcdWriteCmd(0x01); //清屏 LcdShowStr(4, 0, "Warning!"); //显示警告 LcdShowStr(0, 1, "Temp:"); LcdShowStr(5, 1, str); //显示实时温度 LcdShowStr(11, 1, ">"); LcdShowStr(12, 1, str1); //显示报警温度 } } else //读取失败时,提示错误信息 { LcdShowStr(4, 1, "error!"); } Start18B20(); //重新启动下一次转换 } } } /* 按键动作函数 */ void KeyAction () { if (KEY1 == 0) { KEY1 = 1; warnT++; } else if (KEY2 == 0) { KEY2 = 1; warnT--; } } /* 整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */ unsigned char IntToString(unsigned char *str, int dat) { signed char i = 0; unsigned char len = 0; unsigned char buf[6]; if (dat < 0) //如果为负数,首先取绝对值,并在指针上添加负号 { dat = -dat; *str++ = '-'; len++; } do { //先转换为低位在前的十进制数组 buf[i++] = dat % 10; dat /= 10; } while (dat > 0); len += i; //i最后的值就是有效字符的个数 while (i-- > 0) //将数组值转换为ASCII码反向拷贝到接收指针上 { *str++ = buf[i] + '0'; } *str = '\\0'; //添加字符串结束符 return len; //返回字符串长度 } /* 配置并启动T0,ms-T0定时时间 */ void ConfigTimer0(unsigned int ms) { unsigned long tmp; //临时变量 tmp = 11059200 / 12; //定时器计数频率 tmp = (tmp * ms) / 1000; //计算所需的计数值 tmp = 65536 - tmp; //计算定时器重载值 tmp = tmp + 28; //补偿中断响应延时造成的误差 T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节 T0RL = (unsigned char)tmp; TMOD &= 0xF0; //清零T0的控制位 TMOD |= 0x01; //配置T0为模式1 TH0 = T0RH; //加载T0重载值 TL0 = T0RL; ET0 = 1; //使能T0中断 TR0 = 1; //启动T0 } /* T0中断服务函数,完成250ms, 1s定时 */ void InterruptTimer0() interrupt 1 { static unsigned int tmr1s = 0; static unsigned int tmrms = 0; TH0 = T0RH; //重新加载重载值 TL0 = T0RL; if (enBuzz) //蜂鸣器发声处理 { BUZZ = ~BUZZ; //启动蜂鸣器发声 LED1 = 0; LED2 = 1; } else { BUZZ = 1; //关闭蜂鸣器 LED1 = 1; LED2 = 0; } tmr1s++; tmrms++; if (tmrms >= 250) //延时250ms { tmrms = 0; KeyAction (); } if (tmr1s >= 1000) //定时1s { tmr1s = 0; flag1s = 1; } } DS18B20程序: #include #include sbit IO_18B20 = P3^2; //DS18B20通信引脚 /* 软件延时函数,延时时间(t*10)us */ void DelayX10us(unsigned char t) { do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } while (--t); } bit Get18B20Ack() { bit ack; EA = 0; IO_18B20 = 0; DelayX10us(50); IO_18B20 = 1; DelayX10us(6); ack = IO_18B20; while(!IO_18B20); EA = 1; return ack; } void Write18B20(unsigned char dat) { unsigned char mask; EA = 0; for(mask=0x01; mask!= 0; mask<<=1) { IO_18B20 = 0; _nop_(); _nop_(); if((mask&dat) == 0) IO_18B20 = 0; else IO_18B20 = 1; DelayX10us(6); IO_18B20 = 1; } EA = 1; } unsigned char Read18B20() { unsigned char dat; unsigned char mask; EA = 0; for(mask=0x01; mask!=0; mask<<=1) { IO_18B20 = 0; _nop_(); _nop_(); IO_18B20 = 1; _nop_(); _nop_(); if(!IO_18B20) dat &= ~mask; else dat |= mask; DelayX10us(6); } EA = 1; return dat; } bit Start18B20() { bit ack; ack = Get18B20Ack(); if(ack == 0) { Write18B20(0xCC); Write18B20(0x44); } return ~ack; } bit Get18B20Temp(int *temp) { bit ack; unsigned char LSB, MSB; ack = Get18B20Ack(); if(ack == 0) { Write18B20(0xCC); Write18B20(0xBE); LSB = Read18B20(); MSB = Read18B20(); *temp = ((int)MSB<<8) + LSB; } return ~ack; } LCD1602程序: #include #define LCD1602_DB P0 sbit LCD1602_RS = P2^0; sbit LCD1602_RW = P2^1; sbit LCD1602_E = P2^2; /* 等待液晶准备好 */ void LcdWaitReady() { unsigned char sta; LCD1602_DB = 0xFF; LCD1602_RS = 0; LCD1602_RW = 1; do { LCD1602_E = 1; sta = LCD1602_DB; //读取状态字 LCD1602_E = 0; } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止 } /* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */ void LcdWriteCmd(unsigned char cmd) { LcdWaitReady(); LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_DB = cmd; LCD1602_E = 1; LCD1602_E = 0; } /* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */ void LcdWriteDat(unsigned char dat) { LcdWaitReady(); LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_DB = dat; LCD1602_E = 1; LCD1602_E = 0; } /* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */ void LcdSetCursor(unsigned char x, unsigned char y) { unsigned char addr; if (y == 0) //由输入的屏幕坐标计算显示RAM的地址 addr = 0x00 + x; //第一行字符地址从0x00起始 else addr = 0x40 + x; //第二行字符地址从0x40起始 LcdWriteCmd(addr | 0x80); //设置RAM地址 } /* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */ void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str) { LcdSetCursor(x, y); //设置起始地址 while (*str != '\\0') //连续写入字符串数据,直到检测到结束符 { LcdWriteDat(*str++); } } /* 初始化1602液晶 */ void InitLcd1602() { LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口 LcdWriteCmd(0x0C); //显示器开,光标关闭 LcdWriteCmd(0x06); //文字不动,地址自动+1 LcdWriteCmd(0x01); //清屏 }
