DS1302这个芯片给我的印象就是不同种类的寄存器真的很多,比如秒寄存器的bit7(时间暂停位)可以在时间计数的时候控制停止与启动,再比如写保护寄存器的bit7(写保护位)可以控制写操作,诸如此类的还有很多,在初学阶段比较容易混淆,不过只要多翻翻芯片手册,弄明白各个寄存器的功能就不会晕头转向了。
对该芯片的学习过程中,最郁闷的一件事就是中文资料有时候真的很会误导人,错把VCC1翻译成主电源,如下图所示。
而原版英文资料如下。
接下来进入正题!我想通过 DS1302(官方给的时序代码) + 矩阵按键 + 数码管 来实现电子钟的时间暂停、时间启动、时间重置的功能。
头文件
/*
程序说明: DS1302驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include
#include
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3; // DS1302复位
void Write_Ds1302_Byte(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK=0;
SDA=temp&0x01;
temp>>=1;
SCK=1;
}
}
void Write_Ds1302( unsigned char address,unsigned char dat )
{
RST=0;
_nop_();
SCK=0;
_nop_();
RST=1;
_nop_();
Write_Ds1302_Byte(address);
Write_Ds1302_Byte(dat);
RST=0;
}
unsigned char Read_Ds1302 ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0;
_nop_();
SCK=0;
_nop_();
RST=1;
_nop_();
Write_Ds1302_Byte(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0;
_nop_();
RST=0;
SCK=0;
_nop_();
SCK=1;
_nop_();
SDA=0;
_nop_();
SDA=1;
_nop_();
return (temp);
}
void delay(int n)/*延时函数*/
{
char z=110;
while(n--)
for(z=0;z<110;z++);
}
主程序
#include "reg52.h"
#include "ds1302.h" //上文中的头文件
#define uchar unsigned char;
#define uint unsigned int;
sfr P4=0XC0;
sbit KO1=P3^0;
sbit KO2=P3^1;
sbit KI1=P3^4;
sbit KI2=P3^5;
sbit KI3=P4^2;
sbit KI4=P4^4;
unsigned char KeyCodeMap[2][4]={
{0x31,0x32,0x33,0x26}, //<- -> + -
{0x34,0x35,0x36,0x25} //RST C NON NON
};
unsigned char KeySta[2][4]={ //按键状态缓冲区
{1,1,1,1},{1,1,1,1}
};
uchar code LedChar[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf}; //数码管真值表
uchar dsbuff[]={10,10,10,10,10,10,10,10};
uchar s_time[7]={0x00,0x2,0x10,0x05,0x03,0x04,0x18}; //设置时间数组
uchar g_time[7]={0}; //保存时间数组
void init_smg();
void LedScan();
void show_time();
void get_time();
void set_time();
void KeyDriver(); //按键驱动程序
void KeyAction(unsigned char keycode); //按键操作程序
void main()
{
init_smg(); //初始化数码管
set_time(); //设置时间
while(1)
{
show_time(); //显示时间
get_time(); //获取时间
KeyDriver(); //按键驱动程序
}
}
void init_smg() /*配置定时器0*/
{
TMOD|=0x01;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
EA=1;
ET0=1;
TR0=1;
}
void LedScan() /*数码管动态刷新*/
{
static unsigned char i=0;
P2=(P2&0X1F)|0XE0;
P0=0XFF;
P2&=0X1F;
P2|=0XC0;
P0=0x80>>i;
P2&=0X1F;
P2|=0XE0;
P0=LedChar[dsbuff[i]];
P2&=0X1F;
if(++i==8) i=0;
}
void set_time() /*初始化时间*/
{
char i=0;
char adr=0x80;
Ds1302_Single_Byte_Write(0x8e,0x00); //清除写保护位
for(i=0;i<7;i++) //将设置好的时间写入Register
Ds1302_Single_Byte_Write(adr+i*2,s_time[i]); //将设置好的时间数组写入DS1302
Ds1302_Single_Byte_Write(0x8e,0x80); //启动写保护
}
void get_time() /*获取时间*/
{
char i=0;
for(i=0;i<7;i++)
{
g_time[i]=Ds1302_Single_Byte_Read(0x81+i*2);
delay(30);
}
}
void show_time() /*显示时间*/
{
dsbuff[1]=(g_time[0]&0x70)>>4; //秒十位
dsbuff[0]=g_time[0]&0x0f; //秒各位
dsbuff[5]=11; //-
dsbuff[4]=(g_time[1]&0x70)>>4; //分十位
dsbuff[3]=g_time[1]&0x0f; //分各位
dsbuff[2]=11; //-
dsbuff[7]=(g_time[2]&0x30)>>4; //时十位
dsbuff[6]=g_time[2]&0x0f; //时各位
}
void KeyDriver()
{
unsigned char i,j;
static unsigned char backup[2][4]={ //按键状态区
{1,1,1,1},{1,1,1,1}
};
for(i=0;i<2;i++)
{
for(j=0;j<4;j++)
{
if(backup[i][j]!=KeySta[i][j])
{
if(backup[i][j]==0)
{
KeyAction(KeyCodeMap[i][j]);
}
backup[i][j]=KeySta[i][j];
}
}
}
}
void KeyAction(unsigned char keycode) //按键行动
{
if(keycode==0x34) //重置时间
{
set_time();
}
else if(keycode==0x31) //时间暂停
{
Ds1302_Single_Byte_Write(0x80,g_time[0]|0x80);
}
else if(keycode==0x32) //时间启动
{
Ds1302_Single_Byte_Write(0x80,g_time[0]&0x7f);
}
}
void KeyScan() //按键刷新函数
{
unsigned char i;
static unsigned char keyout=0;
static unsigned char keybuf[4][4]={
{0XFF,0XFF,0XFF,0XFF},{0XFF,0XFF,0XFF,0XFF}
};
/*低4位全为0,认为按键按下,耗时4毫秒*/
keybuf[keyout][0]=(keybuf[keyout][0]<<1)|KI4;
keybuf[keyout][1]=(keybuf[keyout][1]<<1)|KI3;
keybuf[keyout][2]=(keybuf[keyout][2]<<1)|KI2;
keybuf[keyout][3]=(keybuf[keyout][3]<<1)|KI1;
for(i=0;i<4;i++)
{
if((keybuf[keyout][i] & 0X0F)==0X00)
{
KeySta[keyout][i]=0;
}
else if((keybuf[keyout][i] & 0X0F)==0X0F)
{
KeySta[keyout][i]=1;
}
}
keyout++;
if(keyout>=2) keyout=0;
/*矩阵按键的一个局限性,因为一次只能控制一行的低电平,矩阵按键行的动态刷新*/
switch(keyout)
{
case 0: KO2=1;KO1=0;break;
case 1: KO1=1;KO2=0;break;
default: break;
}
}
void smg_time() interrupt 1 /*定时器0中断,1毫秒进入一次中断*/
{
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
LedScan(); //数码管刷新
KeyScan(); //按键刷新
}
其实上述代码遗留了一些问题,其体现在数码管跳动的问题上,通过200ms检测(读取)一次时间数据可解决此问题,以下是改动的三个地方
①。先定义一个全局变量 int flag=0; //200ms标志位
②。主函数改为如下:
void main()
{
init_smg();
set_time();
while(1)
{
show_time();
if(flag>=200)
{
flag=0;
get_time();
}
}
}
③。中断函数
void smg_time()interrupt 1 /*定时器0中断*/
{
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
LedScan(); //数码管刷新
flag++;
}