一、前言
電子鐘是一種能夠準(zhǔn)確顯示時間的設(shè)備,廣泛應(yīng)用于家庭、辦公場所和公共場所,為人們提供了方便和準(zhǔn)確的時間信息。本項目設(shè)計一個基于51單片機(jī)的電子鐘,使用DS1302作為RTC時鐘芯片,LCD1602作為顯示屏,并通過串口方式連接上位機(jī)進(jìn)行時間設(shè)置和鬧鐘設(shè)置。
STC89C52作為主控芯片,具有較高的性能和穩(wěn)定性,可完成對外設(shè)的控制和數(shù)據(jù)處理。DS1302是一款低功耗的實時時鐘芯片,能夠提供準(zhǔn)確的時間計數(shù)和日期功能。LCD1602是一款常用的字符型液晶顯示屏,具有兩行16列的顯示區(qū)域,能夠清晰顯示時間和其他相關(guān)信息。
通過串口連接上位機(jī),用戶可以方便地設(shè)置電子鐘的時間和鬧鐘時間,實現(xiàn)個性化需求。此外,電子鐘還帶有一個蜂鳴器,可以根據(jù)設(shè)置的鬧鐘時間進(jìn)行響鈴,提醒用戶。
電子鐘具有以下功能:
(1)顯示當(dāng)前時間和日期:LCD1602顯示屏將實時更新并顯示當(dāng)前的時間和日期信息。
(2)時間設(shè)置:通過串口連接上位機(jī),用戶可以進(jìn)行時間的設(shè)置,包括小時、分鐘和秒。
(3)日期設(shè)置:用戶可以通過上位機(jī)設(shè)置當(dāng)前的年、月和日。
(4)鬧鐘設(shè)置:用戶可以設(shè)置鬧鐘的時間,包括小時和分鐘。到達(dá)設(shè)定時間時,蜂鳴器將響鈴提醒用戶。
(5)整點報時:每到整點,蜂鳴器將發(fā)出短促的提示音,提醒用戶當(dāng)前時間。
(6)鬧鐘響鈴:當(dāng)鬧鐘時間到達(dá)時,蜂鳴器將持續(xù)響鈴,直到用戶停止。
(7)該項目將借助STC89C52單片機(jī)的控制能力和串口通信功能,結(jié)合DS1302時鐘芯片和LCD1602顯示屏,實現(xiàn)一個簡單而實用的電子鐘。用戶可以根據(jù)自己的
(8)需求進(jìn)行時間設(shè)置和鬧鐘設(shè)置,方便實用,并且具有較高的準(zhǔn)確性和穩(wěn)定性。
二、項目的設(shè)計思路
項目的設(shè)計思路分為硬件設(shè)計和軟件設(shè)計兩部分。
2.1 硬件設(shè)計思路
(1)主控芯片選擇:選擇STC89C52作為主控芯片,由于其較高的性能和穩(wěn)定性,適合用于控制和數(shù)據(jù)處理。
(2)RTC時鐘芯片選擇:選擇DS1302作為RTC時鐘芯片,具有低功耗、精確計時和日期功能。
(3)顯示屏選擇:選擇LCD1602作為顯示屏,它具有兩行16列的字符顯示區(qū)域,能夠清晰顯示時間和其他相關(guān)信息。
(4)串口連接:設(shè)計串口連接電路,實現(xiàn)與上位機(jī)的通信,用于時間設(shè)置和鬧鐘設(shè)置。
(5)蜂鳴器:添加蜂鳴器模塊,用于整點報時和鬧鐘響鈴功能。
(6)按鍵輸入:添加按鍵輸入模塊,用于用戶操作,如切換設(shè)置模式、調(diào)整時間和設(shè)置鬧鐘。
2.2 軟件設(shè)計思路
(1)初始化設(shè)置:在程序啟動時,進(jìn)行硬件初始化,包括配置主控芯片的引腳、初始化DS1302時鐘芯片和LCD1602顯示屏。
(2)時間獲取與顯示:通過DS1302時鐘芯片獲取當(dāng)前的時間和日期,并將其顯示在LCD1602顯示屏上。
(3)串口通信:通過串口與上位機(jī)進(jìn)行通信,接收上位機(jī)發(fā)送的時間設(shè)置和鬧鐘設(shè)置指令,并進(jìn)行相應(yīng)的處理
(4)時間設(shè)置:根據(jù)上位機(jī)發(fā)送的時間設(shè)置指令,更新DS1302時鐘芯片的時間計數(shù)器。
(5)日期設(shè)置:根據(jù)上位機(jī)發(fā)送的日期設(shè)置指令,更新DS1302時鐘芯片的日期計數(shù)器。
(6)鬧鐘設(shè)置:根據(jù)上位機(jī)發(fā)送的鬧鐘設(shè)置指令,設(shè)置鬧鐘時間,并將其保存在主控芯片的內(nèi)部存儲器中。
(7)整點報時:通過檢測DS1302時鐘芯片的小時計數(shù)器,當(dāng)小時值變化時,觸發(fā)蜂鳴器發(fā)出短促的提示音。
(8)鬧鐘響鈴:通過比較當(dāng)前時間和保存的鬧鐘時間,當(dāng)達(dá)到鬧鐘時間時,觸發(fā)蜂鳴器持續(xù)響鈴,直到用戶停止或設(shè)定的時間段結(jié)束。
三、項目硬件接線
(1)STC89C52與DS1302:
STC89C52的P2.0口連接到DS1302的SCLK(時鐘)引腳,用于提供時鐘信號。
STC89C52的P2.1口連接到DS1302的IO(數(shù)據(jù))引腳,用于數(shù)據(jù)傳輸。
STC89C52的P2.2口連接到DS1302的RST(復(fù)位)引腳,用于對DS1302進(jìn)行復(fù)位操作。
(2)STC89C52與LCD1602:
STC89C52的P0口連接到LCD1602的D0-D7引腳,用于傳輸字符數(shù)據(jù)和控制信號。
STC89C52的P2.3口連接到LCD1602的RS(寄存器選擇)引腳,用于選擇數(shù)據(jù)或命令寄存器。
STC89C52的P2.4口連接到LCD1602的RW(讀寫選擇)引腳,用于選擇讀或?qū)懖僮鳌?/p>
STC89C52的P2.5口連接到LCD1602的E(使能)引腳,用于啟動傳輸。
(3)STC89C52與蜂鳴器模塊:
STC89C52的P3.7口連接到蜂鳴器模塊的信號引腳,用于觸發(fā)蜂鳴器響鈴。
(4)串口通信接口。在STC89C52單片機(jī)上,串口引腳如下:
UART接收線(RXD):連接至外部設(shè)備的發(fā)送線。
STC89C52的P3.0口(RXD)用于接收串口數(shù)據(jù)。
UART發(fā)送線(TXD):連接至外部設(shè)備的接收線。
STC89C52的P3.1口(TXD)用于發(fā)送串口數(shù)據(jù)。
四、項目代碼
4.1 DS1302時鐘讀取、設(shè)置
下面代碼實現(xiàn)了,STC89C52讀取DS1302時鐘信息打印到串口,以及設(shè)置鬧鐘、時間讀取、打印到串口的功能。其中,采用了UART通信進(jìn)行與上位機(jī)交互,可以接收上位機(jī)發(fā)送過來的時間字符串,并據(jù)此設(shè)置鬧鐘和時間。
#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; // 標(biāo)記是否接收到上位機(jī)的時間字符串
// 初始化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;
}
// 將單個字節(jié)發(fā)送到串口
void SendData(uchar dat) {
SBUF = dat;
while (!TI);
TI = 0;
}
// 將字符串發(fā)送到串口
void SendString(uchar *s) {
while (*s != '?') {
SendData(*s++);
}
}
// 初始化DS1302時鐘芯片
void InitDS1302() {
uchar i;
// 使能DS1302寫保護(hù)功能
DS1302_CE = 0;
DS1302_SCL = 0;
DS1302_CE = 1;
Write_DS1302(DS1302_CMD_WRITE | 0x8e, 0x80);
// 關(guān)閉時鐘允許,準(zhǔn)備寫入數(shù)據(jù)
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寫入數(shù)據(jù)
void Write_DS1302(uchar addr, uchar dat) {
uchar i;
DS1302_CE = 0;
DS1302_SCL = 0;
// 發(fā)送起始信號
DS1302_CE = 1;
DS1302_SCL = 1;
DS1302_CE = 0;
// 發(fā)送命令字節(jié)地址
DS1302_WriteByte(addr);
// 發(fā)送數(shù)據(jù)字節(jié)
DS1302_WriteByte(dat);
// 停止信號
DS1302_SCL = 0;
DS1302_CE = 1;
// 延時至少1us
for (i = 0; i < 10; i++);
}
// 向DS1302讀取數(shù)據(jù)
uchar Read_DS1302(uchar addr) {
uchar dat;
uchar i;
DS1302_CE = 0;
DS1302_SCL = 0;
// 發(fā)送起始信號
DS1302_CE = 1;
DS1302_SCL = 1;
DS1302_CE = 0;
// 發(fā)送命令字節(jié)地址
DS1302_WriteByte(addr | 0x01);
// 讀取數(shù)據(jù)字節(jié)
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/%02drn", hour, min, sec, day, month, year);
SendString(time_buffer);
}
// 向DS1302寫入鬧鐘時間
void SetAlarm(uchar *str) {
uint i = 0;
// 將字符串轉(zhuǎn)換為數(shù)字
while (str[i] != '?') {
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]);
}
// 從串口接收數(shù)據(jù)中解析出時間信息
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; // 標(biāo)記已經(jīng)接收到字符串
}
// 主函數(shù)
void main() {
InitUart();
InitDS1302();
flag = 0;
while (1) {
if (flag) { // 接收到時間字符串,設(shè)置鬧鐘和時間
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(); // 讀取當(dāng)前時間并發(fā)送到串口
}
}
// UART接收中斷函數(shù)
void UartIsr() interrupt 4 {
if (RI) { // 接收到數(shù)據(jù)
ParseTime(); // 解析時間字符串
}
RI = 0;
}
4.2 LCD1602顯示時鐘
基于STC89C52控制LCD1602顯示時間字符串的實現(xiàn)代碼。
#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引腳(使能)
// 定義數(shù)據(jù)總線端口
#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; // 發(fā)送指令
DelayMs(1); // 延時等待指令寫入
LCD_EN = 1; // 高電平使能
DelayMs(1); // 持續(xù)一段時間
LCD_EN = 0; // 結(jié)束使能
}
void WriteData(unsigned char dat) {
LCD_RS = 1; // 選擇數(shù)據(jù)寄存器
LCD_RW = 0; // 寫模式
LCD_EN = 0; // 低電平使能
LCD_DATA = dat; // 發(fā)送數(shù)據(jù)
DelayMs(1); // 延時等待數(shù)據(jù)寫入
LCD_EN = 1; // 高電平使能
DelayMs(1); // 持續(xù)一段時間
LCD_EN = 0; // 結(jié)束使能
}
void LCDInit() {
WriteCommand(0x38); // 設(shè)置顯示模式為2行、5x8點陣字符
WriteCommand(0x0C); // 顯示器開,光標(biāo)關(guān)閉
WriteCommand(0x06); // 光標(biāo)右移,整屏不移動
WriteCommand(0x01); // 清除顯示并設(shè)置光標(biāo)回到初始位置
}
void LCDDisplayTime(char* time) {
int i;
WriteCommand(0x80); // 設(shè)置光標(biāo)位置為第一行的起始位置
for (i = 0; i < 16; i++) {
WriteData(time[i]); // 在第一行顯示時間字符串
}
WriteCommand(0xC0); // 設(shè)置光標(biāo)位置為第二行的起始位置
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; // 當(dāng)前時間變量
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_RS
、LCD_RW
和LCD_EN
分別表示LCD1602的RS、RW和EN引腳。數(shù)據(jù)總線通過LCD_DATA
定義,連接到P0端口。先初始化LCD顯示器,在一個無限循環(huán)中更新時間變量并格式化時間字符串,最后在LCD上顯示時間字符串。