51单片机+DS1302设计一个电子钟(LCD1602显示时间)

一、前言

电子钟是一种能够准确显示时间的设备,广泛应用于家庭、办公场所和公共场所,为人们提供了方便和准确的时间信息。本项目设计一个基于51单片机的电子钟,使用DS1302作为RTC时钟芯片,LCD1602作为显示屏,并通过串口方式连接上位机进行时间设置和闹钟设置。

STC89C52作为主控芯片,具有较高的性能和稳定性,可完成对外设的控制和数据处理。DS1302是一款低功耗的实时时钟芯片,能够提供准确的时间计数和日期功能。LCD1602是一款常用的字符型液晶显示屏,具有两行16列的显示区域,能够清晰显示时间和其他相关信息。

通过串口连接上位机,用户可以方便地设置电子钟的时间和闹钟时间,实现个性化需求。电子钟带有一个蜂鸣器,可以根据设置的闹钟时间进行响铃,提醒用户。

image-20230913112300737

image-20230913112208076

电子钟具有以下功能:

(1)显示当前时间和日期:LCD1602显示屏将实时更新并显示当前的时间和日期信息。

(2)时间设置:通过串口连接上位机,用户可以进行时间的设置,包括小时、分钟和秒。

(3)日期设置:用户可以通过上位机设置当前的年、月和日。

(4)闹钟设置:用户可以设置闹钟的时间,包括小时和分钟。到达设定时间时,蜂鸣器将响铃提醒用户。

(5)整点报时:每到整点,蜂鸣器将发出短促的提示音,提醒用户当前时间。

(6)闹钟响铃:当闹钟时间到达时,蜂鸣器将持续响铃,直到用户停止。

(7)该项目将借助STC89C52单片机的控制能力和串口通信功能,结合DS1302时钟芯片和LCD1602显示屏,实现一个简单而实用的电子钟。用户可以根据自己的

(8)需求进行时间设置和闹钟设置,方便实用,并且具有较高的准确性和稳定性。

二、项目的设计思路

项目的设计思路分为硬件设计和软件设计两部分。

2.1 硬件设计思路

(1)主控芯片选择:选择STC89C52作为主控芯片,由于其较高的性能和稳定性,适合用于控制和数据处理。

(2)RTC时钟芯片选择:选择DS1302作为RTC时钟芯片,具有低功耗、精确计时和日期功能。

(3)显示屏选择:选择LCD1602作为显示屏,它具有两行16列的字符显示区域,能够清晰显示时间和其他相关信息。

(4)串口连接:设计串口连接电路,实现与上位机的通信,用于时间设置和闹钟设置。

(5)蜂鸣器:添加蜂鸣器模块,用于整点报时和闹钟响铃功能。

(6)按键输入:添加按键输入模块,用于用户操作,如切换设置模式、调整时间和设置闹钟。

2.2 软件设计思路

(1)初始化设置:在程序启动时,进行硬件初始化,包括配置主控芯片的引脚、初始化DS1302时钟芯片和LCD1602显示屏。

(2)时间获取与显示:通过DS1302时钟芯片获取当前的时间和日期,并将其显示在LCD1602显示屏上。

(3)串口通信:通过串口与上位机进行通信,接收上位机发送的时间设置和闹钟设置指令,并进行相应的处理

(4)时间设置:根据上位机发送的时间设置指令,更新DS1302时钟芯片的时间计数器。

(5)日期设置:根据上位机发送的日期设置指令,更新DS1302时钟芯片的日期计数器。

(6)闹钟设置:根据上位机发送的闹钟设置指令,设置闹钟时间,并将其保存在主控芯片的内部存储器中。

(7)整点报时:通过检测DS1302时钟芯片的小时计数器,当小时值变化时,触发蜂鸣器发出短促的提示音。

(8)闹钟响铃:通过比较当前时间和保存的闹钟时间,当达到闹钟时间时,触发蜂鸣器持续响铃,直到用户停止或设定的时间段结束。

三、项目硬件接线

(1)STC89C52与DS1302:

STC89C52的P2.0口连接到DS1302的SCLK(时钟)引脚,用于提供时钟信号。

STC89C52的P2.1口连接到DS1302的IO(数据)引脚,用于数据传输。

STC89C52的P2.2口连接到DS1302的RST(复位)引脚,用于对DS1302进行复位操作。

(2)STC89C52与LCD1602:

STC89C52的P0口连接到LCD1602的D0-D7引脚,用于传输字符数据和控制信号。

STC89C52的P2.3口连接到LCD1602的RS(寄存器选择)引脚,用于选择数据或命令寄存器。

STC89C52的P2.4口连接到LCD1602的RW(读写选择)引脚,用于选择读或写操作。

STC89C52的P2.5口连接到LCD1602的E(使能)引脚,用于启动传输。

(3)STC89C52与蜂鸣器模块:

STC89C52的P3.7口连接到蜂鸣器模块的信号引脚,用于触发蜂鸣器响铃。

(4)串口通信接口。在STC89C52单片机上,串口引脚如下:

UART接收线(RXD):连接至外部设备的发送线。

STC89C52的P3.0口(RXD)用于接收串口数据。

UART发送线(TXD):连接至外部设备的接收线。

STC89C52的P3.1口(TXD)用于发送串口数据。

四、项目代码

4.1 DS1302时钟读取、设置

下面代码实现了,STC89C52读取DS1302时钟信息打印到串口,以及设置闹钟、时间读取、打印到串口的功能。其中,采用了UART通信进行与上位机交互,可以接收上位机发送过来的时间字符串,并据此设置闹钟和时间。

#include <reg52.h>
#include <stdio.h>

#define uchar unsigned char
#define uint unsigned int

// 定义DS1302时钟寄存器地址
#define DS1302_SEC_REG 0x80
#define DS1302_MIN_REG 0x82
#define DS1302_HR_REG 0x84
#define DS1302_DAY_REG 0x86
#define DS1302_MONTH_REG 0x88
#define DS1302_YEAR_REG 0x8C

// 定义DS1302控制寄存器命令
#define DS1302_CMD_WRITE 0x80
#define DS1302_CMD_READ 0x81

// 定义串口波特率为9600
#define BAUDRATE 9600
#define FOSC 11059200L
#define TIMER_INTERVAL (65536 - FOSC / 12 / BAUDRATE)

// 声明全局变量
uchar time_buffer[20]; // 存放时间字符串
uchar alarm_buffer[20]; // 存放闹钟时间字符串
uint i;
bit flag; // 标记是否接收到上位机的时间字符串

// 初始化UART模块
void InitUart() {
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = TIMER_INTERVAL / 256;
    TL1 = TIMER_INTERVAL % 256;
    PCON |= 0x80;
    SCON = 0x50;
    ES = 1;
    TR1 = 1;
    EA = 1;
}

// 将单个字节发送到串口
void SendData(uchar dat) {
    SBUF = dat;
    while (!TI);
    TI = 0;
}

// 将字符串发送到串口
void SendString(uchar *s) {
    while (*s != '\0') {
        SendData(*s++);
    }
}

// 初始化DS1302时钟芯片
void InitDS1302() {
    uchar i;

    // 使能DS1302写保护功能
    DS1302_CE = 0;
    DS1302_SCL = 0;
    DS1302_CE = 1;
    Write_DS1302(DS1302_CMD_WRITE | 0x8e, 0x80);

    // 关闭时钟允许,准备写入数据
    Write_DS1302(DS1302_CMD_WRITE | 0x90, 0x00);

    // 写入年月日时分秒周
    Write_DS1302(DS1302_SEC_REG, 0x00);
    Write_DS1302(DS1302_MIN_REG, 0x30);
    Write_DS1302(DS1302_HR_REG, 0x11);
    Write_DS1302(DS1302_DAY_REG, 0x08);
    Write_DS1302(DS1302_MONTH_REG, 0x09);
    Write_DS1302(DS1302_YEAR_REG, 0x21);
    Write_DS1302(0x8e, 0x00);

    // 初始化闹钟时间
    for (i = 0; i < 20; i++) {
        alarm_buffer[i] = 0;
    }
}

// 向DS1302写入数据
void Write_DS1302(uchar addr, uchar dat) {
    uchar i;

    DS1302_CE = 0;
    DS1302_SCL = 0;

    // 发送起始信号
    DS1302_CE = 1;
    DS1302_SCL = 1;
    DS1302_CE = 0;

    // 发送命令字节地址
    DS1302_WriteByte(addr);
    // 发送数据字节
    DS1302_WriteByte(dat);

    // 停止信号
    DS1302_SCL = 0;
    DS1302_CE = 1;

    // 延时至少1us
    for (i = 0; i < 10; i++);
}

// 向DS1302读取数据
uchar Read_DS1302(uchar addr) {
    uchar dat;
    uchar i;

    DS1302_CE = 0;
    DS1302_SCL = 0;

    // 发送起始信号
    DS1302_CE = 1;
    DS1302_SCL = 1;
    DS1302_CE = 0;

    // 发送命令字节地址
    DS1302_WriteByte(addr | 0x01);
    // 读取数据字节
    dat = DS1302_ReadByte();

    // 停止信号
    DS1302_SCL = 0;
    DS1302_CE = 1;

    // 延时至少1us
    for (i = 0; i < 10; i++);
    return dat;
}

// 读取DS1302时间并打印到串口
void ReadTime() {
    uchar sec, min, hour, day, month, year;
    sprintf(time_buffer, "Time: ");
    sec = Read_DS1302(DS1302_SEC_REG);
    min = Read_DS1302(DS1302_MIN_REG);
    hour = Read_DS1302(DS1302_HR_REG);
    day = Read_DS1302(DS1302_DAY_REG);
    month = Read_DS1302(DS1302_MONTH_REG);
    year = Read_DS1302(DS1302_YEAR_REG);
    sprintf(time_buffer + 6, "%02d:%02d:%02d %02d/%02d/%02d\r\n", hour, min, sec, day, month, year);
    SendString(time_buffer);
}

// 向DS1302写入闹钟时间
void SetAlarm(uchar *str) {
    uint i = 0;

    // 将字符串转换为数字
    while (str[i] != '\0') {
        alarm_buffer[i] = str[i] - '0';
        i++;
        if (i > 19) // 防止溢出
            break;
    }

    // 写入闹钟时间
    Write_DS1302(DS1302_CMD_WRITE | 0x81, alarm_buffer[10] << 4 | alarm_buffer[11]);
    Write_DS1302(DS1302_CMD_WRITE | 0x83, alarm_buffer[8] << 4 | alarm_buffer[9]);
    Write_DS1302(DS1302_CMD_WRITE | 0x85, alarm_buffer[6] << 4 | alarm_buffer[7]);
}

// 从串口接收数据中解析出时间信息
void ParseTime() {
    uchar i, j;
    uchar temp;
    for (i = 0; i < 20; i++) {
        time_buffer[i] = 0;
    }

    // 接收字符串格式为:hh:mm:ss dd/mm/yy
    for (i = 0; i < 8; i++) {
        temp = 0;
        for (j = 0; j < 2; j++) {
            temp *= 10;
            temp += (SBUF - '0');
            while (!RI); // 等待接收完成
            RI = 0;
        }
        time_buffer[i] = temp;
        if (i == 2 || i == 4) {
            while (SBUF != ' '); // 跳过空格字符
            while (!RI); // 等待接收完成
            RI = 0;
        }
    }
    flag = 1; // 标记已经接收到字符串
}

// 主函数
void main() {
    InitUart();
    InitDS1302();
    flag = 0;
    while (1) {
        if (flag) { // 接收到时间字符串,设置闹钟和时间
            SetAlarm(time_buffer);
            Write_DS1302(DS1302_CMD_WRITE | 0x80, time_buffer[6] << 4 | time_buffer[7]);
            Write_DS1302(DS1302_CMD_WRITE | 0x82, time_buffer[3] << 4 | time_buffer[4]);
            Write_DS1302(DS1302_CMD_WRITE | 0x84, time_buffer[0] << 4 | time_buffer[1]);
            flag = 0;
        }
        ReadTime(); // 读取当前时间并发送到串口
    }
}

// UART接收中断函数
void UartIsr() interrupt 4 {
    if (RI) { // 接收到数据
        ParseTime(); // 解析时间字符串
    }
    RI = 0;
}

4.2 LCD1602显示时钟

基于STC89C52控制LCD1602显示时间字符串的实现代码。

#include <reg52.h>
#include <stdio.h>

// 定义Data和Command寄存器选择端口
sbit LCD_RS = P2^0;  // RS引脚(寄存器选择)
sbit LCD_RW = P2^1;  // RW引脚(读写选择)
sbit LCD_EN = P2^2;  // EN引脚(使能)

// 定义数据总线端口
#define LCD_DATA P0    

void DelayMs(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 120; j++);
}

void WriteCommand(unsigned char cmd) {
    LCD_RS = 0;  // 选择指令寄存器
    LCD_RW = 0;  // 写模式
    LCD_EN = 0;  // 低电平使能
    LCD_DATA = cmd;  // 发送指令
    DelayMs(1);  // 延时等待指令写入
    LCD_EN = 1;  // 高电平使能
    DelayMs(1);  // 持续一段时间
    LCD_EN = 0;  // 结束使能
}

void WriteData(unsigned char dat) {
    LCD_RS = 1;  // 选择数据寄存器
    LCD_RW = 0;  // 写模式
    LCD_EN = 0;  // 低电平使能
    LCD_DATA = dat;  // 发送数据
    DelayMs(1);  // 延时等待数据写入
    LCD_EN = 1;  // 高电平使能
    DelayMs(1);  // 持续一段时间
    LCD_EN = 0;  // 结束使能
}

void LCDInit() {
    WriteCommand(0x38);  // 设置显示模式为2行、5x8点阵字符
    WriteCommand(0x0C);  // 显示器开,光标关闭
    WriteCommand(0x06);  // 光标右移,整屏不移动
    WriteCommand(0x01);  // 清除显示并设置光标回到初始位置
}

void LCDDisplayTime(char* time) {
    int i;
    WriteCommand(0x80);  // 设置光标位置为第一行的起始位置

    for (i = 0; i < 16; i++) {
        WriteData(time[i]);  // 在第一行显示时间字符串
    }

    WriteCommand(0xC0);  // 设置光标位置为第二行的起始位置

    for (i = 0; i < 16; i++) {
        WriteData(time[16 + i]);  // 在第二行显示时间字符串
    }
}

void main() {
    char time_buffer[32] = "Current Time: 00:00:00";  // 时间字符串
    unsigned char sec = 0, min = 0, hour = 0;  // 当前时间变量

    LCDInit();  // 初始化LCD显示器

    while (1) {
        // 更新时间变量
        sec++;
        if (sec >= 60) {
            sec = 0;
            min++;
            if (min >= 60) {
                min = 0;
                hour++;
                if (hour >= 24) {
                    hour = 0;
                }
            }
        }

        // 格式化时间字符串
        sprintf(time_buffer + 14, "%02d:%02d:%02d", hour, min, sec);

        // 显示时间字符串
        LCDDisplayTime(time_buffer);

        DelayMs(1000);  // 延时1秒
    }
}

代码使用LCD_RSLCD_RWLCD_EN分别表示LCD1602的RS、RW和EN引脚。数据总线通过LCD_DATA定义,连接到P0端口。先初始化LCD显示器,在一个无限循环中更新时间变量并格式化时间字符串,最后在LCD上显示时间字符串。

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

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

相关文章

亚马逊、Shein、lazada自养号测评如何解决支付和环境问题?

年底旺季&#xff0c;平台风控都会大规模持续升级&#xff0c;针对风控升级如果测评环境没有进行相对应的更新可能会导致大批量砍单&#xff0c;或者F号&#xff0c;严重的店铺还会被关联。有人以为是支付卡的问题&#xff0c;也有人觉得是IP被关联了。其实他们讲的也没错&…

【万字长文】前端性能优化实践 | 京东云技术团队

一、引言 从一个假死页面引发的思考&#xff1a; 作为前端开发&#xff0c;除了要攻克页面难点&#xff0c;也要有更深的自我目标&#xff0c;性能优化是自我提升中很重要的一环&#xff1b; 在前端开发中&#xff0c;会偶遇到页面假死的现象&#xff0c; 是因为当js有大量计算…

mysql 中with的用法(2)

with递归练习主要用于表里面包含父节点id之类的 查询出对应的省份和市。 建表 CREATE TABLE tb(id VARCHAR(3), pid VARCHAR(3), name VARCHAR(64));INSERT INTO tb VALUES(002, 0, 浙江省); INSERT INTO tb VALUES(001, 0, 广东省); INSERT INTO tb VALUES(003, 002, 衢州市…

史上最强AI芯片!英伟达H200震撼来袭!141 GB 超大显存,Llama2推理性能翻倍,老黄赢麻了!

原创 作者 | 王二狗英伟达又一次打了所有人措手不及&#xff01; 就在昨晚&#xff0c;老黄发布了新一代史上最强 AI芯片 NVIDIA HGX™ H200 。 141 GB 超大显存&#xff01;带宽增加 2.4 倍 H200 拥有141GB 显存&#xff01;相比之前的 H100和A100&#xff0c;容量几乎翻倍&…

SpringBoot整合Activiti7——定时器事件(九)

文章目录 定时器事件时间定义时间固定时间段时间周期 1.开始事件2.中间事件3.边界事件代码实现xml文件自定义服务任务监听器自定义用户任务监听器测试流程流程执行步骤 定时器事件 可以用在开始事件、中间事件、边界事件上&#xff0c;边界事件可以是中断和非中断边界事件 需要…

css实现元素四周阴影

前言 首先确定的是需要使用box-shadow这一属性 语法如下&#xff1a; box-shadow: h-shadow v-shadow blur spread color inset; h-shadow&#xff1a;表示水平方向上的阴影偏移量&#xff0c;必须指明&#xff0c;可以是正数、负数、0&#xff0c;如果为正数左方有阴影&…

安装MinGW并在codeblocks下使用

一、下载安装MinGW 1.下载MinGw安装器&#xff0c;下载地址 2. 安装 下载下来的知识一个安装器&#xff0c;我们双击安装会帮我们自动下载好相关文件 安装完成后会打开一个安装管理工具&#xff0c;在这个工具中我们选中想要安装的软件包然后安装到本地 选好以后在菜单栏选…

多区域LCC-HVDC一次调频二次调频

多区域LCC-HVDC高压直流互联一次二次调频&#xff0c;后续可加入双馈永磁风机调频&#xff0c;储能调频等。多个四机两区域模型互联。 目前实现的主要是区域之间的调频。

灯饰行业ERP有什么用?有哪些模块

在我们的生活当中会遇到各种类型的灯饰产品&#xff0c;这些商品有不同的设计特点和用料以及选型&#xff0c;并且在销售策略和价格策略等方面也比较灵活。 随着近些年灯饰行业同质化竞争愈演愈烈&#xff0c;国内很多的灯饰企业也遭遇较大的运营压力&#xff0c;尤其是采用传…

MySQL最新2023年面试题及答案,汇总版(7)【MySQL最新2023年面试题及答案,汇总版-第三十七刊】

文章目录 MySQL最新2023年面试题及答案&#xff0c;汇总版(7)01、NOW() 和 CURRENT_DATE() 有什么区别&#xff1f;02、CHAR和VARCHAR的区别&#xff1f;03、主键索引与唯一索引的区别&#xff1f;04、MySQL中有哪些不同的表格&#xff1f;05、SQL的生命周期&#xff1f;06、你…

浪潮服务器安装操作系统

文章目录 一、准备工作1.1 制作启动 U 盘 一、准备工作 一开始认为将镜像文件导入U盘即可&#xff0c;但实操中并不行&#xff0c;得首先将U盘制作成启动盘才行。   首先需要确定安装的版本&#xff0c;我这里要安装的是 CentOS 7.9 版本&#xff0c;镜像文件为 CentOS-7-x86…

Git企业开发级讲解(一)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、Git初识1、提出问题2、如何解决--版本控制器3、注意事项 二、Git 安装1、Linux-centos2、…

【图的定义和术语,图的类型定义】

文章目录 图的定义和术语图的类型定义 图的定义和术语 图&#xff1a;G&#xff08;V,E&#xff09;V:顶点&#xff08;数据元素&#xff09;的有穷非空集合。 E&#xff1a;边的有穷集合。 无向图&#xff1a;每条边都是无方向的。 有向图&#xff1a;每条边都是有方向的。 …

绿盟远程安全评估系统 RSAS 使用体验-难用

最近领导让我用公司采购的RSAS对产品进行漏洞扫描&#xff0c;学习并使用了这个软件&#xff0c;体验就是真的很难用。使用遇到问题时&#xff0c;咨询售后服务&#xff0c;机器人需要有公司认证&#xff0c;不能随便问问题&#xff0c;也是无语了。咨询客服&#xff0c;客服回…

京东数据运营与分析:如何全面获取电商销售数据?

随着电商行业的快速发展&#xff0c;数据分析成为了电商运营中一个非常重要的环节&#xff0c;这一环往往能够帮助品牌方来提升销售业绩和管理效率。然而&#xff0c;如何获取到电商平台中详细、全面的销售数据是很多电商品牌方所关心的问题&#xff0c;事实上&#xff0c;第三…

基于springboot实现生鲜超市管理的设计与实现系统【项目源码】计算机毕业设计

基于springboot实现生鲜超市管理的设计与实现系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群…

Network(一)计算机网络介绍

一 计算机网络 1 概述 什么是计算机网络&#xff1f; 硬件方面:通过线缆将网络设备和计算机连接起来 软件方面:操作系统&#xff0c;应用软件&#xff0c;应用程序通过通信线路互连 实现资源共享、信息传递、增加可靠性、提高系统处理能力 2 网络与云计算 3 计算机网…

Android 12 intent-filter添加android:exported后任然报错解决方法

Android 12 或更高版本为目标平台&#xff0c;且包含使用intent-filter 过滤器的 activity、service或receiver&#xff0c;您必须为这些应用组件显式声明 android:exported 属性。 常规操作 查看AndroidManifest.xml文件&#xff0c;搜索intent-filter&#xff0c;然后添加好…

ReportLab创建合同PDF

一、前言 有一个项目需要将电子签名后的报价合同和生成的发票发送给客户&#xff0c;这种发送给客户的文件一般都是使用PDF格式&#xff0c;主要是因为PDF特别适合阅读且不同平台打开文件格式不会变形&#xff0c;不过要在程序中生成PDF还是比较麻烦的&#xff0c;我们的发票是…

NTTRU:兼容 NTT 算法的 NTRU-based KEM 方案

参考文献&#xff1a; [CT65] Cooley J W, Tukey J W. An algorithm for the machine calculation of complex Fourier series[J]. Mathematics of computation, 1965, 19(90): 297-301.[Mont85] Montgomery P L. Modular multiplication without trial division[J]. Mathemat…