专注电子技术学习与研究
当前位置:单片机教程网 >> STM32 >> 浏览文章

RTC:能够自动进行网络对时的电子时钟

作者:佚名   来源:互联网   点击数:  更新时间:2014年05月16日   【字体:

  STM32片内包含一个32位的RTC定时器,可以通过外部时钟提供32.768kHz计数时钟,实现按秒的精确计数,并且这个定时器在MCU复位或者断电的时候,只要VBAT管脚能够提供3V的供电,计数器将会继续计数。

 
    网上有很多人埋怨说STM32的RTC时钟不准,说实话这完全是冤枉ST,RTC仅仅是个计数器,计数肯定是准确的,如果说时钟不准,基本上是由于用了劣质的晶振导致的。下图这是Rainbow用到的RTC晶振:
    考虑到很多铁壳RTC晶振会出现不能起振、时钟不准确,我们特意选用了这种高精度的晶振,因此网上提到的时钟不准的问题是可以避免的。
 
    还有一些网友提到说STM32的RTC耗电量很大,一颗纽扣电池只能维持6个月左右。我个人认为能够在断电的情况下维持这么久已经足够了,试想想,如果一个设备半年都不用了,再次启用的时候需要换个纽扣电池也是可以接受的,如果设备正常使用,是不会消耗纽扣电池的电量的。
 
    由于Rainbow具有网络接入能力,因此我们在对RTC类库做封装的时候特地增加了从NTP服务器读取网络时间的功能。通过类库可以读取网络上标准32位的unix格式的时间戳,将这个时间戳写入到STM32的RTC计数器中,就可以实现Rainbow和网络时间同步,之后Rainbow的RTC将按秒进行计数,可以继续维持这个时钟。
 
    由于我们的计数值采用的是符合unix标准的时间戳,所以我们可以轻松通过这个时间戳计算出当前的日期、时间、星期几等,谁叫STM32运算能力有这么强呢?NTP的时间戳是从1900-01-01 00:00:00开始到现在按照秒计数的,而unix的时钟又是从1970年开始的,所以我们将从NTP服务器获取的时间戳减去从1900到1970的秒数,写入到Rainbow的RTC计数器即可。
 
   在软件包的“Projects\RTC”文件夹包含了本文的完整工程,可以直接编译、烧写和调试。程序代码如下:
#include "WProgram.h"
#include "Ethernet.h"
#include "HardwareRTC.h"
#include "LiquidCrystal.h"
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//发送NTP请求的本地端口
uint16_t localPort = 8888;
EthernetUDP Udp;
 
//NTP服务器地址
IPAddress timeServer(132, 163, 4, 101);
 
//定义LCD对象,使用d4-d7四条数据线进行驱动,将rw接地
//我们共用到了6个IO:RS、E、D4-D7,RW接低电平
//本程序接法:
//  RS => PC0
// E => PC2
// D4-D7 => PA0、PA2、PA4、PA6
LiquidCrystal lcd(PC0, PC2, PA0, PA2, PA4, PA6);
 
//RTC对象
HardwareRTC rtc;
 
void setup()
{
  //两行显示,每行16个字符
  lcd.begin(16, 2);
  lcd.print("Waiting...");
 
  uint8_t ethFlag = Ethernet.begin(mac);
  //启用RTC
  rtc.begin();
 
  //将RTC与网络时间进行同步
  if(ethFlag)
  {
    Udp.begin(localPort);
    rtc._udp = &Udp;
    rtc._timeServer = &timeServer;
    rtc.syncNetworkTime();
  }
  //重新初始化,两行显示,每行16个字符
  lcd.begin(16, 2);
}
 
void loop()
{
  while(1)
  {
    lcd.setCursor(0, 0);
    tm t = rtc.getTime();
    lcd.print(t.tm_year);
    lcd.print('-');
    if(t.tm_mon < 9) lcd.print('0');
    lcd.print(t.tm_mon + 1);
    lcd.print('-');
    if(t.tm_mday < 10) lcd.print('0');
    lcd.print(t.tm_mday);
    lcd.setCursor(0, 1);
    if(t.tm_hour < 10) lcd.print('0');
    lcd.print(t.tm_hour);
    lcd.print(':');
    if(t.tm_min < 10) lcd.print('0');
    lcd.print(t.tm_min);
    lcd.print(':');
    if(t.tm_sec < 10) lcd.print('0');
    lcd.print(t.tm_sec);
    //每隔1秒刷新
    delay(1000);
  }
}
 
 
int main(void)
{
  //初始化开发板
  boardInit();
  setup();
  while(1) loop();
}
 
    这个程序使用了1602的LCD作为时钟显示,程序首先通过DHCP获取到网络参数,如果获取成功,则调用HardwareRTC类的syncNetworkTime()方法,从NTP服务器同步时间戳到Rainbow,在之后的程序运行中,每隔1秒对LCD上的时间进行一次更新。下图是程序运行的效果:
关闭窗口

相关文章