苏州大学微机实验——基于DS18B20的数字温度计的设计

前言

本文是在完成苏州大学电子信息学院微机实验——基于DS18B20的数字温度计的设计过程中所萌生的一个想法,相比于各路大佬,可能会有很多地方存在不足,还请见谅。用到的主要芯片和设备有STC15系列单片机,DS18B20温度传感器,LCD1602液晶显示器。

系统功能设计要求

1. 测温范围为:-10℃~85℃,且测量误差不大于±0.5℃

2. 模式1显示当前温度值

3. 模式2显示该DS18B20芯片的序列号

LCD1602介绍

LCD1602液晶工作原理详解及例程代码

DS18B20介绍

DS18B20温度传感器原理详解及例程代码

功能实现

本实验所用电路板为4T科技官方蓝桥杯竞赛板,单片机为STC15F2K60S2。

实验采用采用模块化编程,实现功能如下:

开机默认模式1,矩阵键盘S7按下后,处于工作模式1,同时点亮led1,按键S6按下后,处于工作模式2,同时led1熄灭,led2点亮。

本实验各模块代码如下

main.c

#include <STC15F2K60S2.H>
#include "LCD1602.h"
#include "DELAY.h"
#include "DS18B20.h"
#define LED(x) {P2 = ((P2 & 0x1f) | 0x80);P0 = x;P2 &= 0x1f;}//LED显示宏定义
int key_flag = 0;			// 键盘扫描标志位
unsigned char key_value = 0xff;// 键盘读取值
unsigned char mode =	0;			// 界面模式

void cls_buzz(void)	//关继电器、蜂鸣器
{
    P2 = (P2 & 0x1F | 0xA0);
    P0 = 0x00;
    P2 &= 0x1F;
}

/**********************************************************
 * @name Timer0
 * @brief 	Timer0中断服务程序
 * @param void
 * @return none
**********************************************************/
void Timer0_Isr(void) interrupt 1
{	
	static unsigned char icount = 0;		// 定时辅助计数
	if(icount==10)
	{
		key_flag = 1;
		icount=0;
	}
	icount++;
}
/**********************************************************
 * @name Timer0Init
 * @brief 	Timer0定时器初始化
 * @param void
 * @return none
 **********************************************************/

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x20;		//设置定时初值
	TH0 = 0xD1;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;			//使能定时器0中断
	EA=1;				//开总中断
}
/**********************************************************
 * @name read_keyboard
 * @brief 	矩阵键盘读取
 * @param void
 * @return none
**********************************************************/

void read_keyboard(void)//矩阵键盘读取
{
  static unsigned char hang;
	static unsigned char key_state=0;	
	switch(key_state)
	{
		case 0:
		{
			P3 = 0x0f; P42 = 0; P44 = 0;
			if(P3 != 0x0f) //有按键按下
			key_state=1;	
		}
		break;
		case 1:
		{
			P3 = 0x0f; P42 = 0; P44 = 0;
			if(P3 != 0x0f) //有按键按下
			{
				if(P30 == 0)hang = 1;
				if(P31 == 0)hang = 2;
				if(P32 == 0)hang = 3;
				if(P33 == 0)hang = 4;//确定行	    
				switch(hang){
					case 1:{P3 = 0xf0; P42 = 1; P44 = 1;
						if(P44 == 0)		  {key_value=0;key_state=2;}//S7按下
						else if(P42 == 0) {key_value=1;key_state=2;}//S11按下
						else if(P35 == 0) {key_value=2;key_state=2;}//S15按下
						else if(P34 == 0) {key_value=3;key_state=2;}//S19按下
					}
					break;
					case 2:{P3 = 0xf0; P42 = 1; P44 = 1;
						if(P44 == 0) 			{key_value=4;key_state=2;}//S6按下
						else if(P42 == 0) {key_value=5;key_state=2;}//S10按下
						else if(P35 == 0) {key_value=6;key_state=2;}//S14按下
						else if(P34 == 0) {key_value=7;key_state=2;}//S18按下
					}
					break;
					case 3:{P3 = 0xf0; P42 = 1; P44 = 1;
						if(P44 == 0) 			{key_value=8;key_state=2;}//S5按下
						else if(P42 == 0) {key_value=9;key_state=2;}//S9按下
						else if(P35 == 0) {key_value=10;key_state=2;}//S13按下
						else if(P34 == 0) {key_value=11;key_state=2;}//S17按下
					}
					break;
					case 4:{P3 = 0xf0; P42 = 1; P44 = 1;
						if(P44 == 0) 			{key_value=12;key_state=2;}//S4按下
						else if(P42 == 0) {key_value=13;key_state=2;}//S8按下
						else if(P35 == 0) {key_value=14;key_state=2;}//S12按下
						else if(P34 == 0) {key_value=15;key_state=2;}//S16按下
					}
					break;
				}	
			}
			else
			{
				key_state=0;	
			}  	   
		}
		break;
		case 2:     
		{
			P3 = 0x0f; P42 = 0; P44 = 0;
			if(P3 == 0x0f) //按键放开
			key_state=0;	
		}
		break;			
    } 
}
/**********************************************************
 * @name key_action
 * @brief 	模式选择
 * @param void
 * @return none
**********************************************************/

void key_action(void)
{

	switch(key_value)//模式选择
	{
	case 0:
		LED(0xfe);
		mode=0;//温度显示模式
		break;
	case 4:
		LED(0xfd);
		mode=1;//序列显示模式
		break;
	}
	key_value = 0xff;
}
/**********************************************************
 * @name PowerOn
* @brief 	开机动画loading...
 * @param void
 * @return none
**********************************************************/

void PowerOn(void)//开机动画
{
	unsigned char i = 0, len = 0;
	LCD_Display_String(1, 1, "loading");
	for(i=0; i<4; i++)
	{
		LCD_Display_String(1, 8, "   ");
		DelayMs(100);
		LCD_Display_String(1, 8, ".  ");
		DelayMs(100);
		LCD_Display_String(1, 8, ".. ");
		DelayMs(100);
		LCD_Display_String(1, 8, "...");
		DelayMs(100);
	}
}
/**********************************************************
 * @name display
 * @brief 	LCD显示
 * @param void
 * @return none
**********************************************************/

void display(void)//LED显示
{
	char len=-1;
	unsigned int temp;
	temp=Ds18b20ReadTemp();
	switch (mode)
		{
		case 0://温度显示模式
						LcdInit();	
						LCD_Display_String(1,1,"  CURRENT-TEMP  ");
						LCD_Display_String(2,1,"TEMP:           ");
						len=LCD_WriteNum(2,6,4,2,temp);
						LCD_Display_Char(2,len,0xdf);
						LCD_Display_Char(2,len+1,'C');
						DelayMs(3);
						break;
			case 1://序列显示模式
					LcdInit();
					LCD_Display_String(1,1,"READ ROM CODE:  ");
					Disp_rom_code();
					break;
		}
}

void main() 			//ַ主函数
{
	cls_buzz();//关继电器蜂鸣器
	LED(0xff);//关LED
	LcdInit();//LCD初始化
	Timer0_Init();//定时器初始化
	PowerOn();//开机加载动画
	while(1)
	{
		if(key_flag)
    {
      key_flag = 0;
			read_keyboard();
			if(key_value != 0xFF)
			{
				key_action();
			}
    } 
		display();
	}
}

LCD1602.h

#ifndef __LCD1602_H_
#define __LCD1602_H_

void LcdWriteCom(unsigned char com);
void LcdWriteData(unsigned char dat);
void LcdInit();						      
void LCD_SET_POS(unsigned char p,unsigned char q);
void LCD_Display_Char(unsigned char p,unsigned char q,unsigned char ch);
void LCD_Display_String(unsigned char p,q,unsigned char *s);
unsigned char LCD_WriteNum(unsigned char p, unsigned char q, unsigned char len, unsigned char point, long num);
unsigned char LCD_WriteHex(unsigned char p, unsigned char q, unsigned char len, long num);
#endif

LCD1602.c

#include <LCD1602.h>
#include <REG52.H>
//#include <STC15F2K60S2.H>


//1602的控制引脚定义
sbit RS = P2^0;    	//寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器
sbit RW = P2^1;		//读写控制,低写,高读
					// RS=0、RW=0——表示向LCM写入指令,可以写入指令或者显示地址
					// RS=0、RW=1——表示读取Busy标志
					// RS=1、RW=0——表示向LCM写入数据
					// RS=1、RW=1——表示从LCM读取数据
sbit EN = P1^2;		//使能端,当E端由高电平跳变成低电平时,液晶模块执行命令
#define LCD_DATA P0	//定义传入数据的特殊功能寄存器,或者使用 “static sfr LCD_DATA=0X80;”定义
static unsigned char MaxStrlen = 40;	//定义一行最多显示字符数



/*****函数部分*****/
static void LCD_Delay();
static bit LCD_BUSY(void);

/*************************************************
 * @name LCD_Delay()
 * @brief LCD延时函数(12Mhz晶振延时4uS)
 * @param 无
 * @return 无
**************************************************/
static void LCD_Delay()
{
	unsigned char data i;
	i = 10;
	while (--i);
}

/*************************************************
 * @name LCD_BUSY()
 * @brief 检测LCD1602是否处于忙状态
 * @param 无
 * @return 忙信号布尔值(bit)。返回1,则LCD_BUSY;返回0,则OK
**************************************************/
static bit LCD_BUSY(void)
{
	LCD_DATA=0xff;
	RS = 0;
	RW = 1;
	EN = 1;
	LCD_Delay();
	EN = 0;
	return (bit)(LCD_DATA & 0x80); //最高位为忙信号位
}

/*************************************************
 * @name LcdWriteCom()
 * @brief LCD写一字节命令
 * @param cmd 待写控制命令(unsigned char)
 * @return 无
**************************************************/
void LcdWriteCom(unsigned char cmd)
{
	while(LCD_BUSY());   //测忙
	RS = 0;
	RW = 0;
	EN = 1;
	LCD_DATA = cmd;
	LCD_Delay();
	EN = 0;
}

/*************************************************
 * @name LcdWriteData()
 * @brief LCD写一字节数据
 * @param dat 待写显示数据(unsigned char)
 * @return 无
**************************************************/
void LcdWriteData(unsigned char dat)
{
	while(LCD_BUSY());   //测忙
	RS = 1;
	RW = 0;
	EN = 1;
	LCD_DATA = dat;
	LCD_Delay();
	EN = 0;
}

/*************************************************
 * @name LCD_ClsLine()
 * @brief LCD清除行
 * @param line 设置待清除行号(unsigned char)
 * @return 无
**************************************************/
void LCD_ClsLine(unsigned char line)
{
	unsigned char i=0;
	for(i=0;i<16;i++)
		LCD_Display_Char(i,line,' ');
}

/*************************************************
 * @name LCD_Clear()
 * @brief LCD清屏
 * @param 无
 * @return 无
**************************************************/
void LCD_Clear(void)
{
	LCD_ClsLine(0);
	LCD_ClsLine(1);
}

/*************************************************
 * @name LcdInit()
 * @brief LCD液晶屏的初始化
 * @param 无
 * @return 无
**************************************************/
void LcdInit(void)
{
	LcdWriteCom(0x38);   //功能设置,8位数据接口,两行显示,5×8点阵,即0b00111000也就是0x38
	LcdWriteCom(0x0c);   //显示开关控制,00001100 显示开,光标不显示,光标不闪烁
	LcdWriteCom(0x06);   //输入方式设置,00000110 I/D=1:写入新数据后光标右移;S=0:显示不移动。
	LcdWriteCom(0x01);   //清除LCD的显示内容
	LCD_Clear();
}


/*************************************************
 * @name LCD_Display_Char()
 * @brief LCD写一个字符
 * @param p 设置显示的行坐标(unsigned char)
 * @param q 设置显示的列坐标(unsigned char)
 * @param ch 待写字符(unsigned char)
 * @return 无
**************************************************/
void LCD_Display_Char(unsigned char p,unsigned char q,unsigned char ch)
{

	unsigned char addr;
	p-=1;
	q-=1;
	while(q>=16)
	{
		q-=16;
		p++;
	}
	while(p>=2) p-=2;
	if (p == 0)
  	{
    	addr = 0x00 + q; //第一行的x位置显示
  	}
  	else
  	{
    	addr = 0x40 + q; //第二行x的位置显示
  	}
  	LcdWriteCom(addr + 0x80);
		LcdWriteData(ch);
}

/*************************************************
 * @name LCD_Display_String()
 * @brief LCD写字符串
 * @param p 设置显示的行坐标(unsigned char)
 * @param q 设置显示的列坐标(unsigned char)
 * @param str 待写字符串(unsigned char)
 * @return 字符长度(unsigned char)
**************************************************/
void LCD_Display_String(unsigned char p,unsigned char q, unsigned char *str)
{

	unsigned char i=0;
	while(str[i]!=0)
	{
		LCD_Display_Char(p,q, str[i]);
		q++;
		i++;
	}
}


/*************************************************
 * @name 	LCD_WriteNum()
 * @brief 	LCD写一个数字。
 * 			整数显示——len=数字位数,point=0,num=要显示的数字;
 * 			小数显示——len=num位数,point=小数位数,num=需要显示的小数*10^(point)(即,先将小数放大为整形)
 * @param p 设置显示的行坐标(unsigned char)
 * @param q 设置显示的列坐标(unsigned char)
 * @param len 	待显示的数字位数(unsigned char)
 * @param point 小数点位置(unsigned char)。point=0时,显示整数
 * @param num 	待显示的数字(long)。小数请放大为整数,范围-2147483648~2147483648
 * @return 显示出来的数字所占真实位数(unsigned char)
**************************************************/
unsigned char LCD_WriteNum(unsigned char p, unsigned char q, unsigned char len, unsigned char point, long num)
{

	unsigned char real_len, flag=0;
	if(point) 	len++;
	else		point--;
	if(num<0)
	{
		LCD_Display_Char(p,q,'-');
		len++;
		num=-num;
		flag=1;
	}
	real_len=len+6;
	while(len-flag)
	{
		if(num>=0) len--;
		if(point)
		{
			
			LCD_Display_Char(p, q+len, '0'+num%10);
			num/=10;
		}
		else
		{
			LCD_Display_Char(p, q+len, '.');
		}
		point--;
	}
	return real_len;
}
/*************************************************
 * @name 	LCD_WriteHex()
 * @brief 	LCD写一个16进制数字。
 * 			整数显示——len=数字位数,point=0,num=要显示的数字;
 * 			小数显示——len=num位数,point=小数位数,num=需要显示的小数*10^(point)(即,先将小数放大为整形)
 * @param x 设置显示的X坐标(unsigned char)
 * @param y 设置显示的y坐标(unsigned char)
 * @param len 	待显示的16进制数字位数(unsigned char)
 * @param num 	待显示的数字(long)。小数请放大为整数,范围-2147483648~2147483648
 * @return 显示出来的数字所占真实位数(unsigned char)
**************************************************/

unsigned char LCD_WriteHex(unsigned char p, unsigned char q, unsigned char len, long num)
{
	unsigned char temp, real_len=len, i=0;
	for(i=0;i<len;i++)
	{
		temp = (num>>((len-1)*4))&0x0f;
		if(temp>=0 && temp<=9)
		{
			LCD_Display_Char(p, q+i, '0'+temp);
		}
		else{
			LCD_Display_Char(p, q+i, 'A'+temp-10);
		}
		num = num<<4;
	}
		return real_len;
}

DS18B20.h

#ifndef __DS18B20_H
#define __DS18B20_H

unsigned char Ds18b20Init();	//DS18B20初始化

void Ds18b20WriteByte(unsigned char dat);//DS18B20写数据
unsigned char Ds18b20ReadByte();//DS18B20读数据
int Ds18b20ReadTemp();//读取温度
unsigned char* Read_rom_code();//读取ROM
void Disp_rom_code();//显示ROM

#endif

DS18B20.c

/* DS18B20的数据总线需要上拉一个5kΩ的电阻,从而提供高电平
 * 访问DS18B20的流程:初始化——ROM命令——功能命令
 *
 * DS18B20 命令集
 * 一、ROM命令
 *	 1、0XF0:搜索ROM
 *	 2、0X33:读取ROM
 *	 3、0X55:匹配ROM
 *	 4、0XCC:跳过ROM
 *	 5、0XEC:警报搜索
 *二、功能命令
 *	 1、0X44:温度转换
 *	 2、0X4E:写入暂存寄存器
 *	 3、0XBE:读取暂存寄存器
 *	 4、0X48:拷贝暂存寄存器
 *	 5、0XB8:召回EEPROM
 *   6、0XB4: 读取供电模式
************************************************************/

#include <STC15F2K60S2.H>
#include "DS18B20.h"
#include "LCD1602.h"
#include "Delay.h"
unsigned char drom[16];
unsigned char RomCode[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
sbit DQ=P1^4;            //DS18B20总线

/*************************************************
 * @name delay()
 * @brief 延时函数、12Mhz晶振延时
 * @param 无
 * @return 无
**************************************************/

static void delay(unsigned int t)
{
	t*=12;
	while(t--);
}
/*************************************************
 * @name Ds18b20Init()
* @brief DS18B20复位函数
 * @param 无
 * @return 无
**************************************************/

unsigned char Ds18b20Init()        //DS18B20复位
{
	bit init_flag = 0;
	DQ=1;      		//DQ复位
	delay(1);    			//稍做延时
	DQ=0;        		//单片机将DQ拉低
	delay(45);   			//精确延时,大于480us
	DQ=1;       		//拉高总线
	delay(1);
	init_flag = DQ;
	delay(45);
	return init_flag;
}
/*************************************************
 * @name Ds18b20WriteByte()
 * @brief 向DS18B20写一字节数据
 * @param dat 待写的数据 (unsigned char)
 * @return 无
**************************************************/

void Ds18b20WriteByte(unsigned char dat)       //向DS18B20写一字节数据
{
	unsigned char i = 0;	
	DQ=1;      		//DQ复位
	delay(45);    			//稍做延时
	for (i = 8; i > 0; i--)
	{
		DQ=0;
		delay(1);
		dat & 0x01 ? (DQ=1):(DQ=0);
		delay(6);
		DQ=1;
		dat >>= 1;
	}
}
/*************************************************
 * @name Ds18b20ReadByte()
 * @brief 从DS18B20读一字节数据
 * @param 无
 * @return dat 读得的数据 (unsigned char)
**************************************************/
unsigned char Ds18b20ReadByte()         //从DS18B20读一字节数据
{
	unsigned char i=0;
	unsigned char dat = 0X00;
	for (i = 8; i > 0; i--)
	{
		DQ=0;     // 给脉冲信号
		dat >>= 1;
		DQ=1;     // 给脉冲信号
		if(DQ)
			dat |= 0x80;
		delay(5);
	}
	DQ=0;
	delay(1);
	return dat;
}
/*************************************************
 * @name Ds18b20ReadTemp()
 * @brief 从DS18B20读取温度
 * @param 无
 * @return temp 读得的温度 (unsigned char)
**************************************************/
int Ds18b20ReadTemp()            //
{
	long int temp=0x0000;
	unsigned char high,low;
	
	Ds18b20Init();
	Ds18b20WriteByte(0xcc);		 
	Ds18b20WriteByte(0x44);	    //
	delay(20);
	
	Ds18b20Init();
	Ds18b20WriteByte(0xcc);	
	Ds18b20WriteByte(0xbe);	   //	
	
	low=Ds18b20ReadByte();		//
	high=Ds18b20ReadByte();		    //
	temp=high;
	temp = (temp <<= 8) | low;
	
	if(temp & 0X8000)//正负温度判断
	{
		temp &= 0xF800;
		temp = ~temp + 1;
		temp = (temp * 0.0625+0.005) * 100;
		temp = ~(temp - 1);
	}
	else
	{
		temp=(temp * 0.0625+0.005) * 100;
	}
	return temp;
}
/*************************************************
 * @name Read_rom_code()
 * @brief 从DS18B20读取序列
 * @param 无
 * @return RomCode 读得的序列 (unsigned char*)
**************************************************/

unsigned char* Read_rom_code()
{
     unsigned char i;
     Ds18b20Init();                  //DS18B20???
     Ds18b20WriteByte(0x33);                 //???ROM
     for(i=0;i<8;i++)
     RomCode[7-i]=Ds18b20ReadByte();
	return RomCode;
}
/*************************************************
 * @name Disp_rom_code()
	* @brief 在LCD显示序列
 * @param 无
 * @return 无
**************************************************/
void Disp_rom_code()
{
	unsigned char i = 0;
	Read_rom_code();
	for(i = 0; i < 8; i++)
		{
			LCD_WriteHex(2,2*i+1, 2,RomCode[i]);
		}
}

DELAY.h

#ifndef __DELAY_H
#define __DELAY_H
void DelayMs(unsigned char n);
	
#endif

DELAY.c

#include <STC15F2K60S2.H>
#include "DELAY.h"
void DelayMs(unsigned char n)            //@12MHz,延时nms
{
	unsigned char j;
	while(n--)
	{
		for(j=0;j<113;j++);
	}
}

实验结果如下图

模式1:

模式2:

总结与感悟

以上代码仅实现了实验要求的功能,如需增加额外的功能用于作业加分,恕作者无能为力。

作为一名小白,在完成这份作业的过程中,除了任课老师外,得到了多名同学的帮助,他们是WGL、CYX、XYL,在此十分感谢,特别感谢WGL同学赠送了一块LCD1602显示屏,让我可以离开仿真图,进行实际的实验的调试与修改,并最终完成作业。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值