找回密码
 注册会员
更新自动建库工具PCB Footprint Expert 2024.04 Pro / Library Expert 破解版

自学AVR单片机二十二(时钟芯片DS1302)

[复制链接]
慧龙 发表于 2010-5-7 11:15:32 | 显示全部楼层 |阅读模式

本文包含原理图、PCB、源代码、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有账号?注册会员

×
一、简单原理
        DS1302是一种高性能、低功耗、带RAM的实时时钟芯片,它可以对年、月、日、星期、天、时、分、秒自动计时,并且具有闰年补偿功能。
        DS1302具有宽电压工作范围:2.7-5.5v。
       DS1302采用3线串行接口与单片机进行同步通信,并可采用突发方式一次传送多字节的时钟信号或RAM数据。
        DS1302内部有一个31字节的用于临时存放数据的RAM寄存器。
       DS1302还具备主电源/后备电源供电的双电源引脚,可以自动切换电源,并且提供了对后备电源进行涓细电流充电的能力。
       DS1302的引脚说明如下:
8160_1243303239JD25.jpg
Vcc1为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。X1和X2是振荡源,外接32.768kHz晶振。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc≥2.5V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),SCLK始终是输入端。
     

DS1302的控制字节

DS1302的控制字如下图所示。控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出

8160_1243303239JD25.jpg

数据输入输出(I/O)

在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。

DS1302的寄存器

DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字请查阅DS1302的数据手册。




二、电路实现
   如下是本实例中DS1302的电路连接图。由于只是学习DS1302的基本功能,所以本电路没有使用后备电源。
8160_1243303239JD25.jpg

三、 程序设计
下面是DS1302的完整程序,程序实现将一个初始化时间写入DS1302,然后读出DS1302的实时时间,并发送到串口,在计算机上通过串口助手观察程序执行结果。


  1. #include<AVR/io.h>        
  2. #include <util/delay.h>
  3. #include <avr/interrupt.h>   //中断函数头文件
  4. //常量声明
  5. #define BAUD 9600
  6. #define TURE 1
  7. #define FALSE 0
  8. //时钟/日历寄存器
  9. #define RD    0x01         //读
  10. #define WR    0x00         //写
  11. #define SECOND  0x80 //秒
  12. #define MINUTE  0x82 //分
  13. #define HOUR  0x84 //时
  14. #define DAY   0x86 //日
  15. #define MONTH  0x88 //月
  16. #define WEEK  0x8A //星期 DATE
  17. #define YEAR  0x8C //年
  18. #define WR_PROTECT 0x8E //控制(写保护)
  19. #define CHARGE     0x90 //涓流充电
  20. #define BURST  0xBE //时钟多字节
  21. //配置位
  22. #define CLK_HALT  0x80 //停止时钟控制位    SECOND bit7
  23. #define CLK_START  0x00 //启动时钟
  24. #define M12_24   0x80 //12/24小时值选择位 HOUR  bit7
  25. #define PROTECT   0x80 //写保护控制位      CONTROL bit7
  26. #define UPROTECT  0x00 //写保护控制位      CONTROL bit7
  27. //涓流充电控制常量
  28. #define TC_D1R2   0xA5 //充电时选择一个二极管和2K电阻  
  29. #define TC_D2R8   0xAB //充电时选择二个二极管和8K电阻   
  30. #define TC_DISABLED  0x00 //禁止充电功能
  31. //RAM 命令
  32. #define RAMBASE  0xC0 //RAM起始位为0XCO,RAM范围0-31
  33. //全局变量声明
  34. unsigned char Get_Time[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};

  35. //函数声明
  36. void Delayus(unsigned int lus);         //us延时函数
  37. void Delayms(unsigned int lms);        //ms延时函数
  38. void Port_Init(void);   //端口初始化配置
  39. void Usart_Init(void);  //USART寄存器设置
  40. void Usart_PutChar(unsigned char cTXData);  //字节发送函数
  41. void Usart_PutString(unsigned char *pcString); //字符串发送函数
  42. unsigned char DS1302_ReadByte(void);         //从DS1302读一个字节数据
  43. void DS1302_WriteByte(unsigned char dat);    //向DS1302写一个字节数据
  44. unsigned char DS1302_ReadData(unsigned addr); //从DS1302的指定地址读一个字节数据
  45. void DS1302_WriteData(unsigned char addr,unsigned data);  
  46.                                                  //向DS1302的指定地址写一个字节数据
  47. void DS1302_SetTime(unsigned char *time);  //对DS1302设置时间
  48. void DS1302_GetTime(void);   //从DS1302读取时间
  49. unsigned char DS1302_Check(void);           //DS1302是否工作检测
  50. void DS1302_Init(void);                      //DS1302初始化
  51. int main(void)            
  52. {
  53. unsigned char Set_Time[7] = {0x59,0x59,0x10,0x20,0x01,0x02,0x09};
  54.                                         //设置秒,分,时,日,月,星期,年
  55. unsigned char i;

  56. Port_Init();
  57. Usart_Init();

  58. Usart_PutString("DS1302实时时钟试验");
  59. Usart_PutString("设置当前时间为:");
  60. Usart_PutChar(0x0D);
  61. Usart_PutChar(0x0A);  //发送回车换行
  62. Usart_PutChar('2');
  63. Usart_PutChar('0');
  64. Usart_PutChar(Set_Time[6] / 16 + 0x30);
  65. Usart_PutChar(Set_Time[6] % 16 + 0x30);
  66. Usart_PutString("年");
  67. Usart_PutChar(' ');
  68. Usart_PutChar(Set_Time[4] / 16 + 0x30);
  69. Usart_PutChar(Set_Time[4] % 16 + 0x30);
  70. Usart_PutString("月");
  71. Usart_PutChar(' ');
  72. Usart_PutChar(Set_Time[3] / 16 + 0x30);
  73. Usart_PutChar(Set_Time[3] % 16 + 0x30);
  74. Usart_PutString("日");
  75. Usart_PutChar(' ');
  76. Usart_PutChar(Set_Time[2] / 16 + 0x30);
  77. Usart_PutChar(Set_Time[2] % 16 + 0x30);
  78. Usart_PutString("时");
  79. Usart_PutChar(' ');
  80. Usart_PutChar(Set_Time[1] / 16 + 0x30);
  81. Usart_PutChar(Set_Time[1] % 16 + 0x30);
  82. Usart_PutString("分");
  83. Usart_PutChar(' ');
  84. Usart_PutChar(Set_Time[0] / 16 + 0x30);
  85. Usart_PutChar(Set_Time[0] % 16 + 0x30);
  86. Usart_PutString("秒");
  87. Usart_PutChar(' ');
  88. Usart_PutString("星期");
  89. Usart_PutChar(' ');
  90. //Usart_PutChar(Set_Time[5] / 16 + 0x30);
  91. Usart_PutChar(Set_Time[5] % 16 + 0x30);
  92. Usart_PutChar(0x0D);
  93. Usart_PutChar(0x0A);  //发送回车换行

  94. DS1302_SetTime(Set_Time);


  95. sei();          //使能全局中断  

  96. while(1)
  97. {
  98.   DS1302_GetTime();
  99.   
  100.   Usart_PutString("现在时间");
  101.   Usart_PutChar(0x0D);
  102.   Usart_PutChar(0x0A);  //发送回车换行
  103.   Usart_PutChar('2');
  104.   Usart_PutChar('0');
  105.   Usart_PutChar(Get_Time[6] / 16 + 0x30);
  106.   Usart_PutChar(Get_Time[6] % 16 + 0x30);
  107.   Usart_PutString("年");
  108.   Usart_PutChar(' ');
  109.   Usart_PutChar(Get_Time[4] / 16 + 0x30);
  110.   Usart_PutChar(Get_Time[4] % 16 + 0x30);
  111.   Usart_PutString("月");
  112.   Usart_PutChar(' ');
  113.   Usart_PutChar(Get_Time[3] / 16 + 0x30);
  114.   Usart_PutChar(Get_Time[3] % 16 + 0x30);
  115.   Usart_PutString("日");
  116.   Usart_PutChar(' ');
  117.   Usart_PutChar(Get_Time[2] / 16 + 0x30);
  118.   Usart_PutChar(Get_Time[2] % 16 + 0x30);
  119.   Usart_PutString("时");
  120.   Usart_PutChar(' ');
  121.   Usart_PutChar(Get_Time[1] / 16 + 0x30);
  122.   Usart_PutChar(Get_Time[1] % 16 + 0x30);
  123.   Usart_PutString("分");
  124.   Usart_PutChar(' ');
  125.   Usart_PutChar(Get_Time[0] / 16 + 0x30);
  126.   Usart_PutChar(Get_Time[0] % 16 + 0x30);
  127.   Usart_PutString("秒");
  128.   Usart_PutChar(' ');
  129.   Usart_PutString("星期");
  130.   Usart_PutChar(' ');
  131.   
  132.   //Usart_PutChar(Set_Time[5] / 16 + 0x30);
  133.   Usart_PutChar(Get_Time[5] % 16 + 0x30);
  134.   Usart_PutChar(0x0D);
  135.   Usart_PutChar(0x0A);  //发送回车换行

  136. for(i = 0;i < 4;i++)
  137. {
  138.   Delayms(1000);
  139. }

  140. }
  141. }
  142. //端口状态初始化设置函数
  143. void Port_Init()
  144. {
  145. PORTD = 0X00;          //USART的发送接收端口分别为PD0和PD1
  146. DDRD |= (1 << PD3);   //PD0为接收端口,置为输入口;PD1为发送端口,置为输出口

  147. DDRE |= (1 << PE2) | (1 << PE3) | (1 << PE6); //DS1302的SCLK和I/O引脚设为输出
  148. //DDRE |= (1 << PE6);                //DS1302的RST引脚设为输出
  149. }
  150. //USART寄存器配置函数
  151. void Usart_Init()
  152. {
  153. UCSR1A = 0X00;
  154. UCSR1C |= (1 << UCSZ11) | (1 << UCSZ10);  //异步,数据格式8,N,1
  155.       
  156. UBRR1L = (F_CPU / BAUD / 16 - 1) % 256;    //波特率设置
  157. UBRR1H = (F_CPU / BAUD / 16 - 1) / 256;  
  158. UCSR1B |= (1 << RXCIE1) | (1 << RXEN1) | (1 << TXEN1);    //发送使能

  159. }
  160. //字节发送函数
  161. void Usart_PutChar(unsigned char cTXData)
  162. {
  163. while( !(UCSR1A & (1 << UDRE1)) );  //只有数据寄存器为空时才能发送数据
  164. UDR1 = cTXData;                  //发送数据送USART I/O数据寄存器-UDR
  165. }
  166. //接收中断函数
  167. ISR(USART1_RX_vect)
  168. {
  169. unsigned char Rev;
  170. Rev = UDR1;              //从USART I/O数据寄存器-UDR中读出数据
  171. Usart_PutChar(Rev);    //将接收到的数据发送
  172. }

  173. void Usart_PutString(unsigned char *pcString)
  174. {
  175. while (*pcString)
  176. {
  177.   Usart_PutChar(*pcString++);
  178. }
  179. }
  180. //从DS1302读一个字节数据
  181. unsigned char DS1302_ReadByte(void)         
  182. {
  183. unsigned char i,dat = 0;  //dat存放读出的数据,初始化为0
  184. PORTE &= ~(1 << PE3);     //DS1302的I/O口上拉不使能,
  185. DDRE &= ~(1 << PE3);      //DS1302的I/O口设置为输入口,准备读数据

  186. for(i = 0;i < 8;i++)     //读8位,低位在前,右移
  187. {
  188.   dat >>= 1;           //读出的数据右移一位
  189.   PORTE |= (1 << PE2);  //DS1302的SCLK端口拉高
  190.   Delayus(10);          //
  191.   PORTE &= ~(1 << PE2);  //DS1302的SCLK端口拉低,产生下降沿,
  192.   Delayus(10);
  193.   if(PINE & (1 << PE3))     //读数据端口状态
  194.   {
  195.    dat |= 0x80;          //如果数据端口位高,相应数据位置1
  196.   }  
  197. }
  198. DDRE |= (1 << PE3);   //最后将数据端口设置为输出
  199.     return dat;     //返回读出的数据
  200. }
  201. //向DS1302写一个字节数据
  202. void DS1302_WriteByte(unsigned char dat)   
  203. {
  204. unsigned char i;

  205. for(i = 0;i < 8;i++)      //写8位,低位在前
  206. {
  207.   PORTE &= ~(1 << PE2);  //DS1302的SCLK置低
  208.   if(dat & 0x01)        //写数据位
  209.   {
  210.    PORTE |= (1 << PE3);   //如果该位为1,则I/O口置高
  211.   }
  212.   else
  213.   {
  214.    PORTE &= ~(1 << PE3);   //如果该位为0,则I/O口置低
  215.   }
  216.   Delayus(10);          //
  217.   PORTE |= (1 << PE2);   //DS1302的SCLK置高,产生上升沿
  218.   dat >>= 1;               //数据右移1位
  219. }  
  220. }
  221. //从DS1302的指定地址读一个字节数据
  222. unsigned char DS1302_ReadData(unsigned addr)
  223. {
  224. unsigned char data;

  225. PORTE &= ~(1 << PE6); //拉低片选端
  226. PORTE &= ~(1 << PE2);//拉低时钟端
  227. Delayus(10);
  228. PORTE |= (1 << PE6);//拉高片选端
  229. Delayus(10);
  230. DS1302_WriteByte(addr);//写入操作命令(地址)
  231. Delayus(10);
  232. data = DS1302_ReadByte();//读出数据
  233. Delayus(10);
  234. PORTE &= ~(1 << PE2);  //拉低时钟端
  235. PORTE &= ~(1 << PE6); //拉低片选端  

  236. return data;
  237. }
  238. //向DS1302的指定地址写一个字节数据
  239. void DS1302_WriteData(unsigned char addr,unsigned data)  
  240. {
  241. PORTE &= ~(1 << PE6); //拉低片选端
  242. PORTE &= ~(1 << PE2);//拉低时钟端
  243. Delayus(10);
  244. PORTE |= (1 << PE6);//拉高片选端
  245. Delayus(10);
  246. DS1302_WriteByte(addr);//写入操作命令(地址)
  247. Delayus(10);
  248. PORTE &= ~(1 << PE2);//拉低时钟端
  249. Delayus(10);
  250. DS1302_WriteByte(data);//写入数据
  251. PORTE &= ~(1 << PE2);  //拉低时钟端
  252. Delayus(10);
  253. PORTE &= ~(1 << PE6); //拉低片选端
  254. }
  255. //对DS1302设置时间            
  256. void DS1302_SetTime(unsigned char *time)  
  257. {
  258. unsigned char i;
  259. unsigned char addr = 0x80;//写入地址从秒寄存器开始

  260. DS1302_WriteData(WR_PROTECT | WR,UPROTECT);//控制命令,WP位为0,允许写操作
  261. Delayms(5);
  262. for(i = 0;i < 7;i++)
  263. {
  264.   DS1302_WriteData(addr | WR,time);// 秒 分 时 日 月 星期 年   
  265.   addr += 2;
  266.   Delayms(1);
  267. }
  268. DS1302_WriteData(WR_PROTECT | WR,PROTECT);//控制命令,WP位为1,不允许写操作   
  269. }
  270. //从DS1302读取时间
  271. void DS1302_GetTime(void)   
  272. {
  273. unsigned char i;
  274. /*
  275. //
  276. unsigned char addr;
  277. PORTA &= ~(1 << PA1);
  278. for(i = 0;i < 7;i++)
  279. {
  280.   tim = DS1302_ReadData(addr | RD);
  281.   addr += 2;
  282. }
  283. */

  284. ////////////////////
  285. PORTE &= ~(1 << PE6);
  286. Delayus(10);
  287. PORTE |= (1 << PE6);
  288. Delayus(10);
  289. DS1302_WriteByte(0xbf);
  290. for(i = 0;i < 8;i++)
  291. {
  292.   Get_Time = DS1302_ReadByte();
  293. }
  294. PORTE &= ~(1 << PE6);
  295. ///////////////////////////////////
  296. PORTE &= ~(1 << PE2);
  297. }
  298. //DS1302是否工作检测
  299. unsigned char DS1302_Check(void)           
  300. {
  301. DS1302_WriteData(WR_PROTECT | WR,UPROTECT);
  302. DS1302_WriteData(RAMBASE | WR,0x31);

  303. if(DS1302_ReadData(RAMBASE | WR) == 0x31)
  304. {
  305.   return TURE;
  306. }
  307. else
  308. {
  309.   return FALSE;
  310. }
  311. }
  312. //DS1302初始化
  313. void DS1302_Init(void)     
  314. {
  315. DS1302_WriteData(WR_PROTECT | WR,UPROTECT);   //写入写允许命令
  316.     DS1302_WriteData(SECOND | WR,CLK_START);      //启动振荡器,DS1302开始工作
  317. DS1302_WriteData( WR_PROTECT | WR,PROTECT);     //控制命令,WP位为1,不允许写操作   
  318. }

  319. //us级别的延时函数
  320. void Delayus(unsigned int lus)
  321. {
  322. while(lus--)
  323. {
  324.   _delay_loop_2(4);      //_delay_loop_2(1)是延时4个时钟周期,参数为3则延时12
  325.              //个时钟周期,本实验用12M晶体,则12个时钟周期为12/12=1us
  326.     }
  327. }
  328. //ms级别的延时函数
  329. void Delayms(unsigned int lms)
  330. {
  331. while(lms--)
  332. {
  333.   Delayus(1000);        //延时1ms
  334.     }
  335. }

复制代码

*滑块验证:
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

QQ|手机版|MCU资讯论坛 ( 京ICP备18035221号-2 )|网站地图

GMT+8, 2024-12-23 12:09 , Processed in 0.063158 second(s), 11 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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