矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 7126|回复: 18

单片机练手,最近做的数码管DS1302时钟。

[复制链接]
     
发表于 2014-1-5 23:29:09 | 显示全部楼层 |阅读模式
本帖最后由 其实不想走 于 2014-1-5 23:32 编辑

        最近在学习51单片机,刚入门,做了个数码管时钟练练手。
        做这个电路和程序都比较简单,也具有一定的实用价值。
        单片机是用的STC12C2052AD,学习51一直在用STC的单片机,12系列还是1T单片机,比传统51要快不少呢,而且它的I/O口支持推挽输出,可以直接驱动LED数码管,省去了驱动电路。按说这片单片机是5V的正常电压,但实验了下在3.5V下还能正常,所以电路电源用了一节锂电池。工作受限于PCB的尺寸DS1302辅助电源用一个470uF电容代替,理论上维持走时2个小时没问题(换电池的时间是足够了)。
        程序还没有设置日期显示,只有时间显示,回头要加上,可以用按键来调整时间。图片上是三个按键,实际只有两个在用。
1.jpg 2.jpg 3.jpg 4.jpg 5.jpg 6.jpg

补充内容 (2014-1-7 21:21):
电路在3楼,程序在5楼~!

评分

1

查看全部评分

     
 楼主| 发表于 2014-1-6 10:39:46 | 显示全部楼层
本帖最后由 其实不想走 于 2014-1-6 10:42 编辑

DS1302_clock.rar (1.1 KB, 下载次数: 508) DS1302_clock.rar (3 KB, 下载次数: 540) /
**********************************************************
   数码管显示DS1302时钟程序                         (晶振频率=12.000MHz)




                                                                        2014年1月5日
**********************************************************/
#include <reg52.h>
#include <intrins.h>

#define        uchar unsigned char
#define uint unsigned int

sbit A0=P3^5;        //定义74HC138的地址线。
sbit A1=P3^7;        //同上。
sbit key1=P3^2;                 //定义键盘1.
sbit key2=P3^3;                 //定义键盘2.
sbit SCLK=P3^1;                 //定义1302时钟线接口。
sbit DAT=P3^0;                 //定义1302数据线接口。
sbit CE=P3^4;                 //定义1302使能口。

sfr P0M0  = 0x93;
sfr P0M1  = 0x94;
sfr P1M0  = 0x91;        //定义STC12系列单片机特殊功能寄存器地址(控制P1口工作模式)
sfr P1M1  = 0x92;        //同上。
sfr P2M0  = 0x95;
sfr P2M1  = 0x96;
sfr P3M0  = 0xb1;
sfr P3M1  = 0xb2;

uchar num,num1,x;
uchar code table[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};                  
uchar code table1[10] ={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};                 //带点数字



void DS1302WriteByte (uchar dat);                //此函数向DS1302写入8bit数据
uchar DS1302ReadByte();                                //此函数读取DS1302输出的8bit数据
void DS1302Write(uchar cmd,uchar dat);            //此函数向cmd相应地址中写入一个字节的数据
uchar DS1302Read(uchar cmd);                //此函数从CMD相应地址中读取一个字节的数据
void Init_DS1302();                        //DS1302初始化函数。  
void display(uchar n,uchar m);                           //LED显示函数           (正常走时状态的
void display1(uchar n,uchar m);                           //LED显示函数           (小时位闪烁状态)
void display2(uchar n,uchar m);                    //LED显示函数           (分钟位闪烁状态)
void Display_time();                                           //时间显示函数
void Init_check();                                                   //初始化判断函数
uchar BCDtoDEC(uchar bcd);
uchar DECtoBCD(uchar dec);



void Delay_us (uchar i)   //执行次函数延时( i+1) us ,注:此函数i最小取值为1 ,且不能改变为其它数据类型,所以此函数最大延时为256us。 (相当精确)
{

        do
        {
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
        }
        while(--i);
}


void Delay_ms(unsigned int i)  //执行此函数延时 i ms。    (比较精确)
{
        unsigned char j,k;
        do
        {
                j = 44;
                do
                {
                        k = 54;
                        do
                        {
                                _nop_();
                        }
                        while(--k);     
                }
                while(--j);
        }
        while(--i);

}


/*****************************************
               

                        主函数


*****************************************/
void main()
{
        P1M0 = 0x00;         //          设置P1口为
        P1M1 = 0xff;           //          推挽输出方式         (最大20MA输出能力)。
        P3M0=0x00;                 //   设置P3口为       
        P3M1=0xff;                 //          挽输出方式         (最大20MA输出能力)。
       
        TMOD=0x01;                 //设置定时器T0和T1工作方式为16为定时器。
        TL0=(65536-10000)%256;
        TH0=(65536-10000)/256;       
        EA=1;                   //开总中断。
        ET0=1;                   //打开T0中断
        TR0=1;                   //定时器0的运行控制位,置1启动T0
        EX0=1;                   //打开外部中断0
//        EX1=1;                   //打开外部中断1
        IT0=1;                   //外部中断0触发方式选择,置1为下降沿触发
//        IT1=1;                   //外部中断1触发方式选择,置1为下降沿触发

        Init_check();

        while(1)
        {
                Display_time();                       
        }
}








void DS1302WriteByte (uchar dat)                 //此函数向DS1302写入8bit数据
{
        uchar i;
        SCLK=0;           //初始时钟线设置为0.
        Delay_us(2);
        for(i=0;i<8;i++)   //开始传输8个字节的数据。
        {
                DAT=dat&0x01;  //取最低位,DS1302的数据和地址都是从最低位开始传输的。
                Delay_us(2);
                SCLK=1;                   //时钟线拉高,制造上升沿,DAT的数据被传输。
                Delay_us(2);
                SCLK=0;                  //时钟线拉低,为下一个上升沿做准备。
                dat>>=1;          //数据右移一位,准备传输下一位数据。
        }

}


uchar DS1302ReadByte()                   //此函数读取DS1302输出的8bit数据
{
        uchar i,dat;
        Delay_us(2);
        for(i=0;i<8;i++)
        {
                dat>>=1;          //要返回的数据右移一位(DS1302最先输出的是最低位数据)
                if(DAT==1)
                        dat|=0x80; //按位或,若数据线为高,把返回的数据最高位置1,而不改变其它位。
                SCLK=1;                  //拉高时钟线,为下降沿做准备。
                Delay_us(2);
                SCLK=0;                  //制造下降沿,DS1302的数据在下降沿输出。
                Delay_us(2);
        }
        return dat;
}



/* ******************************  
此函数从CMD相应地址中读取一个字节的数据
        cmd为要写入的地址
        每次操作都是先写地址,后读数据
********************************/

uchar DS1302Read(uchar cmd)
{
        uchar dat;
        CE=0;
        SCLK=0;
        CE=1;          //CE设置为1,开始传输
        DS1302WriteByte (cmd);          //写入要读取的时间/日历的地址
        dat=DS1302ReadByte();          //读取要得到的时间日期
        SCLK=1;
        CE=0;           //读取结束,CE设置为0,结束数据的传输
        return dat;
}

/*************************************
        此函数向cmd相应地址中写入一个字节的数据
        cmd 是要写入的地址
        dat是要写入的数据
        先写地址,后写数据
************************************/

void DS1302Write(uchar cmd,uchar dat)
{
        CE=0;
        SCLK=0;
        CE=1;                //CE设置为1,开始传输
        DS1302WriteByte (cmd);                //写入要修改的时间/日历的地址
        DS1302WriteByte (dat);                // 写入要修改的时间日期
        SCLK=1;
        CE=0;                //读取结束,CE设置为0,结束数据的传输
}


/*****************************************
        DS1302初始化程序
        只需要调用一次,写入一个初始值即可
        这里初始值的设置如下:
        2014年1月4
        22:30:00
****************************************/

void Init_DS1302()
{
        DS1302Write(0x8e,0x00);                //写保护关
        DS1302Write(0x8c,0x14);                //设置年为14年
        DS1302Write(0x88,0x01);                // 设置月为1月
        DS1302Write(0x86,0x04);                // 设置日为4
        DS1302Write(0x84,0x00);                //        设置时为00
        DS1302Write(0x82,0x15);                //        设置分为15
        DS1302Write(0x80,0x00);                //         设置秒为0
        DS1302Write(0x90,0xa6);           //涓流充电寄存器,这里设置为1个二极管4K电阻模式(如不初始化次寄存器,加电时涓流充电是禁止的)
        DS1302Write(0xc0,0xf0);           //初始化一次标示***
        DS1302Write(0x8e,0x80);           //写保护开
}



/************************************
        LED显示函数           (正常走时状态的)
        把从DS1302中读取的时和分数据显示到LED数码管上
        m 为时数据,n 为分数据
************************************/
void display(uchar n,uchar m)
{
        uchar shi,ge,ss,gg;
        shi=n/10;
        ge=n%10;
        ss=m/10;
        gg=m%10;

        if(num<=50)
        {
                A0=0;
                A1=0;
                P1=table[ss];
                Delay_ms(5);
               
       
                A0=1;
                A1=0;
                P1=table1[gg];
                Delay_ms(5);
                       

                A0=0;
                A1=1;
                P1=table[shi];
                Delay_ms(5);

                A0=1;
                A1=1;
                P1=table[ge];
                Delay_ms(5);       
        }
        else
        {
                A0=0;
                A1=0;
                P1=table[ss];
                Delay_ms(5);
               
               
                A0=1;
                A1=0;
                P1=table[gg];
                Delay_ms(5);
                       

                A0=0;
                A1=1;
                P1=table[shi];
                Delay_ms(5);

                A0=1;
                A1=1;
                P1=table[ge];
                Delay_ms(5);
        }
}               

/************************************
        LED显示函数           (小时位闪烁状态)
        把从DS1302中读取的时和分数据显示到LED数码管上
        m 为时数据,n 为分数据
************************************/
void display1(uchar n,uchar m)
{
        uchar shi,ge,ss,gg;
        shi=n/10;
        ge=n%10;
        ss=m/10;
        gg=m%10;

       
        if(num1<=20)
        {       
                A0=0;
                A1=0;
                P1=table[ss];
                Delay_ms(5);
               
       
                A0=1;
                A1=0;
                P1=table1[gg];
                Delay_ms(5);
                       

                A0=0;
                A1=1;
                P1=table[shi];
                Delay_ms(5);

                A0=1;
                A1=1;
                P1=table[ge];
                Delay_ms(5);       
        }
        else
        {
                A0=0;
                A1=1;
                P1=table[shi];
                Delay_ms(5);

                A0=1;
                A1=1;
                P1=table[ge];
                Delay_ms(5);
        }

}       

/************************************
        LED显示函数           (分钟位闪烁状态)
        把从DS1302中读取的时和分数据显示到LED数码管上
        m 为时数据,n 为分数据
************************************/
void display2(uchar n,uchar m)
{
        uchar shi,ge,ss,gg;
        shi=n/10;
        ge=n%10;
        ss=m/10;
        gg=m%10;

       
        if(num1<=20)
        {       
                A0=0;
                A1=0;
                P1=table[ss];
                Delay_ms(5);
               
       
                A0=1;
                A1=0;
                P1=table1[gg];
                Delay_ms(5);
                       

                A0=0;
                A1=1;
                P1=table[shi];
                Delay_ms(5);

                A0=1;
                A1=1;
                P1=table[ge];
                Delay_ms(5);       
        }
        else
        {
                A0=0;
                A1=0;
                P1=table[ss];
                Delay_ms(5);
               
       
                A0=1;
                A1=0;
                P1=table1[gg];
                Delay_ms(5);
        }

}               
       

/*****************************************
        时间显示函数
        此函数完成按键扫描和判断,数据修改,
        并把最终数据通过LED显示函数显示到
        LED数码管上
*****************************************/
void Display_time()
{
        uchar i,j;
        i=BCDtoDEC(DS1302Read(0x85));
        j=BCDtoDEC(DS1302Read(0x83));
       
        if(x==0)                  display(j,i);
        else if(x==1)         
        {
                if(key2==0)
                {
                        i+=1;
                        if(i>=24) i=0;
                        i=DECtoBCD(i);
                        DS1302Write(0x8e,0x00);                 //写保护关
                        DS1302Write(0x84,i);                //修改的时数据,写入DS1302
                        DS1302Write(0x8e,0x80);           //写保护开
                        while(!key2);               
                }
                display1(j,i);
        }
        else       
        {
                    if(key2==0)
                 {
                          j+=1;
                         if(j>=60) j=0;
                         j=DECtoBCD(j);
                         DS1302Write(0x8e,0x00);                  //写保护关
                         DS1302Write(0x82,j);                     //        修改的分数据,写入DS1302
                         DS1302Write(0x80,0x00);                //         设置秒为0
                         DS1302Write(0x8e,0x80);           //写保护开
                         while(!key2);
                 }
                 display2(j,i);
        }
}




/*************************************
        此程序将从DS1302中读取的BCD码数据
        转换成十进制DEC码
*************************************/
uchar BCDtoDEC(uchar bcd)
{
        uchar dec;
        dec=bcd&0x0f;
        bcd=bcd&0x70;
        bcd>>=4;
        dec=dec+bcd*10;
        return dec;
}

/***********************************
        初始化判断函数
        执行此函数可判断DS1302目前状态
        是否需要进行初始化,如果需要,
        则执行初始化,如果不需要,
        跳过初始化程序继续往下执行
***********************************/
void Init_check()
{
         uchar n;
         n=DS1302Read(0xc1);
         if(n!=0xf0)
         Init_DS1302();
}


/***********************************
        此程序将十进制DEC码转换成
        DS1302可以识别的BCD码
***********************************/
uchar DECtoBCD(uchar dec)
{
        uchar bcd;
        bcd=dec%10;
        dec=dec/10;
        dec<<=4;
        bcd=bcd+dec;
        return bcd;
}




void T0_time() interrupt 1
{
        TH0=(65536-10000)/256;
        TL0=(65536-10000)%256;
        num++;
        num1++;
        if(num==100)num=0;
        if(num1==40)num1=0;

}


void INT0_key1() interrupt 0
{
        x++;
        if(x>=3)x=0;
       
}





回复 支持 1 反对 0

使用道具 举报

     
发表于 2014-1-6 00:02:25 | 显示全部楼层
电路图?                              
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2014-1-6 00:07:14 | 显示全部楼层
lxa000 发表于 2014-1-6 00:02
电路图?

电路图还没画,只有草稿,明天画好了再传吧,顺便吧程序也传了。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2014-1-6 10:38:47 | 显示全部楼层
上传电路图。

电路图

电路图
回复 支持 反对

使用道具 举报

发表于 2014-1-6 14:20:47 | 显示全部楼层
做工不错
回复 支持 反对

使用道具 举报

     
发表于 2014-1-6 16:31:14 | 显示全部楼层
做的很漂亮。
回复 支持 反对

使用道具 举报

     
发表于 2014-1-6 18:14:48 | 显示全部楼层
把1302的时间调整准确,是个技术活
回复 支持 反对

使用道具 举报

     
发表于 2014-1-7 14:42:26 | 显示全部楼层
电路板排版挺不错的!
回复 支持 反对

使用道具 举报

     
发表于 2014-1-7 18:16:21 | 显示全部楼层
单片机啊,当初一冲动,买了一块AVR的学习板,并且很努力的学了一段不短的时间,但到现在神马也没做一个,想想,都忘记得差不多了吧!?
回复 支持 反对

使用道具 举报

发表于 2014-1-7 20:56:51 | 显示全部楼层
不错,楼主很用工啊
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2014-1-7 21:24:59 | 显示全部楼层
多谢楼上各位的鼓励加分。
回复 支持 反对

使用道具 举报

     
发表于 2014-1-15 00:26:09 | 显示全部楼层
的确比51简单多了!!
回复 支持 反对

使用道具 举报

发表于 2014-3-28 15:23:08 | 显示全部楼层
请问这个板子 是叫什么名字?
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2014-3-28 20:46:18 | 显示全部楼层
逑问 发表于 2014-3-28 15:23
请问这个板子 是叫什么名字?

什么板子,你是说洞洞板吗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-4-24 02:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表