rtc.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /************************************RTC.c文件***************************/
  2. #include "includes.h"
  3. #include "delay.h"
  4. #include "rtc.h"
  5. _calendar_obj calendar;//时钟结构体
  6. u32 timecount=0;
  7. static void RTC_NVIC_Config(void)
  8. {
  9. NVIC_InitTypeDef NVIC_InitStructure;
  10. NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断
  11. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位
  12. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占优先级0位,从优先级4位
  13. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该通道中断
  14. NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  15. }
  16. //实时时钟配置
  17. //初始化RTC时钟,同时检测时钟是否工作正常
  18. //BKP->DR1用于保存是否第一次配置的设置
  19. //返回0:正常
  20. //其他:错误代码
  21. u8 RTC_Init(void)
  22. {
  23. //检查是不是第一次配置时钟
  24. u8 temp=0;
  25. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  26. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
  27. if (BKP_ReadBackupRegister(BKP_DR1) != 0x5051) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
  28. {
  29. BKP_DeInit(); //复位备份区域
  30. RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
  31. while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
  32. {
  33. temp++;
  34. delay_ms(10);
  35. }
  36. if(temp>=250)return 1; //初始化时钟失败,晶振有问题
  37. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
  38. RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
  39. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  40. RTC_WaitForSynchro(); //等待RTC寄存器同步
  41. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
  42. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  43. RTC_EnterConfigMode(); // 允许配置
  44. RTC_SetPrescaler(32767); //设置RTC预分频的值,计算方式32768/(32767+1) = 1Hz 周期刚好是1秒。
  45. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  46. RTC_Set(2021,4,26,23,16,00); //设置时间
  47. RTC_ExitConfigMode(); //退出配置模式
  48. BKP_WriteBackupRegister(BKP_DR1, 0X5051); //向指定的后备寄存器中写入用户程序数据
  49. }
  50. else //系统继续计时
  51. {
  52. RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
  53. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
  54. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  55. }
  56. RTC_NVIC_Config(); //RCT中断分组设置
  57. RTC_Get(); //更新时间
  58. return 0; //ok
  59. }
  60. //RTC时钟中断
  61. //每秒触发一次
  62. //extern u16 tcnt;
  63. void RTC_IRQHandler(void)
  64. {
  65. if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
  66. {
  67. RTC_Get();//更新时间
  68. }
  69. if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
  70. {
  71. RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
  72. RTC_Get(); //更新时间
  73. printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间
  74. }
  75. RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清闹钟中断
  76. RTC_WaitForLastTask();
  77. }
  78. //判断是否是闰年函数
  79. //月份 1 2 3 4 5 6 7 8 9 10 11 12
  80. //闰年 31 29 31 30 31 30 31 31 30 31 30 31
  81. //非闰年 31 28 31 30 31 30 31 31 30 31 30 31
  82. //输入:年份
  83. //输出:该年份是不是闰年.1,是.0,不是
  84. u8 Is_Leap_Year(u16 year)
  85. {
  86. if(year%4==0) //必须能被4整除
  87. {
  88. if(year%100==0)
  89. {
  90. if(year%400==0)return 1;//如果以00结尾,还要能被400整除
  91. else return 0;
  92. }else return 1;
  93. }else return 0;
  94. }
  95. //设置时钟
  96. //把输入的时钟转换为秒钟
  97. //以1970年1月1日为基准
  98. //1970~2099年为合法年份
  99. //返回值:0,成功;其他:错误代码.
  100. //月份数据表
  101. u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
  102. //平年的月份日期表
  103. const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  104. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
  105. {
  106. u16 t;
  107. u32 seccount=0;
  108. if(syear<1970||syear>2099)return 1;
  109. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
  110. {
  111. if(Is_Leap_Year(t))seccount+=31622400; //闰年的秒钟数
  112. else seccount+=31536000; //平年的秒钟数
  113. }
  114. smon-=1;
  115. for(t=0;t<smon;t++) //把前面月份的秒钟数相加
  116. {
  117. seccount+=(u32)mon_table[t]*86400; //月份秒钟数相加
  118. if(Is_Leap_Year(syear)&&t==1)seccount+=86400; //闰年2月份增加一天的秒钟数
  119. }
  120. seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
  121. seccount+=(u32)hour*3600; //小时秒钟数
  122. seccount+=(u32)min*60; //分钟秒钟数
  123. seccount+=sec; //最后的秒钟加上去
  124. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  125. PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
  126. RTC_SetCounter(seccount); //设置RTC计数器的值
  127. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  128. return 0;
  129. }
  130. //初始化闹钟
  131. //以1970年1月1日为基准
  132. //1970~2099年为合法年份
  133. //syear,smon,sday,hour,min,sec:闹钟的年月日时分秒
  134. //返回值:0,成功;其他:错误代码.
  135. u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
  136. {
  137. u16 t;
  138. u32 seccount=0;
  139. if(syear<1970||syear>2099)return 1;
  140. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
  141. {
  142. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
  143. else seccount+=31536000; //平年的秒钟数
  144. }
  145. smon-=1;
  146. for(t=0;t<smon;t++) //把前面月份的秒钟数相加
  147. {
  148. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
  149. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
  150. }
  151. seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
  152. seccount+=(u32)hour*3600; //小时秒钟数
  153. seccount+=(u32)min*60; //分钟秒钟数
  154. seccount+=sec; //最后的秒钟加上去
  155. //设置时钟
  156. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  157. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
  158. //上面三步是必须的!
  159. RTC_SetAlarm(seccount);
  160. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  161. return 0;
  162. }
  163. //得到当前的时间
  164. //返回值:0,成功;其他:错误代码.
  165. u8 RTC_Get(void)
  166. {
  167. static u16 daycnt=0;
  168. u32 temp=0;
  169. u16 temp1=0;
  170. timecount=RTC_GetCounter();
  171. temp=timecount/86400; //得到天数(秒钟数对应的)
  172. if(daycnt!=temp) //超过一天了
  173. {
  174. daycnt=temp;
  175. temp1=1970; //从1970年开始
  176. while(temp>=365) //计算出来的天数大于等于一年的天数
  177. {
  178. if(Is_Leap_Year(temp1))//是闰年
  179. {
  180. if(temp>=366)
  181. {
  182. temp-=366;//闰年的秒钟数
  183. }
  184. else
  185. {
  186. temp1++;
  187. break;
  188. }
  189. }
  190. else temp-=365; //平年
  191. temp1++;
  192. }
  193. calendar.w_year=temp1;//得到年份
  194. temp1=0;
  195. while(temp>=28)//超过了一个月
  196. {
  197. if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
  198. {
  199. if(temp>=29) //如果天数超过29天
  200. {
  201. temp-=29; //闰年的秒钟数
  202. }
  203. else break; //小于29天,不到闰年3月,直接跳出。
  204. }
  205. else
  206. {
  207. if(temp>=mon_table[temp1])
  208. {
  209. temp-=mon_table[temp1];//平年
  210. }
  211. else break;
  212. }
  213. temp1++;
  214. }
  215. calendar.w_month=temp1+1; //得到月份
  216. calendar.w_date=temp+1; //得到日期
  217. }
  218. temp=timecount%86400; //得到秒钟数
  219. calendar.hour=temp/3600; //小时
  220. calendar.min=(temp%3600)/60; //分钟
  221. calendar.sec=(temp%3600)%60; //秒钟
  222. calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
  223. /* printf("current time %d-%d-%d %02d:%02d:%02d\r\n",
  224. calendar.w_year,calendar.w_month,calendar.w_date,
  225. calendar.hour, calendar.min, calendar.sec); */
  226. return 0;
  227. }
  228. //获得现在是星期几
  229. //功能描述:输入公历日期得到星期(只允许1901-2099年)
  230. //输入参数:公历年月日
  231. //返回值:星期号
  232. u8 RTC_Get_Week(u16 year,u8 month,u8 day)
  233. {
  234. u16 temp2;
  235. u8 yearH,yearL;
  236. yearH=year/100; yearL=year%100;
  237. // 如果为21世纪,年份数加100
  238. if (yearH>19)yearL+=100;
  239. // 所过闰年数只算1900年之后的
  240. temp2=yearL+yearL/4;
  241. temp2=temp2%7;
  242. temp2=temp2+day+table_week[month-1];
  243. if (yearL%4==0&&month<3)temp2--;
  244. return(temp2%7);
  245. }