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