锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

51单片机实现智能手表(秒表功能、读取温度、显示和修改时间日期、设置闹钟、显示星期)

时间:2022-12-23 01:30:00 0550传感器

智能手表

  • 项目说明
  • 硬件设计
    • 温度传感器
      • 计算温度
      • 读取温度
    • 定时器中断
      • 工作方式
      • 定时器配置
  • 实物硬件连接
  • 软件设计
    • temp.h
    • temp.c
    • main.c
  • 实验现象及总结

项目说明

用普中51和keil uVision4实现智能手表的功能包括:显示秒表、时间、日期、星期、设置闹钟、读取温度、指示灯亮等。综合实验主要包括独立按钮、指示灯亮、温度传感器、定时器、动态数字管显示、蜂鸣器实验,本博客重要解释定时器和温度传感器实验,其他组合实验相对简单,可以自行查阅数据。

硬件设计

温度传感器

我用这个项目精度高的外部 DS18B20 由于该传感器是单总线接口,需要使用数字温度传感器 51 单片机的一个 IO 口模拟单总线口模拟 DS18B20 通信,读取检测到的环境温度。
DS18B20 外观实物如下图所示:
在这里插入图片描述

**注意:**如果将传感器插入向,电源短路,传感器会发热,容易损坏。因此,我们必须注意传感器的方向。传感器的凸起通常标记在开发板上,因此只需将传感器的凸起方向插入开发板的凸起方向即可。

DS18B20 数字温度传感器内部的一些高速临时存储器自行查阅数据,在这里跳过,直接了解它是如何计算温度值的。

计算温度

若温度大于 0,前5 位为‘ 只要将测量值乘以0 0.0625(默认精度为 12 如果温度小于,可以获得实际温度; 0,这 5 位为‘ 1’测量值需要取反加 1 再乘以 0.0625 实际温度际温度。例如,我们需要计算 85 度,数据输出16进制 0X0550,因为高字节高 5位为 0,表示检测温度为正温,0X0550 十进制对应 这个值乘以1360 12 位精度 0.0625,所以可以得到 85 度。

读取温度

知道了怎么计算温度,接下来我们就来看看如何读取温度数据,由于DS18B20 是单总线设备,所有单总线设备都需要严格的信号时序,以确保
数据的完整性。DS18B20 时序包括以下几种:初始化时序和写作(0 和 1)时序,读(0 和 1)时序。 DS18B20 所有的命令和数据都在字节的低位之前。
这里有这些信号的时序:(1) 初始时序,(2)写时序,(3)读时序。详细介绍时序自行查阅。其实根据字面意思大致可以猜出是什么意思。
DS18B20 典型的温度读取过程是:复位→发 SKIP ROM 命令(0XCC)→开始转换命令(0X44)→延时→复位→发送 SKIP ROM 命令(0XCC)→读取存储器命令(0XBE)→连续读取两个字节数据(即温度)→结束。

定时器中断

在学习定时器之前,你需要明白:
①51 单片机有两组定时器/计数器,因为可以定时计数,所以称之为
定时器/计数器。
②定时器/计数器和单片机 CPU 它们是独立的。定时器/计数器的工作过程自动完成,无需 CPU 的参与。
③51 单片机中的定时器/计数器是基于机器内部的时钟或外部的脉冲信
将数据添加到寄存器中的数据中 1。
有了定时器/计数器,单片机的效率可以提高,一些简单的重复添加 1 的
工作可以交给定时器/计数器处理。CPU 转而处理一些复杂的事情,同时实现准确的定时效果。

STC89C5X 单片机有两个定时/计数器可编程 T0、T1 特殊功能定时器 T2。

工作方式

重点介绍定时器常用的工作方法:
计数位数是 16 位,由 TL0 作为低 8 位,TH0 作为高 8 位,组成了16 位加 1 计数器。
计数初值与计数个数的关系如下:X=2(16)-N。

定时器配置

这里以定时器 0 例如,介绍定时器的工作方法 1、设定 1ms 初始值,打开定时器计数功能和总中断,如下:

void Timer0Init() { 
         TMOD|=0X01;///选择定时器 0 模式,工作模式 1,仅用 TR0 打开启动 TH0=0XFC; ///给定时器赋初值,定时 1ms TL0=0X18; ET0=1;///打开定时器 0 中断允许 EA=1;//打开总中断 TR0=1;///打开定时器 }

百度会教你如何赋值定时器赋值的初值 1 使用方法是一样的,只是上面提到的 0 变为 1 即可。

实物硬件连接



因为这条线连接有点绕,以直接口述还好些吧。

上面就是用到的GPIO口,LSA、LSB、LSC指的就是74HC138模块,k1到k8就是独立按键模块,led到led4指的是LED交通灯模块,beep指蜂鸣器模块,DS18B20我连接的是P3^7接口,动态数码管我连接的是P0口(在普中51单片机上是J22连接到J6,就是上图中的8排杜邦线)。

软件设计

上述DS18B20温度传感器的代码实现如下:

temp.h

#ifndef _TEMP_H_
#define _TEMP_H_

#include 
//---重定义关键词---// 
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif
//--定义使用的 IO 口--// 
sbit DSPORT=P3^7;
//--声明全局函数--// 
void Delay1ms(uint ); 
uchar Ds18b20Init(); 
void Ds18b20WriteByte(uchar com);
uchar Ds18b20ReadByte(); 
void Ds18b20ChangTemp(); 
void Ds18b20ReadTempCom(); 
int Ds18b20ReadTemp();

#endif

temp.c

#include "temp.h"

void Delay1ms(uint y) //延时函数
{ 
         
uint x; 
for(;y>0;y--)
{ 
         
for(x=110;x>0;x--);
}
}

uchar Ds18b20Init() //初始化函数
{ 
         
 uchar i;
 DSPORT = 0; //将总线拉低 480us~960us 
 i = 70; 
 while(i--);//延时 642us 
 DSPORT = 1; //然后拉高总线,如果 DS18B20 做出反应会将在 15us~60us 后总线拉低 
 i = 0; 
 while(DSPORT) //等待 DS18B20 拉低总线 
 { 
         
  Delay1ms(1); 
  i++; 
  if(i>5)//等待>5ms 
  { 
         
  return 0;//初始化失败 
  }
} 
return 1;//初始化成功
}

void Ds18b20WriteByte(uchar dat) //向 18B20 写入一个字节 
{ 
        
uint i, j;
for(j=0; j<8; j++) 
{ 
         
DSPORT = 0; //每写入一位数据之前先把总线拉低 1us 
i++; 
DSPORT = dat & 0x01; //然后写入一个数据,从最低位开始 
i=6; 
while(i--); //延时 68us,持续时间最少 60us
DSPORT = 1; //然后释放总线,至少 1us 给总线恢复时间才能接 着写入第二个数值 
dat >>= 1; 
} 
} 

uchar Ds18b20ReadByte() //读取一个字节 
{ 
         
uchar byte, bi; 
uint i, j; 
for(j=8; j>0; j--) 
{ 
         
DSPORT = 0; 
i++; 
DSPORT = 1; 
i++; 
i++;//延时 6us 等待数据稳定 
bi = DSPORT; //读取数据,从最低位开始读取 
/*将 byte 左移一位,然后与上右移 7 位后的 bi,注意移动之后移掉 那位补 0*/ 
byte = (byte >> 1) | (bi << 7); 
i = 4; 
while(i--); 
} 
return byte; 
}

void Ds18b20ChangTemp() //让 18b20 开始转换温度 
{ 
         
Ds18b20Init(); 
Delay1ms(1); 
Ds18b20WriteByte(0xcc); //跳过 ROM 操作命令 
Ds18b20WriteByte(0x44); //温度转换命令 
//Delay1ms(100); 
//等待转换成功,而如果你是一直刷着的话,就不用这个延时了
}

void Ds18b20ReadTempCom() //发送读取温度命令
{ 
        
Ds18b20Init(); 
Delay1ms(1); 
Ds18b20WriteByte(0xcc); //跳过 ROM 操作命令 
Ds18b20WriteByte(0xbe); //发送读取温度命令
}

int Ds18b20ReadTemp() //读取温度
{ 
         
int temp = 0; 
uchar tmh, tml; 
Ds18b20ChangTemp(); //先写入转换命令 
Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令
tml = Ds18b20ReadByte(); //读取温度值共 16 位,先读低字节 
tmh = Ds18b20ReadByte(); //再读高字节 
temp = tmh; 
temp <<= 8; 
temp |= tml;
return temp; 
}

最后在主函数一并实现定时器中断实验:

main.c

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include "temp.h"
typedef unsigned int u16;   //对数据类型进行声明定义
typedef unsigned char u8;
//--定义使用的 IO 口--//
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit k1=P3^1;
sbit k2=P3^2;
sbit k3=P3^3;
sbit k4=P3^4;
sbit k5=P3^5;
sbit k6=P3^6;
sbit k7=P3^0;
sbit led=P2^0;
sbit led1=P2^1;
sbit led2=P2^5;
sbit led3=P2^6;
sbit beep=P2^7;
sbit k8=P1^0;
sbit led4=P1^1;

u8 code smgduan[17]={ 
        0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x070x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

u16 hour=15,minute=32,second=30;//自己设置初始时间,全局变量
u16 year=2020,month=6,day=30,week=2;//自己设置初始日期、星期,全局变量
u8 ssec,sec,min; //毫秒,秒,分
//不同功能的数码管显示//
u8 DisplayData[8];
u8 DisplayData1[8];
u8 DisplayData2[8];
u8 DisplayData3[8];
u8 DisplayData4[8];

void delay(u16 i)
{ 
        
 while(i--); 
}

void keypros() //按键处理函数
{ 
         
if(k1==0) //检测按键 K1 是否按下 
{ 
         
delay(1000); //消除抖动 一般大约 10ms 
if(k1==0) //再次判断按键是否按下 
{ 
        
//--只留当前指示灯亮,其他指示灯灭--//
led1=1;
led=1;
led3=1;
led4=1; 
led=~led; //led 状态取反 
} 
while(!k1); //检测按键是否松开 
}
//--其它按键注释一样,下面就不注释了--//
if(k2==0)  
{ 
        
delay(1000); 
if(k2==0)  
{ 
         
led=1;
led2=1;
led3=1;
led4=1;
led1=~led1;  
} 
while(!k2);  
}
if(k3==0)  
{ 
        
delay(1000); 
if(k3==0)  
{ 
         
led=1;
led1=1;
led3=1;
led4=1;
led2=~led2;  
} 
while(!k3);  
}
if(k4==0)  
{ 
        
delay(1000); 
if(k4==0)  
{ 
         
led=1;
led1=1;
led2=1;
led4=1;
led3=~led3;  
} 
while(!k4);  
}
if(k8==0)  
{ 
        
delay(1000); 
if(k8==0)  
{ 
         
led=1;
led1=1;
led2=1;
led3=1;
led4=~led4;  
} 
while(!k8);  
}
}

void datapros1(int temp) //温度传感器数码管数字设置
{ 
         
float tp; 
if(temp< 0) //当温度值为负数 
{ 
         
DisplayData[0] = 0x40; //因为读取的温度是实际温度的补码,所以减 1,再取反求出原码 
temp=temp-1; 
temp=~temp; 
tp=temp; 
temp=tp*0.0625*100+0.5;
}
else 
{ 
         
DisplayData[0] = 0x00; 
tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量 
//如果温度是正的那么,那么正数的原码就是补码它本身 
temp=tp*0.0625*100+0.5; 
//留两个小数点就*100,+0.5 是四舍五入,因为 C 语言浮点数转换为整型的时候把小数点 //后面的数自动去掉,不管是否大于 0.5,而+0.5 之后大于 0.5 的 就是进 1 了,小于 0.5 的就 //算加上 0.5,还是在小数点后面。 
} 
DisplayData1[1] = smgduan[temp / 10000]; 
DisplayData1[2] = smgduan[temp % 10000 / 1000]; 
DisplayData1[3] = smgduan[temp % 1000 / 100] | 0x80; 
DisplayData1[4] = smgduan[temp % 100 / 10]; 
DisplayData1[5] = smgduan[temp % 10]; 
}

void Timer0Init()//定时器0初始化
{ 
        
 TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
 TH0=0Xd8; //给定时器赋初值,定时10ms
 TL0=0Xf0; 
 ET0=1;//打开定时器0中断允许
 EA=1;//打开总中断
 TR0=1;//打开定时器 
}

void DigDisplay1() //温度传感器数码管数字显示
{ 
         
u8 i; 
for(i=0;i<6;i++) 
{ 
         
switch(i) //位选,选择点亮的数码管, 
{ 
         
case(0): LSA=0;LSB=0;LSC=0; break;//显示第 0 位 
case(1): LSA=1;LSB=0;LSC=0; break;//显示第 1 位 
case(2): LSA=0;LSB=1;LSC=0; break;//显示第 2 位 
case(3): LSA=1;LSB=1;LSC=0; break;//显示第 3 位 
case(4): LSA=0;LSB=0;LSC=1; break;//显示第 4 位 
case(5): LSA=1;LSB=0;LSC=1; break;//显示第 5 位
} 
P0=DisplayData1[i];//发送数据 
delay(100); //间隔一段时间扫描 
P0=0x00;//消隐
}
}

void DigDisplay2()//时间数码管数字显示
{ 
        
   u8 i;
 for(i=0;i<8;i++)
 { 
        
  switch(i)  //位选,选择点亮的数码管,
  { 
        
   case(0):
    LSA=0;LSB=0;LSC=0; break;//显示第0位
   case(1):
    LSA=1;LSB=0;LSC=0; break;//显示第1位
   case(2):
    LSA=0;LSB=1;LSC=0; break;//显示第2位
   case(3):
    LSA=1;LSB=1;LSC=0; break;//显示第3位
   case(4):
    LSA=0;LSB=0;LSC=1; break;//显示第4位
   case(5):
    LSA=1;LSB=0;LSC=1; break;//显示第5位
   case(6):
    LSA=0;LSB=1;LSC=1; break;//显示第6位
   case(7):
    LSA=1;LSB=1;LSC=1; break;//显示第7位 
  }
  P0=DisplayData2[i];//发送段码
  delay(100); //间隔一段时间扫描 
  P0=0x00;//消隐
 } 
}

void datapros2()//时间数码管数字设置
{ 
        
 DisplayData2[0]=smgduan[hour/10];
 DisplayData2[1]=smgduan[hour%10];
 DisplayData2[2]=0x40; 
 DisplayData2[3]=smgduan[minute/10];
 DisplayData2[4]=smgduan[minute%10];
 DisplayData2[5]=0x40;
 DisplayData2[6]=smgduan[second/10];
 DisplayData2[7]=smgduan[second%10]; 
}

void datapros4()//星期数码管数字设置
{ 
        
 DisplayData4[0]=0x00;
 DisplayData4[1]=0x00;
 DisplayData4[2]=0x00; 
 DisplayData4[3]=0x00;
 DisplayData4[4]=

相关文章