基于51单片机的电子时钟设计

1.设计任务

利用AT89C51单片机为核心控制元件,设计一个简易的数字时钟,设计的系统实用性强、操作简单,实现了智能化、数字化。

       它可以对年、月、日、时、分、秒进行计时,而且DS1302的使用寿命长,误差小。对于数字电子时钟采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒等信息,还具有时间校准等功能。该电路采用ATC89C52单片机作为核心,功耗小,能在5V的低压工作,电压可选用4.5~5.5V电压供电。此电子时钟具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。

2. 设计要求

2.1系统方案论证

根据设计任务,分析设计系统的组成,给出实现设计任务的几种方案,分析比较几种设计方案的优略,本着尽量以软件代替硬件,同时力求电路简单,工作可靠的原则,确定总体设计方案。

2.2系统硬件电路设计

根据系统设计方案进行软、硬件的分配,软、硬件设计分别进行。硬件设计包括单片机最小系统和扩展接口及配置,硬件结构在设计时要选择合适的元器件,硬件电路要简洁、工作可靠,需用Proteus绘制整个系统的电路仿真原理图。

2.3软件设计

根据该系统要求的功能进行软件设计,简述软件的功能,并根据每个模块的功能绘制软件流程图,根据流程图编写程序并汇编调试通过;列出软件清单,软件清单要求加以注释。


#include "main.h"
 
/*---------------------------------------------------------------------------*/
#define TIMER0_MODE1_INIT_VALUE_H         (0x4C)  //50ms
#define TIMER0_MODE1_INIT_VALUE_L         (0x00)

#define LCD1602_PORT  P0  //1602引脚定义
sbit LCD1602_PIN_RS = P3^4; //1602引脚定义
sbit LCD1602_PIN_RW = P3^5; //1602引脚定义
sbit LCD1602_PIN_EN = P3^6; //1602引脚定义

sbit k1 = P1^0;
sbit k2 = P1^1;
sbit k3 = P1^2;
sbit k4 = P1^3;

/*---------------------------------------------------------------------------*/
idata uint8_t display_buf[16];
static bit sec_flag = 0;

static uint8_t set_flag = 0;

static data_time_t data_time;

/*---------------------------------------------------------------------------*/
static void timer0_mode1_init(void)
{
	TMOD &= 0xF0;		///<设置定时器0为方式1
	TMOD |= 0x01;		///<设置定时器0为方式1
	TL0 = TIMER0_MODE1_INIT_VALUE_L;///<设定定时器初值
	TH0 = TIMER0_MODE1_INIT_VALUE_H;			
	TF0 = 0;		///<清除TF0标志
	TR0 = 1;		///<定时器0开始计时
  ET0 = 1;    ///<允许定时/计数器0中断
}
static void lcd1602_delay_1us(void)
{
  _nop_();  //延时1us
}
/*---------------------------------------------------------------------------*/
void lcd1602_delay_1ms(void)
{
	unsigned char i, j; //定义变量

	_nop_();//延时1us
	i = 2;//变量赋值
	j = 199;//变量赋值
	do
	{
		while (--j);//延时
	} while (--i);//延时
}
/*---------------------------------------------------------------------------*/
static uint8_t lcd1602_read_state(void)
{
 	uint8_t state;//定义变量

  ///下面为lcd操作时序
	LCD1602_PIN_RS = 0; //RS置低
  LCD1602_PIN_RW = 1;//RW置高
  LCD1602_PIN_EN = 1;//EN置高
  lcd1602_delay_1us();//延时
	state = LCD1602_PORT;//读取数据
	LCD1602_PIN_EN = 0;//EN置低
  lcd1602_delay_1us();//延时
  
	return state; //返回数据
}
/*---------------------------------------------------------------------------*/
static void lcd1602_busy_wait(void)
{
  uint16_t timeout;//定义变量

  timeout  = 0xffff;//变量赋值
 	while((lcd1602_read_state() & 0x80) == 0x80){ //读取状态是否忙
    timeout--;  //时间减1
    if(timeout == 0){ //如果时间为0
      break; //跳出循环
    }
  }
	lcd1602_delay_1us();  //延时
}
/*---------------------------------------------------------------------------*/
static void lcd1602_write_data(uint8_t dat)
{
  ///下面为lcd1602操作时序
 	lcd1602_busy_wait();  //等待是否空闲
	LCD1602_PIN_RS = 1;//RS置高
  LCD1602_PIN_RW = 0;//RW置低
  LCD1602_PIN_EN = 0;//EN置低
  LCD1602_PORT = dat;//写入数据
  LCD1602_PIN_EN = 1;//EN置高
  lcd1602_delay_1us();//延时
  LCD1602_PIN_EN = 0;	//EN置低
}
/*---------------------------------------------------------------------------*/
static void lcd1602_write_command(uint8_t cmd)
{
  ///下面为lcd1602操作时序
 	lcd1602_busy_wait();//等待是否空闲
	LCD1602_PIN_RS = 0;//RS置高
  LCD1602_PIN_RW = 0;//RW置低
  LCD1602_PIN_EN = 0;//EN置低
  LCD1602_PORT = cmd;//写入命令
  LCD1602_PIN_EN = 1;//EN置高
  lcd1602_delay_1us();//延时
  LCD1602_PIN_EN = 0;	//EN置低
}
/*---------------------------------------------------------------------------*/
void lcd1602_init(void)
{
 	lcd1602_write_command(0x38); ///<设置16 X 2显示, 5 X 7点阵, 8位数据接口
	lcd1602_delay_1ms();	//延时
	lcd1602_write_command(0x01); ///<显示清0,数据指针清0
	lcd1602_delay_1ms();	//延时
	lcd1602_write_command(0x06); ///<设置写一个字符后地址加1
	lcd1602_delay_1ms();	//延时
	lcd1602_write_command(0x0c); ///<设置开显示,不显示光标
	lcd1602_delay_1ms();//延时
}
/*---------------------------------------------------------------------------*/
void lcd1602_display_char(    uint8_t      x, uint8_t y, uint8_t ch )
{
  if(x > 15 || y > 1){//如果超出范围
    return; //返回
  }
  if(y == 0){ //显示第一行
    lcd1602_write_command(x | 0x80);///<设置LCD1602第一行要显示的光标位置
  }else if(y == 1){//显示第二行
    lcd1602_write_command(x | 0x80 | 0x40);///<设置LCD1602第二行要显示的光标位置
  }
  lcd1602_write_data( ch );//写入数据
}
/*---------------------------------------------------------------------------*/
void lcd1602_display_string(    uint8_t       x, uint8_t y, uint8_t * str )
{
  while(*str != '\0'){  //等待数组为空
    lcd1602_display_char(x, y, *str); ///<显示一个字符
    str++;  ///<显示下一个字符
    x++;    ///<显示下一个位置
    if(x > 15){//如果超出范围
    }
  }
}
/*---------------------------------------------------------------------------*/
void lcd1602_clear_display(void)
{
  lcd1602_write_command(0x01);//输出命令
  lcd1602_delay_1ms();//延时
}
/*---------------------------------------------------------------------------*/
void lcd1602_open_cursor(    uint8_t       x, uint8_t y)
{
  if(y == 0){ //显示第一行
    lcd1602_write_command(x + 0x80);///<设置LCD1602第一行要显示的光标位置
  }else if(y == 1){//显示第二行
    lcd1602_write_command(x + 0x80 + 0x40);///<设置LCD1602第二行要显示的光标位置
  }

  lcd1602_write_command(0x0f);			// 启动光标闪烁
}
/*---------------------------------------------------------------------------*/
void lcd1602_close_cursor(    void)
{
  lcd1602_write_command(0x0C);			// 关闭光标闪烁
}
/*---------------------------------------------------------------------------*/
static void display_time(void)
{
  lcd1602_close_cursor();
  
  ds1302_read_time(&data_time); //读取ds1302时间
  sprintf(display_buf, "D:20%02bu-%02bu-%02bu", data_time.Year, data_time.Month, data_time.Day);
  lcd1602_display_string(0, 0, display_buf);    //显示时间


  sprintf(display_buf, "T:%02bu:%02bu:%02bu", data_time.Hour, data_time.Minute, data_time.Second);
  lcd1602_display_string(0, 1, display_buf);    //显示时间
}
/*---------------------------------------------------------------------------*/
void delay(uint16_t i)
{
	while(i--);	
}
/*---------------------------------------------------------------------------*/
static void display_cursor(void)
{
  if(set_flag == 1)
  {
    lcd1602_open_cursor(5, 0);
  }
  else if(set_flag == 2)
  {
    lcd1602_open_cursor(8, 0);
  }
  else if(set_flag == 3)
  {
    lcd1602_open_cursor(11, 0);
  }
  else if(set_flag == 4)
  {
    lcd1602_open_cursor(3, 1);
  }
  else if(set_flag == 5)
  {
    lcd1602_open_cursor(6, 1);
  }
  else if(set_flag == 6)
  {
    lcd1602_open_cursor(9, 1);
  }
  else if(set_flag == 7)
  {
    lcd1602_open_cursor(12, 1);
  }
  else if(set_flag == 8)
  {
    lcd1602_open_cursor(15, 1);
  }
}

/*---------------------------------------------------------------------------*/
static void key_add(void)
{
  if(set_flag == 1)
  {
    if(data_time.Year < 99)
      data_time.Year++;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 2)
  {
    if(data_time.Month < 12)
      data_time.Month++;
    else
      data_time.Month = 1;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 3)
  {
    if(data_time.Day < 31)
      data_time.Day++;
    else
      data_time.Day = 1;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 4)
  {
    data_time.Hour++;
    if(data_time.Hour > 23)
      data_time.Hour = 0;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 5)
  {
    data_time.Minute++;
    if(data_time.Minute > 59)
      data_time.Minute = 0;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 6)
  {
    data_time.Second++;
    if(data_time.Second > 59)
      data_time.Second = 0;
    ds1302_write_time(&data_time);
    display_time();
  }
}
/*---------------------------------------------------------------------------*/
static void key_dec(void)
{
  if(set_flag == 1)
  {
    if(data_time.Year)
      data_time.Year--;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 2)
  {
    if(data_time.Month > 1)
      data_time.Month--;
    else
      data_time.Month = 12;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 3)
  {
    if(data_time.Day > 1)
      data_time.Day--;
    else
      data_time.Day = 28;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 4)
  {
    if(data_time.Hour)
      data_time.Hour--;
    else
      data_time.Hour = 23;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 5)
  {
    if(data_time.Minute)
      data_time.Minute--;
    else
      data_time.Minute = 59;
    ds1302_write_time(&data_time);
    display_time();
  }
  else if(set_flag == 6)
  {
    if(data_time.Second)
      data_time.Second--;
    else
      data_time.Second = 59;
    ds1302_write_time(&data_time);
    display_time();
  }
}
/*---------------------------------------------------------------------------*/
void keypros()
{
	if(k1==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(k1==0)	 //再次判断按键是否按下
		{
      set_flag++;
      if(set_flag > 6)
        set_flag = 0;
		}
		while(!k1);	 //检测按键是否松开
	}

  if(k2==0)		  //检测按键K2是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(k2==0)	 //再次判断按键是否按下
		{
      key_add();
		}
		while(!k2);	 //检测按键是否松开
	}

  if(k3==0)		  //检测按键K3是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(k3==0)	 //再次判断按键是否按下
		{
      key_dec();
		}
		while(!k3);	 //检测按键是否松开
	}

  if(k4==0)		  //检测按键K3是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(k4==0)	 //再次判断按键是否按下
		{
      if(set_flag)
        set_flag = 0;
		}
		while(!k4);	 //检测按键是否松开
	}
}
/*---------------------------------------------------------------------------*/
void main(void)
{
  timer0_mode1_init(); //定时器初始化
  lcd1602_init();//1602初始化
  ds1302_init();//ds1302初始化
  __enable_irq();//开中断
  ds1302_read_time(&data_time); //读取ds1302时间
  data_time.Hour = 0;
  data_time.Minute = 0;
  data_time.Second = 0;
  ds1302_write_time(&data_time);
  display_time();

  while(1){
    if(sec_flag) //1s时间到
    {
      sec_flag = 0;  //秒标志清零

      if(set_flag == 0)
      {
        display_time();
      }      
      display_cursor();
    }
    keypros();
  }
}
/*---------------------------------------------------------------------------*/
void timer0() interrupt 1   //定时器中断
{
  static uint8_t clock_ticks = 0;
  TL0 = TIMER0_MODE1_INIT_VALUE_L;///<设定定时器初值
	TH0 = TIMER0_MODE1_INIT_VALUE_H;
  ++clock_ticks;
  if(clock_ticks == 20) //进入20次,1s
  {
    clock_ticks = 0;
    sec_flag = 1;  //秒标志置1
  }
}
/*---------------------------------------------------------------------------*/

完整代码点开链接私信  免费  获取。

【iBot机器人工作室的个人空间-哔哩哔哩】 https://b23.tv/ryUWVKa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/206449.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

qt-C++笔记之点击按钮弹出文件资源管理器选择文件后把文件路径赋值给一个QString

qt-C笔记之点击按钮弹出文件资源管理器选择文件后把文件路径赋值给一个QString code review! 文章目录 qt-C笔记之点击按钮弹出文件资源管理器选择文件后把文件路径赋值给一个QString1.运行2.main.cpp3.qt_FileDialog.pro4.QFileDialog类详解 1.运行 2.main.cpp 代码 #inclu…

loading...字符变化动画

公司业务一个简单的需求loading...文字动画&#xff0c;不想用js实现&#xff0c;问过GPT后学习了css写法 效果预览 代码实现 keyframes text-change {0% { content: "."; }33% { content: ".."; }66% { content: "..."; }}.text_loading_anima…

⭐ Unity 里让 Shader 动画在 Scene 面板被持续刷新

写 Unity Shader的时候&#xff0c;只有播放状态下的 Game 面板能看到Shader 顺畅的动态效果&#xff0c;不方便。 想要带有动态效果的 Shader 在 Scene 面板持续更新动画&#xff0c;只需要打开一个开关就能让 Scene 持续刷新动画了。 感谢大家的观看&#xff0c;您的点赞和关…

使用Golang构建高性能网络爬虫

目录 一、Golang的特点 二、构建网络爬虫的步骤 三、关键技术和注意事项 使用协程进行并发处理 使用通道进行协程间的通信 合理控制并发数和处理速度 遵守网站使用协议和法律法规 防止被网站封禁或限制访问 优化网页解析和数据处理 异常处理和错误处理 日志记录和监控…

shell编程系列(7)-使用wc进行文本统计

文章目录 前言wc命令的使用wc命令的参数说明&#xff1a;统计字数统计行数打印文本行号 结语 前言 统计功能也是我们在shell编程中经常碰到的一个需求&#xff0c;wc命令可以适用于任何需要统计的数据&#xff0c;不只是统计文本&#xff0c;配合ls命令我们可以统计文件的个数…

英国人工智能初创公司Stability AI面临卖身压力;深度学习中的检索增强生成简介

&#x1f989; AI新闻 &#x1f680; 英国人工智能初创公司Stability AI面临卖身压力 摘要&#xff1a;多位知情人士透露&#xff0c;英国人工智能初创公司Stability AI正寻求出售公司&#xff0c;因为投资者对其财务状况的压力越来越大。管理层最近几周一直将自己标榜为收购…

ArkTS-日期滑动选择器弹窗

日期滑动选择器弹窗 根据指定的日期范围创建日期滑动选择器&#xff0c;展示在弹窗上。 示例 lunar&#xff1a; 接受一个boolean值&#xff0c;日期是否显示为农历。 Entry Component struct DatePickerDialogExample {selectedDate: Date new Date("2010-1-1")Sta…

2023年亚太杯数学建模A题——深度学习苹果图像识别(

Image Recognition for Fruit-Picking Robots 水果采摘机器人的图像识别功能 问题 1&#xff1a;计数苹果 根据附件 1 中提供的可收获苹果的图像数据集&#xff0c;提取图像特征&#xff0c;建立数学模型&#xff0c;计算每幅图像中的苹果数量&#xff0c;并绘制附件 1 中所有…

(四)基于高尔夫优化算法GOA求解无人机三维路径规划研究(MATLAB代码)

一、无人机模型简介&#xff1a; 单个无人机三维路径规划问题及其建模_IT猿手的博客-CSDN博客 参考文献&#xff1a; [1]胡观凯,钟建华,李永正,黎万洪.基于IPSO-GA算法的无人机三维路径规划[J].现代电子技术,2023,46(07):115-120 二、高尔夫优化算法GOA简介 高尔夫优化算法…

和鲸科技与国科环宇建立战略合作伙伴关系,以软硬件一体化解决方案促进科技创新

近日&#xff0c;在国科环宇土星云算力服务器产品发布会暨合作伙伴年度会上&#xff0c;和鲸科技与国科环宇正式完成战略伙伴签约仪式&#xff0c;宣布达成战略合作伙伴关系。未来&#xff0c;双方将深化合作&#xff0c;充分发挥在产品和市场方面的互补优势&#xff0c;为企事…

【广州华锐互动】节约用水VR互动教育:身临其境体验水资源的珍贵!

随着技术的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术在许多领域得到了广泛应用。在节水宣传教育方面&#xff0c;VR技术也展现出了其独特的优势。与传统宣传教育方式相比&#xff0c;节约用水VR互动教育具有更加沉浸式、互动性和实践性的特点&#xff0c;能…

UWB高精度定位系统源码,支持零维、一维、二维等多种定位方式

智慧工厂人员定位系统源码&#xff0c;UWB高精度定位系统源码 技术架构&#xff1a;java spring boot vue mysql单体服务 硬件&#xff08;UWB定位基站、卡牌&#xff09; 定位能力&#xff1a;支持零维、一维、二维等多种定位方式&#xff0c;满足各种不同的定位需求&#xf…

指数退避和抖动

目录 引入 OCC 添加退避机制 添加抖动机制 小结 引入 OCC 乐观并发控制&#xff08;Optimistic Concurrency Control&#xff0c;OCC&#xff09;是一种既能保证多个写入者安全地修改单个对象又能避免丢失写入的古老方法OCC具有三个优点&#xff1a;只要底层存储可用&#…

Linux 常用命令集

1、根据端口查询进程号&#xff1a; netstat -nlp | grep 10050 或者使用 lsof -i:10050 2、查询所有服务进程号&#xff1a;top 3、根据进程号查询服务路径 ll /proc/28145/cwd 4、同步网络时间 yum install -y ntpdate ntpdate ntp.aliyun.com 设置定时任务 更新时间 * * * *…

Day46力扣打卡

最近一直在做以前的题&#xff0c;刷题量都没有怎么增长&#xff0c;感觉自己算法一直不太行&#xff0c;但也只能菜就多练了。 打卡记录 由子序列构造的最长回文串的长度&#xff08;区间DP&#xff09; 链接 第二次刷这道题&#xff0c;相比上回思路来的很快&#xff0c;但…

VS Code C++可视化调试配置Natvis,查看Qt、STL变量内容

VS Code C可视化调试配置Natvis 使用GlobalVisualizersDirectory Windows下 C:\Users\YourName\.vscode\extensions\ms-vscode.cpptools-1.18.5-win32-x64\debugAdapters\vsdbg\bin\Visualizers\Linux下 ~\.vscode\extensions\ms-vscode.cpptools-1.18.5-win32-x64\debugAd…

PCB布线为什么不能走直角或锐角-笔记

PCB布线为什么不能走直角或锐角-笔记 摘要一.PCB走线在直角转弯的地方&#xff0c;信号前后部分相互影响这几个理由我们来一一分析一下传输线的直角带来的寄生电容从阻抗的角度来看直角的尖角产生放电或者电磁辐射走线直角的工艺问题 摘要 有一定熟悉画过PCB板的人或者PCB教学…

Spring三级缓存处理循环依赖的过程

Spring三级缓存 Spring三级缓存是什么&#xff1f; 一级缓存&#xff1a;单例池。存放的是完整的Bean对象。经过完整的生命周期。二级缓存&#xff1a;存放需要提前暴露的Bean对象。也就不完整的Bean对象。需要提前暴露就是指&#xff0c;可能会被循环依赖。(这里可能需要用代…

中国毫米波雷达产业分析4——毫米波雷达企业介绍

一、矽典微 &#xff08;一&#xff09;公司简介 矽典微致力于实现射频技术的智能化&#xff0c;专注于研发高性能无线技术相关芯片&#xff0c;产品广泛适用于毫米波传感器、下一代移动通信、卫星通信等无线领域。 整合自身在芯片、系统、软件、算法等领域的专业能力&#xf…

c++面试题

1.static的使用 1&#xff09;修饰局部变量&#xff1a;在函数内部使用static修饰局部变量&#xff0c;会使它成为静态局部变量。静态局部变量只会被初始化一次&#xff0c;且只有在第一次调用该函数时才会被初始化&#xff0c;之后每次调用该函数时都会保留上一次的值.从原来…