大家好,又见面了,我是你们的朋友全栈君。
本设计的主要任务是计时和显示。
设计任务的具体要求如下:
- 计时:第一种是通过单片机内部的定时器/计数器来实现为软时钟。第二种是通过专用的硬件时钟芯片来实现为硬时钟。
- 显示:第一种是数码管显示,分为静态显示和动态显示,动态显示需要占用cpu大量时间来运行,不需要复杂的驱动程序,制作成本不高。第二种是LCD1602液晶显示,能显示出较多的信息并且数字清晰,应用广泛,但是不便于观察。
总设计思路阐述
1.时钟方案设计及选择:目前有DS3231、DS302、DS12C887三种时钟芯片。本次设计选择DS3231,
需要应用IC总线协议。
2.核心控制区设计方案:选用STC89C51作为核心控制单元。
3.显示原件:采用LCD1602液晶显示。
4.键盘设置:设计为时、分、秒的调整按键。
5.控制继电器:选择5V/1A继电器,驱动LCD液晶显示。
6.外围电路:复位电路,晶振。
7.时钟时序问题
代码如下:
/*******************************************************************************
实验名 :高精度时钟显示和控制
使用的IO : P3^0 SDA
P3^1 SCL
实验效果:1602 显示时钟,按S4进入时钟设置并暂停时钟,按S1选择时钟设置的时分秒日月周年,按S3使时间加一
*******************************************************************************/
#include<reg51.h>
#include"lcd.h"
#include"ds3231.h"
sbit K1=P3^5;//按键IO口定义
sbit K2=P3^4;
sbit K3=P3^3;
sbit K4=P3^2;
void Int0Configuration();//外部中断初始化函数声明
void LcdDisplay();//LCD初始化函数声明
unsigned char SetState,SetPlace;
void Delay10ms(void); //10ms延迟函数
/*******************************************************************************
* 函数名 : main
* 函数功能 : 主函数
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void main()
{
unsigned char i;
Int0Configuration();//外部中断初始化
LcdInit();//LCD初始化
DS3231_Init();//DS3231模块初始化
while(1)
{
if(SetState==0)//标志位为0时读取时间
{
DS3231_Read_time();
}
else
{
if(K1==0) //检测按键K1是否按下
{
Delay10ms(); //消除抖动
if(K1==0)
{
while(!K1);//检测是否松手
SetPlace++;
if(SetPlace>=7)
SetPlace=0;
}
}
if(K2==0) //检测按键K2是否按下
{
Delay10ms(); //消除抖动
if(K2==0)
{
while(!K2);//松手检测
TIME[SetPlace]++;
if((TIME[SetPlace]>=60)&&(SetPlace<2)) //分秒只能到60
{
TIME[SetPlace]=0;
}
if((TIME[SetPlace]>=24)&&(SetPlace==2)) //小时只能到23
{
TIME[SetPlace]=0;
}
if((TIME[SetPlace]>=32)&&(SetPlace==3)) //日只能到31
{
TIME[SetPlace]=0;
}
if((TIME[SetPlace]>=13)&&(SetPlace==4)) //月只能到12
{
TIME[SetPlace]=0;
}
if((TIME[SetPlace]>=8)&&(SetPlace==5)) //周只能到7
{
TIME[SetPlace]=1;
}
}
while((i<50)&&(K2==0)) //检测按键是否松开
{
Delay10ms();
i++;
}
i=0;
}
}
LcdDisplay(); //LCD显示函数
}
}
/*******************************************************************************
* 函数名 : LcdDisplay()
* 函数功能 : 显示函数
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void LcdDisplay()
{
LcdWriteCom(0x80+0X40);
LcdWriteData('0'+TIME[2]/10); //时
LcdWriteData('0'+(TIME[2]%10));
LcdWriteData('-');
LcdWriteData('0'+TIME[1]/10); //分
LcdWriteData('0'+(TIME[1]%10));
LcdWriteData('-');
LcdWriteData('0'+TIME[0]/10); //秒
LcdWriteData('0'+(TIME[0]%10));
LcdWriteCom(0x80);
LcdWriteData('2');
LcdWriteData('0');
LcdWriteData('0'+TIME[6]/10); //年
LcdWriteData('0'+(TIME[6]%10));
LcdWriteData('-');
LcdWriteData('0'+TIME[4]/10); //月
LcdWriteData('0'+(TIME[4]%10));
LcdWriteData('-');
LcdWriteData('0'+TIME[3]/10); //日
LcdWriteData('0'+(TIME[3]%10));
LcdWriteCom(0x8D);
LcdWriteData('0'+(TIME[5]%10)); //星期
}
/*******************************************************************************
* 函数名 : Int0Configuration()
* 函数功能 : 配置外部中断0
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Int0Configuration()
{
//设置INIT0
IT0=1;//跳边沿触发方式(下降沿)
EX0=1;//打开INT0中断允许
EA=1;// 打开总中断
}
/*******************************************************************************
* 函数名 : Int0()
* 函数功能 : 外部中断0中断服务函数
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Int0() interrupt 0 using 1
{
Delay10ms();
if(K4==0)
{
while(!K4);//松手检测
SetState=~SetState;//标志位取反
SetPlace=0;
DS3231_Init();//DS3231初始化
}
}
/*******************************************************************************
* 函数名 : Delay10ms()
* 函数功能 : 延迟函数,延时10ms
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Delay10ms(void) //Îó²î 0us
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=38;b>0;b--)
for(a=130;a>0;a--);
}
/**********************************************************************************
**********************************************************************************/
#ifndef __DS3231_H_
#define __DS3231_H_
//---包含头文件---//
#include<reg51.h>
#include<intrins.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//---定义ds1302使用的IO口---//
sbit DS3231_IIC_SDA=P3^0;
sbit DS3231_IIC_SCL=P3^1;
//---定义全局函数---// //时基、走时函数
void DS3231_Read_time(); //读取时间
void DS3231_write_time(); //写入时间
void DS3231_Init(); //时间模块初始化
//---加入全局变量--//
extern uchar TIME[7]; //加入全局变量
#endif
/**********************************************************************************
**********************************************************************************/
#include"ds3231.h"
//---DS3231写入和读取时分秒的地址命令//
//---秒分时日月周年最低读写位;-------//
uchar code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
uchar code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
uchar code rtc_address[7]={0x00,0x01,0x02,0x04,0x05,0x03,0x06};//秒分时日月周年最低读写位
uchar RTC_Data[8];
uchar RTC_Data1[8];
uchar RTC_Set_ID;
bit T0_Flag;
bit Open_Flag;
bit shezhi_display_flag;
//---DS3231初始化时间格式为2021年6月28日星期一22点00分00秒---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
uchar TIME[7] = {0, 0, 0x22, 0x28, 0x06, 0x01, 0x21};
/*******************************************************************************
* 函数名 : Ds3231Write
* 函数功能 : 向DS3231命令(地址+数据)
* 输入 : addr,dat
* 输出 : 无
*******************************************************************************/
void DS3231_IIC_delay()//IIC延迟函数
{
_nop_();_nop_();_nop_();_nop_();
}
void DS3231_IIC_start()//起始信号
{
DS3231_IIC_SDA=1;
DS3231_IIC_SCL=1;
DS3231_IIC_delay();
DS3231_IIC_SDA=0;
DS3231_IIC_delay();
DS3231_IIC_SCL=0;
}
void DS3231_IIC_stop()//停止信号
{
DS3231_IIC_SDA=0;
DS3231_IIC_delay();
DS3231_IIC_SCL=1;
DS3231_IIC_delay();
DS3231_IIC_SDA=1;
DS3231_IIC_delay();
}
bit DS3231_IIC_Tack()//接收应答
{
bit ack;
DS3231_IIC_SDA=1;
DS3231_IIC_delay();
DS3231_IIC_SCL=1;
DS3231_IIC_delay();
ack=DS3231_IIC_SDA;
DS3231_IIC_delay();
DS3231_IIC_SCL=0;
DS3231_IIC_delay();
return ack;
}
void DS3231_IIC_write_byte(uchar Data)//写入数据函数
{
uchar i;
for(i=0;i<8;i++)
{
if(Data&0x80)
DS3231_IIC_SDA=1;
else
DS3231_IIC_SDA = 0;
DS3231_IIC_delay();
DS3231_IIC_SCL=1;
DS3231_IIC_delay();
DS3231_IIC_SCL=0;
DS3231_IIC_delay();
Data=Data<<1;
}
}
uchar DS3231_IIC_read_byte()//读取数字函数
{
uchar i;
uchar Data;
for(i=0;i<8;i++)
{
DS3231_IIC_SCL=1;
DS3231_IIC_delay();
Data=Data<<1;
DS3231_IIC_delay();
if(DS3231_IIC_SDA)
Data=Data|0x01;
DS3231_IIC_SCL=0;
DS3231_IIC_delay();
}
return Data;
}
void DS3231_IIC_single_byte_write(uchar Waddr,uchar Data)//写入数据函数
{
DS3231_IIC_start();
DS3231_IIC_write_byte(0xd0);
DS3231_IIC_Tack();
DS3231_IIC_write_byte(Waddr);
DS3231_IIC_Tack();
DS3231_IIC_write_byte(Data);
DS3231_IIC_Tack();
DS3231_IIC_stop();
}
uchar DS3231_IIC_single_byte_read(uchar Waddr)//读取数字函数
{
uchar Data;
DS3231_IIC_start();
DS3231_IIC_write_byte(0xd0);
DS3231_IIC_Tack();
DS3231_IIC_write_byte(Waddr);
DS3231_IIC_Tack();
DS3231_IIC_stop();
DS3231_IIC_start();
DS3231_IIC_write_byte(0xd1);
DS3231_IIC_Tack();
Data=DS3231_IIC_read_byte();
DS3231_IIC_delay();
DS3231_IIC_stop();
return Data;
}
void DS3231_Read_time()//读取时间
{
uchar i,tmp,tmp1,tmp2;
for(i=0;i<7;i++)
{
tmp = DS3231_IIC_single_byte_read(rtc_address[i]);
tmp1 = tmp/16;
tmp2 = tmp%16;
TIME[i]=tmp1*10+tmp2;
}
}
void DS3231_write_time()//写入时间
{
uchar i,tmp;
for(i=0;i<8;i++)
{
tmp=RTC_Data[i]/10;//BCD??
RTC_Data1[i]=RTC_Data[i]%10;
RTC_Data1[i]=RTC_Data1[i]+tmp*16;
}
for(i=0;i<7;i++)
{
DS3231_IIC_single_byte_write(rtc_address[i],RTC_Data1[i]);
}
DS3231_IIC_single_byte_write(0x0e,0x0c);
}
void DS3231_Init()//DS3231初始化函数
{
uchar n;
DS3231_IIC_SDA=1;
DS3231_IIC_SCL=1;
DS3231_IIC_single_byte_write(0x0e,0x0c);
for (n=0; n<7; n++)//дÈë7¸ö×Ö½ÚµÄʱÖÓÐźţº·ÖÃëʱÈÕÔÂÖÜÄê
{
DS3231_IIC_single_byte_write(rtc_address[n],TIME[n]);
}
DS3231_IIC_single_byte_write(0x8E,0x80); //´ò¿ªÐ´±£»¤¹¦ÄÜ
}
/**********************************************************************************
**********************************************************************************/
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS
/**********************************
包含头文件
**********************************/
#include<reg51.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P2
sbit LCD1602_E=P0^5;
sbit LCD1602_RW=P0^6;
sbit LCD1602_RS=P0^7;
/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c); //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/
void LcdWriteData(uchar dat) ;
/*LCD1602初始化子程序*/
void LcdInit();
#endif
/**********************************************************************************
**********************************************************************************/
#include"lcd.h"
/*******************************************************************************
* 函 数 名 : Lcd1602_Delay1ms
* 函数功能 : 延时函数,延时1ms
* 输 入 : c
* 输 出 : 无
* 说 名 : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/
void Lcd1602_Delay1ms(uint c) //误差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/*******************************************************************************
* 函 数 名 : LcdWriteCom
* 函数功能 : 向LCD写入一个字节的命令
* 输 入 : com
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 0; //选择写入命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
// Lcd1602_Delay1ms(1);
LCD1602_DATAPINS = com << 4; //发送低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名 : LcdWriteData
* 函数功能 : 向LCD写入一个字节的数据
* 输 入 : dat
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择输入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择写入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = dat << 4; //写入低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名 : LcdInit()
* 函数功能 : 初始化LCD屏
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //开显示
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#else
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x32); //将8位总线转为4位总线
LcdWriteCom(0x28); //在四位线下的初始化
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#endif
效果如下:
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/129315.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...