前言
本文是在完成苏州大学电子信息学院微机实验——基于DS18B20的数字温度计的设计过程中所萌生的一个想法,相比于各路大佬,可能会有很多地方存在不足,还请见谅。用到的主要芯片和设备有STC15系列单片机,DS18B20温度传感器,LCD1602液晶显示器。
系统功能设计要求
1. 测温范围为:-10℃~85℃,且测量误差不大于±0.5℃
2. 模式1显示当前温度值
3. 模式2显示该DS18B20芯片的序列号
LCD1602介绍
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显示屏,让我可以离开仿真图,进行实际的实验的调试与修改,并最终完成作业。