1. 前提
51单片机,通过实验箱实现实时数字时钟,这里选用DS12C887时钟模块进行实验内容。
2. 目标
使用更为精准的DS12C887时钟模块进行时钟的设置,使用蜂鸣器进行时间按键按下调整的提示音,使用LCD1602模块进行时钟内容的显示。
3. 代码
整体配置文件与接口如下
#define uchar unsigned char
#define uint unsigned int
/**
*蜂鸣器接口
*/
sbit beep = P2 ^ 3;
/**
*LCD1602接口
*/
sbit rs = P2 ^ 0;
sbit rw = P2 ^ 1;
sbit en = P2 ^ 2;
sbit s1 = P3 ^ 0; //功能键
sbit s2 = P3 ^ 1; //增加键
sbit s3 = P3 ^ 2; //减少键
/**
*DS12887接口
*/
sbit dsas = P1 ^ 2;
sbit dsrw = P1 ^ 3; //12887WR
sbit dsrd = P1 ^ 4; //接RD
sbit dscs = P1 ^ 5;
sbit dsirq = P3 ^ 7;
//其它变量定义
uchar flag, key;
char year, month, day, week, shi, fen, miao;
//液晶固定显示内容
uchar code table[] = " 20 - - ";
uchar code table1[] = " : : ";
//函数申明
void write_ds(uchar, uchar);
uchar read_ds(uchar);
主函数文件如下
#include <reg52.h>
#include "init.h"
/*------------------------------------------------
延时函数
------------------------------------------------*/
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for (y = 110; y > 0; y--);
}
/*------------------------------------------------
蜂鸣器模块
------------------------------------------------*/
void di()
{
beep = 0;
delay(100);
beep = 1;
}
/*------------------------------------------------
DS12C877模块
------------------------------------------------*/
void write_ds(uchar add, uchar date)
{ //写12C887函数
dscs = 0; //片选输入,低电平有效
dsas = 1; //地址选通输入脚,将AD0~AD7上的地址信息锁存到DS12C887
dsrd = 1; //数据选择输入引脚,上升沿时,将内部信息传到AD0~AD7
dsrw = 1; //读写输入端,高电平读,低电平写
P0 = add; //先写地址
dsas = 0;
dsrw = 0;
P0 = date; //再写数据
dsrw = 1;
dsas = 1;
dscs = 1;
}
/**
* 读12C887函数
*/
uchar read_ds(uchar add)
{
uchar ds_date;
dsas = 1;
dsrd = 1;
dsrw = 1;
dscs = 0;
P0 = add; //先写地址
dsas = 0;
dsrd = 0;
P0 = 0xff;
ds_date = P0; //再读数据
dsrd = 1;
dsas = 1;
dscs = 1;
return ds_date;
}
/**
* 初始化设置设置DS12C887模块,以后不必再写入
*/
void initDS12C887()
{
write_ds(0x0A, 0x20); //打开振荡器
write_ds(0x0B, 0x26); //设置24小时模式,数据二进制格式
//首次上电初始化时间函数,给予寄存器初始化
write_ds(0, 30); //秒
write_ds(1, 0);
write_ds(2, 28); //分
write_ds(3, 0);
write_ds(4, 8); //时
write_ds(5, 0);
write_ds(6, 6); //周
write_ds(7, 26); //日
write_ds(8, 12); //月
write_ds(9, 20); //年
}
/*------------------------------------------------
LCD1602模块
------------------------------------------------*/
/**
*LCD1602写指令函数
*/
void write_com(uchar com)
{
rs = 0; //RS位置0, 选择指令寄存器
en = 0;
P0 = com;
delay(3);
en = 1;
delay(3);
en = 0;
}
/**
*LCD1602写数据函数
*/
void write_date(uchar date)
{
rs = 1; //RS位置1, 选择数据寄存器
en = 0;
P0 = date;
delay(3);
en = 1;
delay(3);
en = 0;
}
/**
* 加载LCD1602初始值
*/
void initWriteLCD1602()
{
uchar num;
/**
* 写入第一行数据
*/
write_com(0x80);
for (num = 0; num < 15; num++) //写入液晶固定部分显示
{
write_date(table[num]);
delay(1);
}
/**
* 写入第二行数据
*/
write_com(0xc0);
for (num = 0; num < 11; num++)
{
write_date(table1[num]);
delay(1);
}
}
//初始化函数
void init()
{
EA = 1; //打开总中断
EX1 = 1; //开外部中断1 IE中断允许控制寄存器
IT1 = 1; //设置负跳变沿触发中断 Tcon
en = 0;
rw = 0;
key = 0; //判断按键的次数
initDS12C887();
//1602液晶初始化
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
initWriteLCD1602();
}
/**
*对DS12C877传过来的时间,出现按键修改时进行分离送入LCD1602
*/
void write_sfm(uchar add, char date)
{ //1602液晶刷新时分秒函数4为时,7为分,10为秒
char shi, ge;
shi = date / 10;
ge = date % 10;
write_com(0xc0 + add);
write_date(0x30 + shi);
write_date(0x30 + ge);
}
void write_nyr(uchar add, char date)
{ //1602液晶刷新年月日函数3为年,6为分,9为秒
char shi, ge;
shi = date / 10;
ge = date % 10;
write_com(0x80 + add);
write_date(0x30 + shi);
write_date(0x30 + ge);
}
void write_week(char we)
{ //写液晶星期显示函数
write_com(0x80 + 12);
switch (we)
{
case 1:
write_date('M');
delay(5);
write_date('O');
delay(5);
write_date('N');
break;
case 2:
write_date('T');
delay(5);
write_date('U');
delay(5);
write_date('E');
break;
case 3:
write_date('W');
delay(5);
write_date('E');
delay(5);
write_date('D');
break;
case 4:
write_date('T');
delay(5);
write_date('H');
delay(5);
write_date('U');
break;
case 5:
write_date('F');
delay(5);
write_date('R');
delay(5);
write_date('I');
break;
case 6:
write_date('S');
delay(5);
write_date('A');
delay(5);
write_date('T');
break;
case 7:
write_date('S');
delay(5);
write_date('U');
delay(5);
write_date('N');
break;
}
}
/*------------------------------------------------
键盘扫描模块
------------------------------------------------*/
void keyscan()
{
if (s1 == 0) //检测S1端口
{
delay(5);
key++; //记录按下次数
flag = 1;
while (!s1)
;
di();
switch (key)
{ //光标闪烁点定位
case 1:
write_com(0xc0 + 11);
write_com(0x0f);
break;
case 2:
write_com(0xc0 + 8);
break;
case 3:
write_com(0xc0 + 5);
break;
case 4:
write_com(0x80 + 14);
break;
case 5:
write_com(0x80 + 10);
break;
case 6:
write_com(0x80 + 7);
break;
case 7:
write_com(0x80 + 4);
break;
case 8:
key = 0;
write_com(0x0c);
flag = 0;
write_ds(0, miao);
write_ds(2, fen);
write_ds(4, shi);
write_ds(6, week);
write_ds(7, day);
write_ds(8, month);
write_ds(9, year);
break;
}
}
if (key != 0) //只有当S1按下后,才检测S2和S3
{
if (s2 == 0)
{
delay(1);
while (!s2);
di();
switch (key)
{ //根据功能键次数调节相应数值
case 1:
miao++;
if (miao == 60)
miao = 0;
write_sfm(10, miao);
write_com(0xc0 + 11);
break;
case 2:
fen++;
if (fen == 60)
fen = 0;
write_sfm(7, fen);
write_com(0xc0 + 8);
break;
case 3:
shi++;
if (shi == 24)
shi = 0;
write_sfm(4, shi);
write_com(0xc0 + 5);
break;
case 4:
week++;
if (week == 8)
week = 1;
write_week(week);
write_com(0x80 + 14);
break;
case 5:
day++;
if (day == 32)
day = 1;
write_nyr(9, day);
write_com(0x80 + 10);
break;
case 6:
month++;
if (month == 13)
month = 1;
write_nyr(6, month);
write_com(0x80 + 7);
break;
case 7:
year++;
if (year == 100)
year = 0;
write_nyr(3, year);
write_com(0x80 + 4);
break;
}
}
if (s3 == 0)
{
delay(1);
while (!s3);
di();
switch (key)
{ //根据功能键次数调节相应数值
case 1:
miao--;
if (miao == -1)
miao = 59;
write_sfm(10, miao);
write_com(0xc0 + 11);
break;
case 2:
fen--;
if (fen == -1)
fen = 59;
write_sfm(7, fen);
write_com(0xc0 + 8);
break;
case 3:
shi--;
if (shi == -1)
shi = 23;
write_sfm(4, shi);
write_com(0xc0 + 5);
break;
case 4:
week--;
if (week == 0)
week = 7;
write_week(week);
write_com(0x80 + 14);
break;
case 5:
day--;
if (day == 0)
day = 31;
write_nyr(9, day);
write_com(0x80 + 10);
break;
case 6:
month--;
if (month == 0)
month = 12;
write_nyr(6, month);
write_com(0x80 + 7);
break;
case 7:
year--;
if (year == -1)
year = 99;
write_nyr(3, year);
write_com(0x80 + 4);
break;
}
}
}
}
void main() //主函数
{
init(); //调用初始化函数
while (1)
{
keyscan(); //按键扫描
if (flag == 0) //正常工作时进入这里
{
keyscan(); //按键扫描
//读取12C887数据
year = read_ds(9);
month = read_ds(8);
day = read_ds(7);
week = read_ds(6);
shi = read_ds(4);
fen = read_ds(2);
miao = read_ds(0);
//传入液晶显示时分秒
write_nyr(3, year);
write_nyr(6, month);
write_nyr(9, day);
write_week(week);
write_sfm(4, shi);
write_sfm(7, fen);
write_sfm(10, miao);
}
}
}