切换到宽版
  • 831阅读
  • 5回复

[ARM]STM32 ARDUINO HALMX 应用集锦(五):STM32RTC 时钟和闹铃 [复制链接]

上一主题 下一主题
离线huaweiwx
 

发帖
1273
M币
3346
专家
57
粉丝
240
    在 ARDUINO官方支持的STM32包:STM32_core中,包含了一个实时时钟库STM32RTC,但该软件库并未支持stm32全系列,而且不支持备份寄存器读写,不支持开机的日期时间自动设置,因此每次重启均需重新设定RTC,而且星期也需要自己来设定,在使用中既麻烦也使库的实用性大打折扣,因此我决定在此基础上来完善该库,现已完成(下载:https://github.com/huaweiwx/STM32GENERIC):
1 增加 备份寄存器读写功能;
2 时钟初始化后再备份寄存器设置一个标志(这和CUBMX生产代码类似),以后在时钟初始化时检查该标志并决定是否重置时间和日期;
3 增加了星期计算,并在更新year/mouth/day中任何一个时,自动更新库内部星期变量;
4 增加了一个通过命令行输入并设置时间的例子(样板),方便应用者学习和移植使用;


实时钟简述: STM32 内部集成了一个实时时钟芯片,并可以选择不同的时钟源:
  外部低速晶振LSE
  外部告诉晶振 HSE
  内部高速RC振荡器 HSI
  内部低速RC振荡器 LSI

选择外部低速时钟是走时最精确的,并且当我们的板子上装有后备电池时,即使mcu掉电关机,时钟部分仍然能够工作,并能够按我们的要求来产生中断唤醒机器(配合软件);

显然,如果我们不想每次开机都要重新设置时钟,需要选择RTC时钟源为LSE,当前前提是硬件上有外部低速晶振(部分早期F1 RTC对低速晶振要求较高不易起振);

使用:以cmdLine_timerSet.ino为例:

  1. /**
  2.   ******************************************************************************
  3.     @file    cmdLine_timerSet.ino
  4.     [url=u.php?uid=2115719]@author[/url]    huaweiwx@sina.com
  5.     [url=u.php?uid=2263295]@date[/url]      2018/6/8
  6.     [url=u.php?uid=650075]@brief[/url]     Simple RTC set time example.
  7.     command:
  8.         set data & time YYMMDDHHMMSS: set yy mm dd hh mm ss
  9.                      or   MMDDHHMMSS: set mm dd hh mm ss
  10.                      or       HHMMSS: set mm hh mm ss
  11.                            ............
  12.         set data              YYMMDD: setymd yy mm dd
  13.                      or         MMDD: setymd mm dd
  14.                      or           DD: setymd dd
  15.         set time              HHMMSS: sethms hh mm ss
  16.                      or         MMSS: sethms mm ss
  17.                      or           SS: sethms ss
  18.         timer show on               : on
  19.         timer show off              : off            
  20.   ******************************************************************************
  21. */
  22. #include <cmdline.h>
  23. char cmdline[256];
  24. int ptr = 0;
  25. #include <Streaming.h>
  26. #include <LED.h>
  27. #include <STM32RTC.h>
  28. STM32RTC& rtc = STM32RTC::getInstance(); /* Get the rtc object */
  29. /* Change these values to set the current initial time */
  30. byte seconds = 0;
  31. byte minutes = 0;
  32. byte hours = 18;
  33. /* Change these values to set the current initial date */
  34. /* Menday  19th June 2018 */
  35. byte day = 19;
  36. byte month = 5;
  37. byte year = 18;
  38. void setup()
  39. {
  40.   Serial.begin(115200);
  41.   Led.Init();
  42.   Led.flash(10, 320, 5);
  43.   Serial.println("\n****************************************");
  44.   Serial.println(  "*          RTC timer demo              *");
  45.   Serial.println(  "*  Type help Display list of commands  *");
  46.   Serial.println(  "****************************************");
  47.   //select clock souce:  RTC_LSI_CLOCK/RTC_LSE_CLOCK/RTC_HSE_CLOCK/RTC_HSI_CLOCK, default RTC_LSI_CLOCK
  48.   //default STM32RTC::RTC_LSI_CLOCK
  49.   rtc.setClockSource(STM32RTC::RTC_LSE_CLOCK);
  50.   rtc.begin(); // initialize RTC default 24H format
  51.   //check bkreg and set the data/time
  52.   if (rtc.getRegister(RTC_BKP_DR1) != 0x32F2) {
  53.     setdata();
  54.     settime();
  55.     rtc.setRegister(RTC_BKP_DR1, 0x32F2);
  56.   } else {
  57.     //   settime()
  58. #ifdef STM32F1
  59.     setdata();
  60. #endif
  61.   }
  62.   // you can use also
  63.   //rtc.setTime(hours, minutes, seconds);
  64.   //rtc.setDate(day, month, year);
  65. }
  66. boolean stringComplete = false;  // whether the string is complete
  67. boolean timerShowOn = true;
  68. void loop()
  69. {
  70.   //cmdline process
  71.   int nStatus;
  72.   if (stringComplete) {
  73.     if (cmdline[0] > ' ') {
  74.       nStatus = CmdLineProcess(cmdline);
  75.       if (nStatus == CMDLINE_BAD_CMD)
  76.         Serial.println(F("Bad cmd!"));
  77.       else if (nStatus == CMDLINE_TOO_MANY_ARGS)
  78.         Serial.println(F("Too many args!"));
  79.       else if (nStatus != 0)
  80.         Serial << "Cmd rtn err_code:" << nStatus;
  81.     }
  82.     ptr = 0;
  83.     stringComplete = false;
  84.   }
  85.   if (timerShowOn) {
  86.     showDataTime();
  87.   }
  88.   //receiver uart input
  89. #if defined(STM32GENERIC)
  90.   serialEvent();
  91. #endif
  92.   delay(1000);
  93. }
  94. void serialEvent() {
  95.   while (Serial.available()) {
  96.     char inChar = (char)Serial.read();
  97.     cmdline[ptr++] = inChar;
  98.     cmdline[ptr] = '\0';
  99.     if ((inChar == '\n') || (inChar == '\r')) {
  100.       stringComplete = true;
  101.     }
  102.   }
  103. }
  104. const char*  weekStr[7] = {
  105.   "Monday",
  106.   "Tuesday",
  107.   "Wednesday",
  108.   "Thursday",
  109.   "Friday",
  110.   "Saturday",
  111.   "Sunday",
  112. };
  113. void showDataTime(void)
  114. {
  115.   Serial <<  rtc.getHours() << ":"  << rtc.getMinutes() << ":" << rtc.getSeconds() ;
  116.   Serial << "  " << weekStr[ rtc.getWeekDay() - 1] << "  " <<  rtc.getDay() << "/" <<  rtc.getMonth()   << "/" << rtc.getYear() << "\n";
  117. }
  118. //  set the time
  119. void settime(void) {
  120.   rtc.setHours(hours);
  121.   rtc.setMinutes(minutes);
  122.   rtc.setSeconds(seconds);
  123. }
  124. // Set the date
  125. void setdata(void) {
  126.   rtc.setDay(day);
  127.   rtc.setMonth(month);
  128.   rtc.setYear(year);
  129. }
  130. /*example: set 18 6 9 22 18 54*/
  131. int Cmd_set(int argc, char *argv[])
  132. {
  133.   switch (argc) {
  134.     case 7:
  135.       year = atoi(argv[1]);
  136.       month = atoi(argv[2]);
  137.       day = atoi(argv[3]);
  138.       hours = atoi(argv[4]);
  139.       minutes = atoi(argv[5]);
  140.       seconds = atoi(argv[6]);
  141.       break;
  142.     case 6:
  143.       month = atoi(argv[1]);
  144.       day = atoi(argv[2]);
  145.       hours = atoi(argv[3]);
  146.       minutes = atoi(argv[4]);
  147.       seconds = atoi(argv[5]);
  148.       break;
  149.     case 5:
  150.       day = atoi(argv[1]);
  151.       hours = atoi(argv[2]);
  152.       minutes = atoi(argv[3]);
  153.       seconds = atoi(argv[4]);
  154.       break;
  155.     case 4:
  156.       hours = atoi(argv[1]);
  157.       minutes = atoi(argv[2]);
  158.       seconds = atoi(argv[3]);
  159.       break;
  160.     case 3:
  161.       minutes = atoi(argv[1]);
  162.       seconds = atoi(argv[2]);
  163.       break;
  164.     case 2:
  165.       seconds = atoi(argv[1]);
  166.       break;
  167.     default:
  168.       return 0;
  169.   }
  170.   rtc.setYear(year);
  171.   rtc.setMonth(month);
  172.   rtc.setDay(day);
  173.   rtc.setHours(hours);
  174.   rtc.setMinutes(minutes);
  175.   rtc.setSeconds(seconds);
  176.   return 0;
  177. }
  178. /*example: on9*/
  179. int Cmd_timeOn(int argc, char *argv[])
  180. {
  181.   timerShowOn = true;
  182.   return 0;
  183. }
  184. /*example: on9*/
  185. int Cmd_timeOff(int argc, char *argv[])
  186. {
  187.   timerShowOn = false;
  188.   return 0;
  189. }
  190. /*example: setymd 18 6 9*/
  191. int Cmd_ymd(int argc, char *argv[])
  192. {
  193.   switch (argc) {
  194.     case 4:
  195.       year = atoi(argv[1]);
  196.       month = atoi(argv[2]);
  197.       day = atoi(argv[3]);
  198.       break;
  199.     case 3:
  200.       month = atoi(argv[1]);
  201.       day = atoi(argv[2]);
  202.       break;
  203.     case 2:
  204.       day = atoi(argv[1]);
  205.       break;
  206.     default:
  207.       return 0;
  208.   }
  209.   rtc.setYear(year);
  210.   rtc.setMonth(month);
  211.   rtc.setDay(day);
  212.   return 0;
  213. }
  214. /*example: sethms 22 18 54*/
  215. int Cmd_hms(int argc, char *argv[])
  216. {
  217.   switch (argc) {
  218.     case 4:
  219.       hours = atoi(argv[1]);
  220.       minutes = atoi(argv[2]);
  221.       seconds = atoi(argv[3]);
  222.       break;
  223.     case 3:
  224.       minutes = atoi(argv[1]);
  225.       seconds = atoi(argv[2]);
  226.       break;
  227.     case 2:
  228.       seconds = atoi(argv[1]);
  229.       break;
  230.     default:
  231.       return 0;
  232.   }
  233.   rtc.setHours(hours);
  234.   rtc.setMinutes(minutes);
  235.   rtc.setSeconds(seconds);
  236.   return 0;
  237. }
  238. //*****************************************************************************
  239. // This function implements the "help" command.  It prints a simple list of the
  240. // available commands with a brief description.
  241. //*****************************************************************************
  242. int Cmd_help(int argc, char *argv[])
  243. {
  244.   tCmdLineEntry *pEntry;
  245.   pEntry = &g_sCmdTable[0];
  246.   Serial.println(F("\r\nAvailable commands:\r\n------------------\n"));
  247.   while (pEntry->pcCmd)
  248.   {
  249.     Serial.print( pEntry->pcCmd);
  250.     Serial.println(pEntry->pcHelp);
  251.     pEntry++;
  252.   }
  253.   return (0);
  254. }
  255. tCmdLineEntry g_sCmdTable[] =
  256. {
  257.   { "help",      Cmd_help,      "  : Display list of commands"} ,
  258.   { "?",         Cmd_help,   "     : alias for help"} ,
  259.   { "on",        Cmd_timeOn,  "    : timer show on"} ,
  260.   { "off",       Cmd_timeOff,  "   : timer show off"} ,
  261.   { "set",       Cmd_set,      "   : set  yy mo dd hh mi ss"} ,/* set 18 6 9 22 18 54 */
  262.   { "setymd",       Cmd_ymd,      ": time  yy mm dd"} ,         /* set 18 6 9*/
  263.   { "sethms",       Cmd_hms,      ": time  hh mm ss"} ,         /* set 22 18 54*/
  264.   // { "alarm",      Cmd_alarm,    "  :  set alarmtime hms"},
  265.   {  0, 0, 0 }
  266. };


说明:
line 23~25 装入命令行处理库,其中cmdline[256]是串口命令行输入数据的缓冲区,ptr是当前输入字符位置;
line 30 31 装入RTC库,建立一个示例 rtc;
line 34~42  初始化年月日时分秒全局变量,并作为首次使用时初始化RTC用;使用者编译上传时可以修改这些值;
line 38 选择 LSE为RTC时钟源,注释掉该行,就可以使用库缺省时钟源LSI,也可使用HSI或HSE;只有选择了LSE并有后备电池对RTC供电,才能在系统断电时RTC仍能正确运行并且后备寄存器值得以保持;
line 63~72 检查RTC_BKP_DR1标志,如未设定,设定RTC时间和日期,否则不设置,采用RTC内部时钟日期值继续计数,这里有个例外就是F1系列,因为F1的RTC只是个简单计数器,不像其他系列是个完整的日期时间计数,不保持日期值,应此F1虽不需重置时间,但仍需要重置日期;对F1 用户可以使用其他备份寄存器 RTC_BKP_DRx,来保存日期(这部分留给用户自行处理,已后版本我可能会增加);在我的例子里,RTC标志使用了RTC_BKP_DR1,以及已设定标志值为0x32F2;
line 79 一个是否显示当前时间的开关值变量 timerShowOn,可以通过命令行输入命令on或off来开关是否显示;
line 81~107 是标准的cmdline 库用法,其中99~101 根据timerShowOn 决定是否显示当前时间;
line 151~294 建立了如下命令的处理回调函数(由命令行处理函数负责调用):
   help命令及别名?: 简单显示支持的所有命令;
   on 或 off :串口显示或不显示时间开关;
   set:一次性设定年月日时分秒共六个;这命令可以输入6~1个由空格隔开的不同个数的数值(年 月 日 时 分 秒),这样当需要重设月份时,可以仅输入“月 日 时 分 秒” 5个,同理仅时或分钟要修改可以是“时 分 秒”或“时 分 秒”
   setymd 和 sethms:分别是单独设定年月日或时分秒,用法和set一样,可以只输后面两个或一个值;

我增加的库函数:
uint32_t getRegister(uint32_t index);/*读后备寄存器*/
void setRegister(uint32_t index, uint32_t value);/*写后备寄存器*/

uint8_t calculateWeekDay(uint8_t y,uint8_t m, uint8_t d); /*计算星期几*/

其它如闹钟等应用示例文件和用法, 详见:http://arduino.cc/en/Reference/RTC
本帖提到的人: @brief @author @date
本帖最近打赏记录:共10条打赏M币+142专家+3
weizaisifang 专家 +1 優秀文章 06-18
weizaisifang M币 +13 優秀文章 06-18
snowman007 专家 +1 厉害,支持大佬 06-14
snowman007 M币 +20 厉害,支持大佬 06-14
lyy-cy M币 +30 優秀文章 06-13
2545889167 专家 +1 很厉害,支持大佬 06-12
2545889167 M币 +20 很厉害,支持大佬 06-12
数码家园 M币 +20 謝謝分享 06-12
cushion M币 +13 謝謝分享 06-12
發騷友 M币 +26 以資鼓勵 06-12
离线chenghelin

发帖
632
M币
1174
专家
2
粉丝
13
只看该作者 1楼 发表于: 06-12
多谢,收藏学习
本帖最近打赏记录:共1条打赏M币+9
huaweiwx M币 +9 - 06-15
离线ynymwtb

发帖
653
M币
3573
专家
10
粉丝
31
只看该作者 2楼 发表于: 06-12
越来越难了,顶一下
本帖最近打赏记录:共1条打赏M币+9
huaweiwx M币 +9 - 06-15
离线数码家园

发帖
5172
M币
15927
专家
31
粉丝
513
只看该作者 3楼 发表于: 06-12
谢谢分享!学习学习。
本帖最近打赏记录:共1条打赏M币+20
huaweiwx M币 +20 - 06-15
离线2545889167

发帖
12293
M币
13170
专家
280
粉丝
4657
只看该作者 4楼 发表于: 06-12
很厉害,支持大佬
本帖最近打赏记录:共1条打赏M币+20
huaweiwx M币 +20 - 06-15
离线xktx09

发帖
4929
M币
3467
专家
2
粉丝
125
只看该作者 5楼 发表于: 06-15
看不懂也帮顶