2025年4月8日 星期二 乙巳(蛇)年 正月初九 设为首页 加入收藏
rss
您当前的位置:首页 > 电子 > 单片机

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

时间:11-03来源:作者:点击数:26

一、前言

电子钟是一种能够准确显示时间的设备,广泛应用于家庭、办公场所和公共场所,为人们提供了方便和准确的时间信息。本项目设计一个基于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上显示时间字符串。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐