基于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

  • 13
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

K11mvp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值