• 51单片机STC89C52RC——9.1 DS1302涓流充电计时芯片


    目录

    目的/效果

    一,STC单片机模块

    二,DS1302计时器

    2.1 特性/板子位置

    2.1.1 特性 

    2.1.2 板子上的位置

    2.2 针脚定义

    2.3 数据传输

    2.3.1 读数据

     2.3.2 写数据

    2.4 BCD码

    2.5 可编程涓流充电器

    2.6 时钟动态设置

    三,创建Keil项目

    四,代码 

    4.1 读写显示时间  main.c

    4.2 手动设置时间、重新上电显示最后设置的时间 main.c

    五,代码编译、下载到51单片机


    目的/效果

    1:读取DS1302计时器时间,在LCD1602液晶显示屏上显示。

    2:使用按键手动设置DS1302计时器时间,下电后重新上电显示。

    一,STC单片机模块

    二,DS1302计时器

    2.1 特性/板子位置

    2.1.1 特性 

    ★ 实时时钟计算年、月、日、时、分、秒、星期,直到 2100 年,并有闰年调节功能
    31 x 8 位 通用暂存 RAM
    ★ 串行输入输出使管脚数最少
    2.0V 5.5V 宽电压范围操作
    ★ 在 2.0V 时工作电流小于 300nA
    ★ 读写时钟或 RAM 数据时有单字节或 多字节(脉冲串模式)数据传送方式
    8 管脚 DIP 封装或可选的 8 管脚
    表面安装 SO 封装
    ★ 简单的 3 线接口
    ★ 与 TTL 兼容 (VCC = 5V)
    ★ 可选的工业温度范围: -40°C to +85°C
    ★ 与 DS1202 兼容
    ★ 美国保险商试验室 (UL®) 认证

            DS1302 涓流充电计时芯片包含一个实时时钟/ 日历和 31 字节的静态 RAM.通过简单的串行 接口与微处理器通讯 . 这个实时时钟 / 日历提供年月日 , 时分秒信息 . 对于少于 31 天的月份月末 会自动调整 , 还有闰年校正 . 由于有一个 AM/PM 指示器,时钟可以工作在 12 小时制或者 24 小时制。
            使用同步串行通讯简化了 DS1302 与微处理器的接口。与时钟 /RAM 通讯只需要三根线: CE, I/O ( 数据线 ), and SCLK ( 串行时钟 ). 数据输出输入时钟 /RAM 一次 1字节或者在脉冲串中多达 31 字节 . DS1302 被设计工作在非常低的电能下 , 在低于 1μW 时还能保持数据和时钟信息. DS1302 DS1202 的后继者 . 除了 DS1202 的基本计时功能以外 , DS1302 有额外特点比如, 双管脚主电源和备用电源 , 可编程涓流充电器 V CC1 , 还附加 7 字节的暂存器 .

    2.1.2 板子上的位置

    2.2 针脚定义

    1. //引脚定义
    2. sbit DS1302_SCLK=P3^6; //时钟针脚 输入. SCLK 用来同步串行接口上的数据动作.此管脚内部有一个
    3. //40kΩ(典型值)的下拉电阻连接到地
    4. sbit DS1302_IO=P3^4; //数据针脚 推挽输出.I/O 管脚是三线接口的双向数据管脚.此管脚内部有
    5. //一个 40kΩ(典型值)的下拉电阻连接到地
    6. sbit DS1302_CE=P3^5; //重置位针脚 CE信号在读写时必须保持高电平.此管脚内部有一个 40kΩ(典
    7. //型值)的下拉电阻连接到地. 注意:先前的数据手册修正把 CE当作
    8. //RST. 管脚的功能没有改变.

    2.3 数据传输

     

     寄存器地址

    根据上面的寄存器说明,我们程序中要申明对应的寄存器地址 

    1. //寄存器写入地址/指令定义
    2. #define DS1302_YEAR 0x8C //年
    3. #define DS1302_MONTH 0x88 //月
    4. #define DS1302_DATE 0x86 //日
    5. #define DS1302_HOUR 0x84 //时
    6. #define DS1302_MINUTE 0x82 //分
    7. #define DS1302_SECOND 0x80 //秒
    8. #define DS1302_WEEK 0x8A //星期
    9. #define DS1302_WP 0x8E //写保护位
    10. //控制寄存器的位 7是写保护位,前 7位(位 0至位 6)被强制为 0且读取时总是读 0. 在任
    11. //何对时钟或 RAM的写操作以前,位 7必须为 0.当为高时,写保护位禁止任何寄存器的写操
    12. //作. 初始加电状态未定义. 因此,在试图写器件之前应该清除 WP位.

    2.3.1 读数据

     根据上面的时序图我们需要写一个函数,传入寄存器地址,返回对应的数据。

    1. /**
    2. * 函 数:DS1302读一个字节
    3. * 参 数:Addr 命令字/DS1302寄存器地址
    4. * 返 回 值:无
    5. */
    6. unsigned char DS1302_ReadByte(unsigned char Addr)
    7. {
    8. unsigned char i,Data=0x00;
    9. Addr|=0x01; //将指令转换为读指令
    10. DS1302_CE=1; //读写数据时都置高电平
    11. for(i=0;i<8;i++)
    12. {
    13. DS1302_IO=Addr&(0x01<
    14. DS1302_SCLK=0;
    15. DS1302_SCLK=1;
    16. }
    17. for(i=0;i<8;i++)
    18. {
    19. DS1302_SCLK=1;
    20. DS1302_SCLK=0;
    21. if(DS1302_IO){Data|=(0x01<
    22. }
    23. DS1302_CE=0;
    24. DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
    25. return Data;
    26. }

     2.3.2 写数据

    同样更具上面的时序图 ,写一个方法传入地址和需要写入的数据,然后将其操作写入到寄存器中。

    1. /**
    2. * 函 数:DS1302写一个字节
    3. * 参 数:Addr 命令字/DS1302寄存器地址
    4. Data 要写入的数据
    5. * 返 回 值:无
    6. */
    7. void DS1302_WriteByte(unsigned char Addr,Data)
    8. {
    9. unsigned char i;
    10. DS1302_CE=1;//读写数据时都置高电平
    11. /*在IO上先写入寄存器地址*/
    12. for(i=0;i<8;i++)
    13. {
    14. DS1302_IO=Addr&(0x01<//地址
    15. DS1302_SCLK=1; //高电平
    16. DS1302_SCLK=0; //低电平
    17. }
    18. for(i=0;i<8;i++)
    19. {
    20. DS1302_IO=Data&(0x01<
    21. DS1302_SCLK=1;
    22. DS1302_SCLK=0;
    23. }
    24. DS1302_CE=0;
    25. }

    2.4 BCD码

    BCD码(Binary Coded Decimal‎),用4位二进制数来表示1位十进制数

    最小000 表示0 ,最大1001 表示9 

    例:0001 0011表示13,1000 0101表示85,0001 1010不合法

    在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

    BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)

    十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD) 

    补充完读写操作如下

    1. //时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    2. unsigned char DS1302_Time[]={24,6,26,18,59,55,3};
    3. /**
    4. * 函 数:DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
    5. * 参 数:无
    6. * 返 回 值:无
    7. */
    8. void DS1302_SetTime(void)
    9. {
    10. DS1302_WriteByte(DS1302_WP,0x00);
    11. DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    12. DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
    13. DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
    14. DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
    15. DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
    16. DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
    17. DS1302_WriteByte(DS1302_WEEK,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
    18. DS1302_WriteByte(DS1302_WP,0x80);
    19. }
    20. /**
    21. * 函 数:DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
    22. * 参 数:无
    23. * 返 回 值:无
    24. */
    25. void DS1302_ReadTime(void)
    26. {
    27. unsigned char Temp;
    28. Temp=DS1302_ReadByte(DS1302_YEAR);
    29. DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
    30. Temp=DS1302_ReadByte(DS1302_MONTH);
    31. DS1302_Time[1]=Temp/16*10+Temp%16;
    32. Temp=DS1302_ReadByte(DS1302_DATE);
    33. DS1302_Time[2]=Temp/16*10+Temp%16;
    34. Temp=DS1302_ReadByte(DS1302_HOUR);
    35. DS1302_Time[3]=Temp/16*10+Temp%16;
    36. Temp=DS1302_ReadByte(DS1302_MINUTE);
    37. DS1302_Time[4]=Temp/16*10+Temp%16;
    38. Temp=DS1302_ReadByte(DS1302_SECOND);
    39. DS1302_Time[5]=Temp/16*10+Temp%16;
    40. Temp=DS1302_ReadByte(DS1302_WEEK);
    41. DS1302_Time[6]=Temp/16*10+Temp%16;
    42. }

    2.5 可编程涓流充电器

    绝对最大额定值
    任何管脚与地之间的电压范围……………………………………………….-0.5V- +7.0V
    工作温度范围,商用级……………………………………………………….0°C - +70°C
    工作温度范围,工业级(IND)………………………………………………-40°C - +85°C
    存储温度范围…………………………………………………………………-55°C - +125°C
    焊接温度(导线,10秒钟)………………………………………………………………….260°C
    焊接温度(表面安装)………………………………………………见 IPC/JEDEC J-STD-020
    超出绝对最大额定值表中列出的应力会使器件产生永久损坏.这些只是额定值,不包括处于或者超出明书 的工作区间所指明的状态的功能性操作. 长期处于绝对最大额定值会影响器件的可靠性

     

    这一块后面做到定时开关机在详细补充。 

    2.6 时钟动态设置

    要做可以设置的时间,首先需要操作按键,我们用独立按键模块来实现。

    K1:切换模式(0-显示模式【默认】,1-设置模式)

    K2:在设置模式下选中要设置的项【年、月、日、时、分、秒、星期】

    K3:实现当前选中项的值-1

    K4:实现当前选中项的值+1

    1. if(KeyNum==2)//按键2按下
    2. {
    3. //设置索引加1
    4. }
    5. if(KeyNum==4)//按键3按下
    6. {
    7. //设置位数字+1
    8. }
    9. if(KeyNum==3)//按键3按下
    10. {
    11. //设置位数字-1
    12. }
    13. if(KeyNum==1)//按键1按下
    14. {
    15. if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
    16. else if(MODE==1){MODE=0;DS1302_SetTime();}//保存设置的时间
    17. }
    18. switch(MODE)//根据不同的功能执行不同的函数
    19. {
    20. case 0:TimeShow();break; //0 显示模式
    21. case 1:TimeSet();break; //1 设置模式
    22. }

    在设置模式下我们希望设置位闪烁(原理是500毫秒显示空,500毫秒显示数值

    1. void Timer1_Routine() interrupt 1
    2. {
    3. static unsigned int T0Count;
    4. TL0 = 0x18; //设置定时初值
    5. TH0 = 0xFC; //设置定时初值
    6. T0Count++;
    7. if(T0Count>=500)//每500ms进入一次
    8. {
    9. T0Count=0;
    10. TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
    11. }
    12. }

     

    三,创建Keil项目

    详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客

    四,代码 

    LCD1602.c

    1. #include
    2. //引脚配置:
    3. sbit LCD_RS=P2^6;
    4. sbit LCD_RW=P2^5;
    5. sbit LCD_EN=P2^7;
    6. #define LCD_DataPort P0
    7. /**
    8. * 函 数:LCD1602延时函数,12MHz调用可延时1ms
    9. * 参 数:无
    10. * 返 回 值:无
    11. */
    12. void LCD_Delay()
    13. {
    14. unsigned char i, j;
    15. i = 2;
    16. j = 239;
    17. do
    18. {
    19. while (--j);
    20. } while (--i);
    21. }
    22. /**
    23. * 函 数:LCD1602写命令
    24. * 参 数:Command 要写入的命令
    25. * 返 回 值:无
    26. */
    27. void LCD_WriteCommand(unsigned char Command)
    28. {
    29. LCD_RS=0;
    30. LCD_RW=0;
    31. LCD_DataPort=Command;
    32. LCD_EN=1;
    33. LCD_Delay();
    34. LCD_EN=0;
    35. LCD_Delay();
    36. }
    37. /**
    38. * 函 数:LCD1602写数据
    39. * 参 数:Data 要写入的数据
    40. * 返 回 值:无
    41. */
    42. void LCD_WriteData(unsigned char Data)
    43. {
    44. LCD_RS=1;
    45. LCD_RW=0;
    46. LCD_DataPort=Data;
    47. LCD_EN=1;
    48. LCD_Delay();
    49. LCD_EN=0;
    50. LCD_Delay();
    51. }
    52. /**
    53. * 函 数:LCD1602设置光标位置
    54. * 参 数:Line 行位置,范围:1~2
    55. * 返 回 值:Column 列位置,范围:1~16
    56. */
    57. void LCD_SetCursor(unsigned char Line,unsigned char Column)
    58. {
    59. if(Line==1)
    60. {
    61. LCD_WriteCommand(0x80|(Column-1));
    62. }
    63. else if(Line==2)
    64. {
    65. LCD_WriteCommand(0x80|(Column-1+0x40));
    66. }
    67. }
    68. /**
    69. * 函 数:LCD1602初始化函数
    70. * 参 数:无
    71. * 返 回 值:无
    72. */
    73. void LCD_Init()
    74. {
    75. LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
    76. LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
    77. LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
    78. LCD_WriteCommand(0x01);//光标复位,清屏
    79. }
    80. /**
    81. * 函 数:在LCD1602指定位置上显示一个字符
    82. * 参 数: Line 行位置,范围:1~2,
    83. * Column 列位置,范围:1~16,
    84. * Char 要显示的字符
    85. * 返 回 值:无
    86. */
    87. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
    88. {
    89. LCD_SetCursor(Line,Column);
    90. LCD_WriteData(Char);
    91. }
    92. /**
    93. * 函 数:在LCD1602指定位置开始显示所给字符串
    94. * 参 数: Line 起始行位置,范围:1~2,
    95. * Column 起始列位置,范围:1~16,
    96. * String 要显示的字符串
    97. * 返 回 值:无
    98. */
    99. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
    100. {
    101. unsigned char i;
    102. LCD_SetCursor(Line,Column);
    103. for(i=0;String[i]!='\0';i++)
    104. {
    105. LCD_WriteData(String[i]);
    106. }
    107. }
    108. /**
    109. * 函 数:返回值=X的Y次方
    110. * 参 数:X底数,Y指数
    111. * 返 回 值:X的Y次方
    112. */
    113. int LCD_Pow(int X,int Y)
    114. {
    115. unsigned char i;
    116. int Result=1;
    117. for(i=0;i
    118. {
    119. Result*=X;
    120. }
    121. return Result;
    122. }
    123. /**
    124. * 函 数:在LCD1602指定位置开始显示所给数字
    125. * 参 数: Line 起始行位置,范围:1~2
    126. * Column 起始列位置,范围:1~16
    127. * Number 要显示的数字,范围:0~65535
    128. * Length 要显示数字的长度,范围:1~5
    129. * 返 回 值:无
    130. */
    131. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    132. {
    133. unsigned char i;
    134. LCD_SetCursor(Line,Column);
    135. for(i=Length;i>0;i--)
    136. {
    137. LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
    138. }
    139. }
    140. /**
    141. * 函 数:在LCD1602指定位置开始以有符号十进制显示所给数字
    142. * 参 数: Line 起始行位置,范围:1~2
    143. * Column 起始列位置,范围:1~16
    144. * Number 要显示的数字,范围:-32768~32767
    145. * Length 要显示数字的长度,范围:1~5
    146. * 返 回 值:无
    147. */
    148. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
    149. {
    150. unsigned char i;
    151. unsigned int Number1;
    152. LCD_SetCursor(Line,Column);
    153. if(Number>=0)
    154. {
    155. LCD_WriteData('+');
    156. Number1=Number;
    157. }
    158. else
    159. {
    160. LCD_WriteData('-');
    161. Number1=-Number;
    162. }
    163. for(i=Length;i>0;i--)
    164. {
    165. LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
    166. }
    167. }
    168. /**
    169. * 函 数:在LCD1602指定位置开始以十六进制显示所给数字
    170. * 参 数: Line 起始行位置,范围:1~2
    171. * Column 起始列位置,范围:1~16
    172. * Number 要显示的数字,范围:0~0xFFFF
    173. * Length 要显示数字的长度,范围:1~4
    174. * 返 回 值:无
    175. */
    176. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    177. {
    178. unsigned char i,SingleNumber;
    179. LCD_SetCursor(Line,Column);
    180. for(i=Length;i>0;i--)
    181. {
    182. SingleNumber=Number/LCD_Pow(16,i-1)%16;
    183. if(SingleNumber<10)
    184. {
    185. LCD_WriteData(SingleNumber+'0');
    186. }
    187. else
    188. {
    189. LCD_WriteData(SingleNumber-10+'A');
    190. }
    191. }
    192. }
    193. /**
    194. * 函 数:在LCD1602指定位置开始以二进制显示所给数字
    195. * 参 数: Line 起始行位置,范围:1~2
    196. * Column 起始列位置,范围:1~16
    197. * Number 要显示的数字,范围:0~1111 1111 1111 1111
    198. * Length 要显示数字的长度,范围:1~16
    199. * 返 回 值:无
    200. */
    201. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    202. {
    203. unsigned char i;
    204. LCD_SetCursor(Line,Column);
    205. for(i=Length;i>0;i--)
    206. {
    207. LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
    208. }
    209. }

    LCD1602.h

    1. #ifndef __LCD1602_H__
    2. #define __LCD1602_H__
    3. //用户调用函数:
    4. void LCD_Init();
    5. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
    6. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
    7. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    8. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
    9. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    10. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    11. #endif

     Delay.c

    1. /**
    2. * 函 数:延时函数 毫秒
    3. * 参 数:ms 延时多少毫秒
    4. * 返 回 值:无
    5. */
    6. void Delay_ms(int ms) //@12.000MHz
    7. {
    8. unsigned char data i, j;
    9. while(ms--)
    10. {
    11. i = 2;
    12. j = 239;
    13. do
    14. {
    15. while (--j);
    16. } while (--i);
    17. }
    18. }
    19. /**
    20. * 函 数:延时函数 毫秒
    21. * 参 数:ms 延时多少毫秒
    22. * 返 回 值:无
    23. */
    24. void Delay_us(int ms) //@12.000MHz
    25. {
    26. unsigned char data i, j;
    27. while(ms--)
    28. {
    29. i = 2;
    30. j = 239;
    31. do
    32. {
    33. while (--j);
    34. } while (--i);
    35. }
    36. }
    37. /**
    38. * 函 数:延时函数 10微秒
    39. * 参 数:无
    40. * 返 回 值:无
    41. */
    42. void Delay_10us(int _10us) //@11.0592MHz
    43. {
    44. unsigned char data i;
    45. while(_10us--)
    46. {
    47. i = 2;
    48. while (--i);
    49. }
    50. }

    Delay.h

    1. #ifndef __DELAY_H_
    2. #defind __DELAY_H_
    3. void Delay_ms(int ms);
    4. void Delay_10us(int _10us);
    5. #endif

     DS1302.c

    1. #include
    2. //引脚定义
    3. sbit DS1302_SCLK=P3^6; //时钟针脚 输入. SCLK 用来同步串行接口上的数据动作.此管脚内部有一个
    4. //40kΩ(典型值)的下拉电阻连接到地
    5. sbit DS1302_IO=P3^4; //数据针脚 推挽输出.I/O 管脚是三线接口的双向数据管脚.此管脚内部有
    6. //一个 40kΩ(典型值)的下拉电阻连接到地
    7. sbit DS1302_CE=P3^5; //重置位针脚 CE信号在读写时必须保持高电平.此管脚内部有一个 40kΩ(典
    8. //型值)的下拉电阻连接到地. 注意:先前的数据手册修正把 CE当作
    9. //RST. 管脚的功能没有改变.
    10. //寄存器写入地址/指令定义
    11. #define DS1302_YEAR 0x8C //年
    12. #define DS1302_MONTH 0x88 //月
    13. #define DS1302_DATE 0x86 //日
    14. #define DS1302_HOUR 0x84 //时
    15. #define DS1302_MINUTE 0x82 //分
    16. #define DS1302_SECOND 0x80 //秒
    17. #define DS1302_WEEK 0x8A //星期
    18. #define DS1302_WP 0x8E //写保护位
    19. //控制寄存器的位 7是写保护位,前 7位(位 0至位 6)被强制为 0且读取时总是读 0. 在任
    20. //何对时钟或 RAM的写操作以前,位 7必须为 0.当为高时,写保护位禁止任何寄存器的写操
    21. //作. 初始加电状态未定义. 因此,在试图写器件之前应该清除 WP位.
    22. //时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    23. unsigned char DS1302_Time[]={24,6,26,18,59,55,3};
    24. /**
    25. * 函 数:DS1302初始化
    26. * 参 数:无
    27. * 返 回 值:无
    28. */
    29. void DS1302_Init(void)
    30. {
    31. DS1302_CE=0; //重置数据引脚,没有读写数据时给低电平
    32. DS1302_SCLK=0; //时钟引脚,没有读写操作时给低电平
    33. }
    34. /**
    35. * 函 数:DS1302写一个字节
    36. * 参 数:Addr 命令字/DS1302寄存器地址
    37. Data 要写入的数据
    38. * 返 回 值:无
    39. */
    40. void DS1302_WriteByte(unsigned char Addr,Data)
    41. {
    42. unsigned char i;
    43. DS1302_CE=1;//读写数据时都置高电平
    44. /*在IO上先写入寄存器地址*/
    45. for(i=0;i<8;i++)
    46. {
    47. DS1302_IO=Addr&(0x01<//地址
    48. DS1302_SCLK=1; //高电平
    49. DS1302_SCLK=0; //低电平
    50. }
    51. for(i=0;i<8;i++)
    52. {
    53. DS1302_IO=Data&(0x01<
    54. DS1302_SCLK=1;
    55. DS1302_SCLK=0;
    56. }
    57. DS1302_CE=0;
    58. }
    59. /**
    60. * 函 数:DS1302读一个字节
    61. * 参 数:Addr 命令字/DS1302寄存器地址
    62. * 返 回 值:无
    63. */
    64. unsigned char DS1302_ReadByte(unsigned char Addr)
    65. {
    66. unsigned char i,Data=0x00;
    67. Addr|=0x01; //将指令转换为读指令
    68. DS1302_CE=1; //读写数据时都置高电平
    69. for(i=0;i<8;i++)
    70. {
    71. DS1302_IO=Addr&(0x01<
    72. DS1302_SCLK=0;
    73. DS1302_SCLK=1;
    74. }
    75. for(i=0;i<8;i++)
    76. {
    77. DS1302_SCLK=1;
    78. DS1302_SCLK=0;
    79. if(DS1302_IO){Data|=(0x01<
    80. }
    81. DS1302_CE=0;
    82. DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
    83. return Data;
    84. }
    85. /**
    86. * 函 数:DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
    87. * 参 数:无
    88. * 返 回 值:无
    89. */
    90. void DS1302_SetTime(void)
    91. {
    92. DS1302_WriteByte(DS1302_WP,0x00);
    93. DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    94. DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
    95. DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
    96. DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
    97. DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
    98. DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
    99. DS1302_WriteByte(DS1302_WEEK,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
    100. DS1302_WriteByte(DS1302_WP,0x80);
    101. }
    102. /**
    103. * 函 数:DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
    104. * 参 数:无
    105. * 返 回 值:无
    106. */
    107. void DS1302_ReadTime(void)
    108. {
    109. unsigned char Temp;
    110. Temp=DS1302_ReadByte(DS1302_YEAR);
    111. DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
    112. Temp=DS1302_ReadByte(DS1302_MONTH);
    113. DS1302_Time[1]=Temp/16*10+Temp%16;
    114. Temp=DS1302_ReadByte(DS1302_DATE);
    115. DS1302_Time[2]=Temp/16*10+Temp%16;
    116. Temp=DS1302_ReadByte(DS1302_HOUR);
    117. DS1302_Time[3]=Temp/16*10+Temp%16;
    118. Temp=DS1302_ReadByte(DS1302_MINUTE);
    119. DS1302_Time[4]=Temp/16*10+Temp%16;
    120. Temp=DS1302_ReadByte(DS1302_SECOND);
    121. DS1302_Time[5]=Temp/16*10+Temp%16;
    122. Temp=DS1302_ReadByte(DS1302_WEEK);
    123. DS1302_Time[6]=Temp/16*10+Temp%16;
    124. }

    DS1302.h

    1. #ifndef __DS1302_H__
    2. #define __DS1302_H__
    3. //外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    4. extern unsigned char DS1302_Time[];
    5. void DS1302_Init(void);
    6. void DS1302_WriteByte(unsigned char Addr,Data);
    7. unsigned char DS1302_ReadByte(unsigned char Addr);
    8. void DS1302_SetTime(void);
    9. void DS1302_ReadTime(void);
    10. #endif

    4.1 读写显示时间  main.c

    1. #include
    2. #include "LCD1602.h"
    3. #include "DS1302.h"
    4. void main()
    5. {
    6. LCD_Init(); //LCD1602 液晶显示屏初始化
    7. DS1302_Init(); //DS1302 时钟初始化
    8. LCD_ShowString(1,1," - - W: ");//静态字符初始化显示
    9. LCD_ShowString(2,1," : : ");
    10. DS1302_SetTime();//设置时间
    11. while(1)
    12. {
    13. DS1302_ReadTime();//读取时间
    14. LCD_ShowNum(1,1,DS1302_Time[0]+2000,4);//显示年
    15. LCD_ShowNum(1,6,DS1302_Time[1],2);//显示月
    16. LCD_ShowNum(1,9,DS1302_Time[2],2);//显示日
    17. LCD_ShowNum(2,3,DS1302_Time[3],2);//显示时
    18. LCD_ShowNum(2,6,DS1302_Time[4],2);//显示分
    19. LCD_ShowNum(2,9,DS1302_Time[5],2);//显示秒
    20. LCD_ShowNum(1,14,DS1302_Time[6],1);//显示星期
    21. }
    22. }

    4.2 手动设置时间、重新上电显示最后设置的时间 main.c

    1. #include
    2. #include "LCD1602.h"
    3. #include "DS1302.h"
    4. #include "Key.h"
    5. #include "Timer1.h"
    6. unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
    7. /**
    8. * 函 数:显示时间
    9. * 参 数:无
    10. * 返 回 值:无
    11. */
    12. void TimeShow(void)//时间显示功能
    13. {
    14. DS1302_ReadTime();//读取时间
    15. LCD_ShowNum(1,1,DS1302_Time[0]+2000,4);//显示年
    16. LCD_ShowNum(1,6,DS1302_Time[1],2);//显示月
    17. LCD_ShowNum(1,9,DS1302_Time[2],2);//显示日
    18. LCD_ShowNum(2,3,DS1302_Time[3],2);//显示时
    19. LCD_ShowNum(2,6,DS1302_Time[4],2);//显示分
    20. LCD_ShowNum(2,9,DS1302_Time[5],2);//显示秒
    21. LCD_ShowNum(1,14,DS1302_Time[6],1);//显示星期
    22. }
    23. /**
    24. * 函 数:设置时间
    25. * 参 数:无
    26. * 返 回 值:无
    27. */
    28. void TimeSet(void)//时间设置功能
    29. {
    30. if(KeyNum==2)//按键2按下
    31. {
    32. TimeSetSelect++;//设置选择位加1
    33. TimeSetSelect%=7;//越界清零
    34. }
    35. if(KeyNum==4)//按键3按下
    36. {
    37. DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
    38. if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
    39. if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
    40. if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
    41. DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
    42. {
    43. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
    44. }
    45. else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
    46. {
    47. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
    48. }
    49. else if(DS1302_Time[1]==2)
    50. {
    51. if(DS1302_Time[0]%4==0)
    52. {
    53. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
    54. }
    55. else
    56. {
    57. if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
    58. }
    59. }
    60. if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
    61. if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
    62. if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
    63. if(DS1302_Time[6]>6){DS1302_Time[6]=0;}//星期越界判断
    64. }
    65. if(KeyNum==3)//按键3按下
    66. {
    67. DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
    68. if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
    69. if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
    70. if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
    71. DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
    72. {
    73. if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
    74. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
    75. }
    76. else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
    77. {
    78. if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
    79. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
    80. }
    81. else if(DS1302_Time[1]==2)
    82. {
    83. if(DS1302_Time[0]%4==0)
    84. {
    85. if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
    86. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
    87. }
    88. else
    89. {
    90. if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
    91. if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
    92. }
    93. }
    94. if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
    95. if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
    96. if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
    97. if(DS1302_Time[6]<0){DS1302_Time[6]=6;}//星期越界判断
    98. }
    99. //更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
    100. if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,3," ");}
    101. else {LCD_ShowNum(1,3,DS1302_Time[0],2);}
    102. if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,6," ");}
    103. else {LCD_ShowNum(1,6,DS1302_Time[1],2);}
    104. if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,9," ");}
    105. else {LCD_ShowNum(1,9,DS1302_Time[2],2);}
    106. if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,3," ");}
    107. else {LCD_ShowNum(2,3,DS1302_Time[3],2);}
    108. if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,6," ");}
    109. else {LCD_ShowNum(2,6,DS1302_Time[4],2);}
    110. if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,9," ");}
    111. else {LCD_ShowNum(2,9,DS1302_Time[5],2);}
    112. if(TimeSetSelect==6 && TimeSetFlashFlag==1){LCD_ShowString(1,14," ");}
    113. else {LCD_ShowNum(1,14,DS1302_Time[6],1);}
    114. }
    115. void main()
    116. {
    117. LCD_Init(); //LCD1602 液晶显示屏初始化
    118. DS1302_Init(); //DS1302 时钟初始化
    119. Timer1_Init(); //初始化定时器
    120. LCD_ShowString(1,1," - - W: ");//静态字符初始化显示
    121. LCD_ShowString(2,1," : : ");
    122. while(1)
    123. {
    124. KeyNum=Key();//读取键码
    125. if(KeyNum==1)//按键1按下
    126. {
    127. if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
    128. else if(MODE==1){MODE=0;DS1302_SetTime();}//保存设置的时间
    129. }
    130. switch(MODE)//根据不同的功能执行不同的函数
    131. {
    132. case 0:TimeShow();break; //0 显示模式
    133. case 1:TimeSet();break; //1 设置模式
    134. }
    135. }
    136. }
    137. void Timer1_Routine() interrupt 1
    138. {
    139. static unsigned int T0Count;
    140. TL0 = 0x18; //设置定时初值
    141. TH0 = 0xFC; //设置定时初值
    142. T0Count++;
    143. if(T0Count>=500)//每500ms进入一次
    144. {
    145. T0Count=0;
    146. TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
    147. }
    148. }

    Timer1.c

    1. #include
    2. /**
    3. * 函 数:定时器0初始化,1毫秒@12.000MHz
    4. * 参 数:无
    5. * 返 回 值:无
    6. */
    7. void Timer1_Init(void)
    8. {
    9. TMOD &= 0xF0; //设置定时器模式
    10. TMOD |= 0x01; //设置定时器模式
    11. TL0 = 0x18; //设置定时初值
    12. TH0 = 0xFC; //设置定时初值
    13. TF0 = 0; //清除TF0标志
    14. TR0 = 1; //定时器0开始计时
    15. ET0=1; //允许时钟1中断
    16. EA=1; //打开总中断
    17. PT0=0; //优先级设置为最高(0-3)
    18. }
    19. /*定时器中断函数模板 关联较多,直接将其写入mian.c 中
    20. void Timer1_Routine() interrupt 1
    21. {
    22. static unsigned int T0Count;
    23. TL0 = 0x18; //设置定时初值
    24. TH0 = 0xFC; //设置定时初值
    25. T0Count++;
    26. if(T0Count>=1000)
    27. {
    28. T0Count=0;
    29. }
    30. }
    31. */

     Timer1.h

    1. #ifndef __TIMER1_H__
    2. #define __TIMER1_H__
    3. void Timer1_Init(void);
    4. #endif

    五,代码编译、下载到51单片机

    代码编译请参考

    51单片机STC89C52RC——代码编译-CSDN博客

    代码下载请参考

    《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客

  • 相关阅读:
    软件产品确认测试包括哪些方面
    java计算机毕业设计springboot+vue留学服务管理平台系统(源码+系统+mysql数据库+Lw文档)
    MySQL导入导出、视图、索引、执行计划
    TSC2012HYDT双向电流感应放大器芯片中文资料规格书PDF数据手册引脚图参数概述
    Flask 的 ORM 模型 - 概述
    Doris 集群的手动部署
    【数据治理】2021年 中国数据治理行业:全链产业图谱梳理
    Android 12(S) 图像显示系统 - BufferQueue的工作流程(十)
    Jenkins nginx自动化构建前端vue项目
    IDA Pro与x64dbg联动调试记录
  • 原文地址:https://blog.csdn.net/oopxiajun2011/article/details/139983492