
基于 STM32F7的网络时间同步客户端实现 : m, ^- S0 m- f+ X8 r0 D前言" z" L" V/ A( Q7 w 本文将介绍一个基于STM32F7的网络时间同步客户端例程。在开始介绍程序之前,我们先来了解一下什么是网络时间同步,和NTP协议等相关概念。$ F( w1 `& i6 y$ x 通常在计算机工作前,我们会预先设定好系统的时间。但如果网络中多台计算机(或者处于多个网络中的计算机)需要协同工作的时候, 这些计算机的时间就需要保持同步。如果两个计算机(或者两个网络)的时间不同步,有可能会出现,比如你会收到5分钟之后才会被发出的邮件。所以在这种情况下需要通过某种办法将网络上各设备的时间进行同步。 说到这里,我们也知道了网络时间同步,是指将计算机或者设备的时间与网络上的时间源保持一致。时间源是由网络上的时间服务器提供。本文介绍的是NTP客户端,NTP协议是TCP/IP协议中的应用层协议。NTP的全称是Network Time Protocol, 它是用来同步网络中个计算机的时间的协议。它的目的是在国际互联网上传递统一、标准的时间。具体的实现方案是在网络上指定若干时钟源网站,为用户提供授时服务,并且这些网站间应该能够相互比对,提高准确度。 2 Y0 w. i6 `9 c4 L3 `5 ^- G, D1 q # y8 \1 x( ^6 ] NTP简介 NTP工作原理 s5 F1 }) a+ P1 t/ O6 d 这里借用网上的一个图来简单的说下NTP的工作原理:假设设备A和设备B通过网络连接,它们都有各自的时间系统,需要通过网络进行同步。假设同步前设备A的时间是10:00:00 AM, 设备B的时间是11:00:00AM,相差1个小时。" [6 l+ N6 |6 E0 } 设备B作为NTP时间服务器,设备A需要将自己的时间与设备B同步。+ y; j+ R1 D/ S3 ^. F' r 1. 首先设备A向设备B(服务器)发送一个NTP 报文,该报文带有它离开设备A时的时间戳10:00:00AM(T1) 2. 该NTP报文到达设备B,设备B加上当前自己的时间,也就11:00:01AM(T2) 3. 设备B回复设备A,并加上报文离开时的时间戳11:00:02(T3) 4. 设备A收到设备B的回复,此时,设备A的当地时间是10:00:03(T4) ![]() 通过这个过程,设备A 得到了四个时间戳。假设NTP 报文从设备A 到设备B,和从设备B 回到设备A 的时间是对等的,利用这四个时间戳,设备A 就可以计算出和设备B 的时间差,从而来更新自己的系统时间。, \' f. [& s) f" T5 ~+ O. ` NTP 报文的往返时延 Delay = (T4-T1)-(T3-T2) 设备A 相对设备B 的时间差Offset=((T2-T1)+(T3-T4))/2( A m# [8 f' d# O ' t9 B# `; Y9 B, ?0 j NTP 的工作模式和工作模型& f: W6 m, W# } NTP 有三种工作模式:主/被动模式,客户端/服务器模式和广播模式。主/被动模式下,连接双方可以互相同步,客户端/服务器模式下,只能客户端被服务器同步。广播模式,是一对多的连接,服务器主动发出时间信息,客户由此调整自己的时间。 NTP 采用分层结构进行工作(如下图)。最顶层(0 层)是时间同步网络的基准时间参考源,它位于整个同步网络的顶层。直接连接到0 层时钟源的计算机属于第1 层,从第1 层接受时间的计算机属于第2 层,以此类推。每一层的号码代表了这一层到顶层时钟源的距离。参考时钟源的层数从0 到15,16 表示未同步的设备。下层设备可以同时应用几个上层设备的时间作为参考,也可以引用同层设备的时间作为参考。 ![]() # j. h6 S( z5 W: h8 L6 ?9 K9 R- j NTP 时间戳- R/ c& ]7 t0 f g- O$ E$ ? NTP 以UTC 作为标准时间。UTC 是以原子时秒长为基础的时间计量系统。 NTP 时间戳,是从1900 年1 月1 日0 时0 分0 秒开始所经过的秒数。NTP 的时间戳通过一个64 比特的无符号定点数来表示。 前32 比特表示整数部分,后32 比特表示小数部分,以秒为单位。 ![]() 6 V9 M! @1 D7 I N* M* d ( C W) B& T: b { G, W2 x 所以 136年为一个周期,当到2036年时,数据会溢出。NTP 协议中定义了Era Number 来解决这个问题。从1900 年1 月1 日开始的第一个136 年,Era Number 为0,之后的每136 年加1。但Era Number 的值本身并不能从NTP 的数据中得出,需要从外部采用一些方法来解决。 NTP报文格式 下图所示是NTP报文的格式:: ~* v# R9 {% W5 B( g8 D( D ![]() ( P8 [; Q; ]7 S# x9 M( U. F" A -LI闰秒标识器,占2个比特位。预警最近的分钟里将要被插入或者删除的闰秒秒数。 -版本号,占3个比特位。现在为版本4.- {& M9 p2 C$ I! L8 o* C -模式,占3个比特位。 -Stratum(层) ,占8个比特位。表示当前时钟的层。 -轮询(Poll)间隔,占8个比特位。表示连续信息之间的最大间隔。2 }/ t# |; e$ O' K& S" t# @ -本地时钟精度,占8个比特位。 -原始时间戳Originate Timestamp,64比特。客户端发出NTP报文的时间。; i: d& p6 J9 W i, Z -接收时间戳Receive Timestamp,64比特。服务器端接收到NTP报文的时间。# r8 {" y7 u# H0 e -发送时间戳Transmit Timestamp,64比特。服务器端发送应答的时间。 NTP,SNTP和IEEE1588(PTP)的区别# }6 C* g9 S( i4 d. ^9 S) N 大多数环境中,NTP可以提供1~50ms的可靠时钟源。但对于很多系统来说,并不需要这么高精度的同步,而且完全实现NTP协议太复杂了,所以SNTP(Simple Network Time Protocol)应运而生。SNTP基于NTP协议,和NTP的数据帧格式是一样的,计算时间偏差以及数据包往返时延的方法也一眼。区别就是SNPT没有NTP中复杂的同步算法。SNTP提供的同步时间精度比NTP低。SNTP与NTP协议具有互操作性,SNTP客户端可以与NTP服务器协同工作,NTP客户端也可以接受SNTP服务器发出的授时信息。 IEEE1588协议是专门针对工业应用提出的精确时钟同步协议。它能提供微秒级的时间同步。 与IEEE1588相比,NTP/SNTP授时精度不高的原因在于打时间戳的位置。NTP/SNTP是在应用层写入或者读出时间戳,客户端发起授时请求,先从应用层到物理层,经过网络传输,到达服务器端再从物理层到应用层被读出,这三个阶段都存在不确定性。反之亦然,造成NTP/SNTP的精度不会很高。 而IEEE1588的时间戳的获取位置是在物理层,可以避免报文处理时间的不一致性。STM32的以太网外设支持IEEE1588协议提供高精度的同步,在每个帧的发送或接收时给出64位时间戳,与NTP的时间戳格式相同。关于IEEE1588的实现可以参考ST的另一篇AN3411。 5 @4 |3 a9 g5 }( J G NTP客户端代码实现 本代码基于STM32F7Cube库,通过NTP协议从远程NTP时间服务器上读取时间,并同步本地RTC的实时时钟。程序使用了STM32F746的以太网和RTC两个外设,应用FreeRTOS操作系统,TCP/IP编程部分使用netconn接口函数。 实时时钟RTC' [" {4 y/ q5 R: i8 b: Q STM32F7的实时时钟是一个独立的BCD定时器/计数器。可以提供具有可编程闹钟中断功能的日历时钟/日历。两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时(12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。并且还可以进行夏令时补偿。此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。5 @- X Y9 u' f( x 备份域复位后,所有 RTC 寄存器都会受到保护,以防止可能的非正常写访问。 无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC 便不会停止工作。 在本代码中,首先将RTC初始化到默认的时间2014年2月18号,2点0分0秒。当收到NTP服务器的响应后,利用收到的时间修改RTC的值,完成同步。, _! m; Q( [# h# m, y, o& D3 S 下面是RTC相关的函数: RTC_Init和RTC_CalendarConfig,在main函数中调用., P/ [& g# e$ _! O% ?3 T3 h* p ![]() ![]() RTC_CalendarUpdate函数,更新RTC的值,在NTP进程中调用。1 r& \. ]; M+ z( G ![]() 5 G. x) L* x1 h9 p4 A NTP部分 NTP协议基于UDP进行传输,使用的UDP端口号为123。使用LwIP实现NTP协议,要记得在Lwipopts.h里将LWIP_UDP宏定义打开。+ o V& d' ]7 |/ r$ @ 下面是一个简单的NTP通信流程: ![]() # C4 h7 }' L& i, F' R NTP通信相关代码: 建立NTP Client的服务进程 X& d6 F9 y+ o9 @ ![]() ntp_thread,ntp_request与ntp_process,完成与服务器的通信以及NTP时间格式到可读的年月日格式的转换。& P. J( m% l" D: |) ? : M3 Z# |; w3 K* q, u, ]- L ![]() ![]() ![]() 8 Q9 D4 [; b. Q0 u 编程要点5 ^# R7 z- ~" C. \2 @: Z 前面已经介绍过NTP的时间格式是以1900年1月1日零时为元年,以秒为单位来表示某个时刻的时间的。所以从NTP报文中提取到当前的时间后(NTP格式),还需要转换成可读的时间格式(年月日,时分秒)。C标准函数库里已经提供了对应的转换函数gmtime。但gmtime处理的时间是以1970年1月1日零时为元年,所以在调用gmtime之前先要减去这70年的时间差。另外NTP时间是不考虑时区的,所以还需要程序将本地的时区考虑进去。6 M: k2 d: \- m- r 在编写客户端程序的时候,另外一个需要注意的地方就是发起授时请求的间隔时间。这个间隔时间不宜太短,太频繁的请求会加重服务器的负担,导致不能及时获得响应。这个时间间隔应该根据具体系统的精度来计算最大的请求时间间隔,最小间隔不能小于15秒。在RFC4330-SNTP verion4中更详细的描述了NTP/SNTP 客户端实现时应该注意的事项。 ; v8 O9 z6 c+ Q 测试结果 将该程序在STM32F746 Nucleo板上进行测试。 硬件连接方式:STM32F746Nucelo板连接到PC,PC通过无线上网,将无线连接的属性的高级配置中,设置“允许其他网络用户通过此计算机的Internet连接来连接”。PC的有线网口的IP地址设为:192.168.0.1。STM32测试板通过PC连接到Internet。STM32F746Nucelo板的IP地址设为:192.168.0.8. 网关设为:192.168.0.1. 程序向远程网络时间服务器1.cn.pool.ntp.org发起请求,再根据服务器的反馈修改本地时间。 下面是程序执行的打印信息,和wireshark捕获的通信过程。 同步前的时间是:2014年2月18日,2:00:00 R8 B4 V Q. o9 m& a* J. W8 T/ z. v$ v 同步后的时间是:2016年7月14日,11:18:26 ![]() l/ s0 M6 Z. N( P* ^& E7 q 利用RTC的亚秒字段实现高精度同步# H) I4 F0 C0 K( v* d 前面的例程里,只用到NTP时间中的前32位。只同步到了秒。STM32F7的RTC还可以实现亚秒字段的高精度远程时钟同步。: r# a g8 o2 l% p% P 在读取RTC亚秒字段后(RTC_SSR或RTC_TSSSR),即可计算远程时钟的时间与本地RTC之间的精准时间差。之后,可使用RTC_SHIFTR对RTC的时钟进行零点几秒的“平移”,经过调整后可消除此偏差。 M4 R% x# K% d' ~7 U' ^; o( w RTC_SSR 包含同步预分频器计数器的值。这样,便可计算分辨率低至 1/(PREDIV_S + 1)秒的 RTC 的准确时间。因此,可通过增大同步预分频器的值 (PREDIV_S[14:0]) 来提高分辨率。将 PREDIV_S 设置为 0x7FFF 时,可得到允许的最大分辨率(30.52 μs,时钟频率为 32768 Hz)。4 @! @, K& P" p) n! W . M% K/ @. N. L! C% S* w" |) o e 提高本地时间的精度:RTC频率校准 RTC的时间精度与所用的时钟频率相关,想得到高精度的计时,就需要有精确的时钟频率。RTC的精密数字校准寄存器可以帮助校准外部时钟频率。具体的校准方法可以参看STM32F7的参考手册RTC章节。 - U$ R7 c- q+ I7 b* V# s- r* [ ) v# ]% w3 P8 A( |5 ?7 C" Y1 R ![]() |
STM32硬件结构学习
STM32中BOOT的作用
【STM32F769I-DISC1】开发板刷入Micropython并完成点灯、读取内部温度测试
【STM32F769I-DISC1】测评01:创建STM32cube IDE 工程,点个灯
【STM32F769】创建deepseek本地服务,并实现http请求
汇编浮点库qfplib移植STM32F769I-DISCO开发板与硬件浮点运算性能测试对比
coremark移植到STM32F769I-DISCO开发板的两种方法
【GUI板免费申请活动】【圣诞GUI】使用F746-DISO基于TouchGFX的圣诞树
刘氓兔的杂谈【001】-片上USB 高速PHY
【合集】STM32F7教程、资料大集合
不知道F7的方法是否适用于H7