在 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为例:
- /**
- ******************************************************************************
- @file cmdLine_timerSet.ino
- [url=u.php?uid=2115719]@author[/url] huaweiwx@sina.com
- [url=u.php?uid=2263295]@date[/url] 2018/6/8
- [url=u.php?uid=650075]@brief[/url] Simple RTC set time example.
- command:
- set data & time YYMMDDHHMMSS: set yy mm dd hh mm ss
- or MMDDHHMMSS: set mm dd hh mm ss
- or HHMMSS: set mm hh mm ss
- ............
- set data YYMMDD: setymd yy mm dd
- or MMDD: setymd mm dd
- or DD: setymd dd
- set time HHMMSS: sethms hh mm ss
- or MMSS: sethms mm ss
- or SS: sethms ss
- timer show on : on
- timer show off : off
- ******************************************************************************
- */
- #include <cmdline.h>
- char cmdline[256];
- int ptr = 0;
- #include <Streaming.h>
- #include <LED.h>
- #include <STM32RTC.h>
- STM32RTC& rtc = STM32RTC::getInstance(); /* Get the rtc object */
- /* Change these values to set the current initial time */
- byte seconds = 0;
- byte minutes = 0;
- byte hours = 18;
- /* Change these values to set the current initial date */
- /* Menday 19th June 2018 */
- byte day = 19;
- byte month = 5;
- byte year = 18;
- void setup()
- {
- Serial.begin(115200);
- Led.Init();
- Led.flash(10, 320, 5);
- Serial.println("\n****************************************");
- Serial.println( "* RTC timer demo *");
- Serial.println( "* Type help Display list of commands *");
- Serial.println( "****************************************");
- //select clock souce: RTC_LSI_CLOCK/RTC_LSE_CLOCK/RTC_HSE_CLOCK/RTC_HSI_CLOCK, default RTC_LSI_CLOCK
- //default STM32RTC::RTC_LSI_CLOCK
- rtc.setClockSource(STM32RTC::RTC_LSE_CLOCK);
- rtc.begin(); // initialize RTC default 24H format
- //check bkreg and set the data/time
- if (rtc.getRegister(RTC_BKP_DR1) != 0x32F2) {
- setdata();
- settime();
- rtc.setRegister(RTC_BKP_DR1, 0x32F2);
- } else {
- // settime()
- #ifdef STM32F1
- setdata();
- #endif
- }
- // you can use also
- //rtc.setTime(hours, minutes, seconds);
- //rtc.setDate(day, month, year);
- }
- boolean stringComplete = false; // whether the string is complete
- boolean timerShowOn = true;
- void loop()
- {
- //cmdline process
- int nStatus;
- if (stringComplete) {
- if (cmdline[0] > ' ') {
- nStatus = CmdLineProcess(cmdline);
- if (nStatus == CMDLINE_BAD_CMD)
- Serial.println(F("Bad cmd!"));
- else if (nStatus == CMDLINE_TOO_MANY_ARGS)
- Serial.println(F("Too many args!"));
- else if (nStatus != 0)
- Serial << "Cmd rtn err_code:" << nStatus;
- }
- ptr = 0;
- stringComplete = false;
- }
- if (timerShowOn) {
- showDataTime();
- }
- //receiver uart input
- #if defined(STM32GENERIC)
- serialEvent();
- #endif
- delay(1000);
- }
- void serialEvent() {
- while (Serial.available()) {
- char inChar = (char)Serial.read();
- cmdline[ptr++] = inChar;
- cmdline[ptr] = '\0';
- if ((inChar == '\n') || (inChar == '\r')) {
- stringComplete = true;
- }
- }
- }
- const char* weekStr[7] = {
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday",
- "Sunday",
- };
- void showDataTime(void)
- {
- Serial << rtc.getHours() << ":" << rtc.getMinutes() << ":" << rtc.getSeconds() ;
- Serial << " " << weekStr[ rtc.getWeekDay() - 1] << " " << rtc.getDay() << "/" << rtc.getMonth() << "/" << rtc.getYear() << "\n";
- }
- // set the time
- void settime(void) {
- rtc.setHours(hours);
- rtc.setMinutes(minutes);
- rtc.setSeconds(seconds);
- }
- // Set the date
- void setdata(void) {
- rtc.setDay(day);
- rtc.setMonth(month);
- rtc.setYear(year);
- }
- /*example: set 18 6 9 22 18 54*/
- int Cmd_set(int argc, char *argv[])
- {
- switch (argc) {
- case 7:
- year = atoi(argv[1]);
- month = atoi(argv[2]);
- day = atoi(argv[3]);
- hours = atoi(argv[4]);
- minutes = atoi(argv[5]);
- seconds = atoi(argv[6]);
- break;
- case 6:
- month = atoi(argv[1]);
- day = atoi(argv[2]);
- hours = atoi(argv[3]);
- minutes = atoi(argv[4]);
- seconds = atoi(argv[5]);
- break;
- case 5:
- day = atoi(argv[1]);
- hours = atoi(argv[2]);
- minutes = atoi(argv[3]);
- seconds = atoi(argv[4]);
- break;
- case 4:
- hours = atoi(argv[1]);
- minutes = atoi(argv[2]);
- seconds = atoi(argv[3]);
- break;
- case 3:
- minutes = atoi(argv[1]);
- seconds = atoi(argv[2]);
- break;
- case 2:
- seconds = atoi(argv[1]);
- break;
- default:
- return 0;
- }
- rtc.setYear(year);
- rtc.setMonth(month);
- rtc.setDay(day);
- rtc.setHours(hours);
- rtc.setMinutes(minutes);
- rtc.setSeconds(seconds);
- return 0;
- }
- /*example: on9*/
- int Cmd_timeOn(int argc, char *argv[])
- {
- timerShowOn = true;
- return 0;
- }
- /*example: on9*/
- int Cmd_timeOff(int argc, char *argv[])
- {
- timerShowOn = false;
- return 0;
- }
- /*example: setymd 18 6 9*/
- int Cmd_ymd(int argc, char *argv[])
- {
- switch (argc) {
- case 4:
- year = atoi(argv[1]);
- month = atoi(argv[2]);
- day = atoi(argv[3]);
- break;
- case 3:
- month = atoi(argv[1]);
- day = atoi(argv[2]);
- break;
- case 2:
- day = atoi(argv[1]);
- break;
- default:
- return 0;
- }
- rtc.setYear(year);
- rtc.setMonth(month);
- rtc.setDay(day);
- return 0;
- }
- /*example: sethms 22 18 54*/
- int Cmd_hms(int argc, char *argv[])
- {
- switch (argc) {
- case 4:
- hours = atoi(argv[1]);
- minutes = atoi(argv[2]);
- seconds = atoi(argv[3]);
- break;
- case 3:
- minutes = atoi(argv[1]);
- seconds = atoi(argv[2]);
- break;
- case 2:
- seconds = atoi(argv[1]);
- break;
- default:
- return 0;
- }
- rtc.setHours(hours);
- rtc.setMinutes(minutes);
- rtc.setSeconds(seconds);
- return 0;
- }
- //*****************************************************************************
- // This function implements the "help" command. It prints a simple list of the
- // available commands with a brief description.
- //*****************************************************************************
- int Cmd_help(int argc, char *argv[])
- {
- tCmdLineEntry *pEntry;
- pEntry = &g_sCmdTable[0];
- Serial.println(F("\r\nAvailable commands:\r\n------------------\n"));
- while (pEntry->pcCmd)
- {
- Serial.print( pEntry->pcCmd);
- Serial.println(pEntry->pcHelp);
- pEntry++;
- }
- return (0);
- }
- tCmdLineEntry g_sCmdTable[] =
- {
- { "help", Cmd_help, " : Display list of commands"} ,
- { "?", Cmd_help, " : alias for help"} ,
- { "on", Cmd_timeOn, " : timer show on"} ,
- { "off", Cmd_timeOff, " : timer show off"} ,
- { "set", Cmd_set, " : set yy mo dd hh mi ss"} ,/* set 18 6 9 22 18 54 */
- { "setymd", Cmd_ymd, ": time yy mm dd"} , /* set 18 6 9*/
- { "sethms", Cmd_hms, ": time hh mm ss"} , /* set 22 18 54*/
- // { "alarm", Cmd_alarm, " : set alarmtime hms"},
- { 0, 0, 0 }
- };
说明:
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