rtc.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. // printf("RTC Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间
  69. }
  70. if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
  71. {
  72. RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
  73. RTC_Get(); //更新时间
  74. 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);//输出闹铃时间
  75. }
  76. RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清闹钟中断
  77. RTC_WaitForLastTask();
  78. }
  79. //判断是否是闰年函数
  80. //月份 1 2 3 4 5 6 7 8 9 10 11 12
  81. //闰年 31 29 31 30 31 30 31 31 30 31 30 31
  82. //非闰年 31 28 31 30 31 30 31 31 30 31 30 31
  83. //输入:年份
  84. //输出:该年份是不是闰年.1,是.0,不是
  85. u8 Is_Leap_Year(u16 year)
  86. {
  87. if(year%4==0) //必须能被4整除
  88. {
  89. if(year%100==0)
  90. {
  91. if(year%400==0)return 1;//如果以00结尾,还要能被400整除
  92. else return 0;
  93. }else return 1;
  94. }else return 0;
  95. }
  96. //设置时钟
  97. //把输入的时钟转换为秒钟
  98. //以1970年1月1日为基准
  99. //1970~2099年为合法年份
  100. //返回值:0,成功;其他:错误代码.
  101. //月份数据表
  102. u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
  103. //平年的月份日期表
  104. const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  105. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
  106. {
  107. u16 t;
  108. u32 seccount=0;
  109. if(syear<1970||syear>2099)return 1;
  110. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
  111. {
  112. if(Is_Leap_Year(t))seccount+=31622400; //闰年的秒钟数
  113. else seccount+=31536000; //平年的秒钟数
  114. }
  115. smon-=1;
  116. for(t=0;t<smon;t++) //把前面月份的秒钟数相加
  117. {
  118. seccount+=(u32)mon_table[t]*86400; //月份秒钟数相加
  119. if(Is_Leap_Year(syear)&&t==1)seccount+=86400; //闰年2月份增加一天的秒钟数
  120. }
  121. seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
  122. seccount+=(u32)hour*3600; //小时秒钟数
  123. seccount+=(u32)min*60; //分钟秒钟数
  124. seccount+=sec; //最后的秒钟加上去
  125. // printf("seccount=%d\n",seccount);
  126. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  127. PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
  128. RTC_SetCounter(seccount); //设置RTC计数器的值
  129. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  130. return 0;
  131. }
  132. //设置时钟
  133. //以1970年1月1日为基准
  134. //1970~2099年为合法年份
  135. //参数:时间戳
  136. void RTC_Set_Stamp(uint32_t seccount)
  137. {
  138. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  139. PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
  140. RTC_SetCounter(seccount); //设置RTC计数器的值
  141. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  142. }
  143. //初始化闹钟
  144. //以1970年1月1日为基准
  145. //1970~2099年为合法年份
  146. //syear,smon,sday,hour,min,sec:闹钟的年月日时分秒
  147. //返回值:0,成功;其他:错误代码.
  148. u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
  149. {
  150. u16 t;
  151. u32 seccount=0;
  152. if(syear<1970||syear>2099)return 1;
  153. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
  154. {
  155. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
  156. else seccount+=31536000; //平年的秒钟数
  157. }
  158. smon-=1;
  159. for(t=0;t<smon;t++) //把前面月份的秒钟数相加
  160. {
  161. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
  162. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
  163. }
  164. seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
  165. seccount+=(u32)hour*3600; //小时秒钟数
  166. seccount+=(u32)min*60; //分钟秒钟数
  167. seccount+=sec; //最后的秒钟加上去
  168. //设置时钟
  169. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
  170. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
  171. //上面三步是必须的!
  172. RTC_SetAlarm(seccount);
  173. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
  174. return 0;
  175. }
  176. //得到当前的时间
  177. //返回值:0,成功;其他:错误代码.
  178. u8 RTC_Get(void)
  179. {
  180. static u16 daycnt=0;
  181. u32 temp=0;
  182. u16 temp1=0;
  183. timecount=RTC_GetCounter();
  184. temp=timecount/86400; //得到天数(秒钟数对应的)
  185. if(daycnt!=temp) //超过一天了
  186. {
  187. daycnt=temp;
  188. temp1=1970; //从1970年开始
  189. while(temp>=365) //计算出来的天数大于等于一年的天数
  190. {
  191. if(Is_Leap_Year(temp1))//是闰年
  192. {
  193. if(temp>=366)
  194. {
  195. temp-=366;//闰年的秒钟数
  196. }
  197. else
  198. {
  199. temp1++;
  200. break;
  201. }
  202. }
  203. else temp-=365; //平年
  204. temp1++;
  205. }
  206. calendar.w_year=temp1;//得到年份
  207. temp1=0;
  208. while(temp>=28)//超过了一个月
  209. {
  210. if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
  211. {
  212. if(temp>=29) //如果天数超过29天
  213. {
  214. temp-=29; //闰年的秒钟数
  215. }
  216. else break; //小于29天,不到闰年3月,直接跳出。
  217. }
  218. else
  219. {
  220. if(temp>=mon_table[temp1])
  221. {
  222. temp-=mon_table[temp1];//平年
  223. }
  224. else break;
  225. }
  226. temp1++;
  227. }
  228. calendar.w_month=temp1+1; //得到月份
  229. calendar.w_date=temp+1; //得到日期
  230. }
  231. temp=timecount%86400; //得到秒钟数
  232. calendar.hour=temp/3600; //小时
  233. calendar.min=(temp%3600)/60; //分钟
  234. calendar.sec=(temp%3600)%60; //秒钟
  235. calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
  236. /* printf("current time %d-%d-%d %02d:%02d:%02d\r\n",
  237. calendar.w_year,calendar.w_month,calendar.w_date,
  238. calendar.hour, calendar.min, calendar.sec); */
  239. return 0;
  240. }
  241. //获得现在是星期几
  242. //功能描述:输入公历日期得到星期(只允许1901-2099年)
  243. //输入参数:公历年月日
  244. //返回值:星期号
  245. u8 RTC_Get_Week(u16 year,u8 month,u8 day)
  246. {
  247. u16 temp2;
  248. u8 yearH,yearL;
  249. yearH=year/100; yearL=year%100;
  250. // 如果为21世纪,年份数加100
  251. if (yearH>19)yearL+=100;
  252. // 所过闰年数只算1900年之后的
  253. temp2=yearL+yearL/4;
  254. temp2=temp2%7;
  255. temp2=temp2+day+table_week[month-1];
  256. if (yearL%4==0&&month<3)temp2--;
  257. return(temp2%7);
  258. }