STM32F103RC W5500 NTP获取网络时间实现
程序员文章站
2024-02-23 20:28:16
...
NTP 是网络时间协议,将获取到的网络时间同步到本地,是本地时间与网络同步。
一般来说,STM32通过W5500从NTP服务器获取到之后,会存同步到DS1302时钟芯片中,再读取DS1302时间在应用中使用。
DS1302的时间设置和读取,可以参考 《STM32F10x读取DS1302的时间,通过USART显示在串口调试助手上》。
NTP协议是基于UDP基础上封装的协议,NTP报文格式 可以参考 《NTP报文格式》。
STM32驱动W5500,使用UDP来实现获取NTP网络时间的代码实现:NTP服务器IP选取 202.112.10.60,端口固定为123.
ntp.c
#include "types.h"
#include "ntp.h"
#include "socket.h"
#include "w5500.h"
#include <string.h>
#include <stdio.h>
uint8 NTP_SERVER_IP[4] = {202, 112, 10, 60};
void func_analysis_ntp_back_msg(uint8* buf, uint16 idx, TSTAMP *tstmp, DATETIME *datetime)
{
TSTAMP seconds = 0;
uint8 i =0 ,zone = TIMEZONE8;
for (i = 0; i < 4; i++)
{
seconds = (seconds << 8) | buf[idx + i];
}
switch (zone)
{
case 0:
seconds -= 12*3600;
break;
case 1:
seconds -= 11*3600;
break;
case 2:
seconds -= 10*3600;
break;
case 3:
seconds -= (9*3600+30*60);
break;
case 4:
seconds -= 9*3600;
break;
case 5:
case 6:
seconds -= 8*3600;
break;
case 7:
case 8:
seconds -= 7*3600;
break;
case 9:
case 10:
seconds -= 6*3600;
break;
case 11:
case 12:
case 13:
seconds -= 5*3600;
break;
case 14:
seconds -= (4*3600+30*60);
break;
case 15:
case 16:
seconds -= 4*3600;
break;
case 17:
seconds -= (3*3600+30*60);
break;
case 18:
seconds -= 3*3600;
break;
case 19:
seconds -= 2*3600;
break;
case 20:
seconds -= 1*3600;
break;
case 21: //£¿
case 22:
break;
case 23:
case 24:
case 25:
seconds += 1*3600;
break;
case 26:
case 27:
seconds += 2*3600;
break;
case 28:
case 29:
seconds += 3*3600;
break;
case 30:
seconds += (3*3600+30*60);
break;
case 31:
seconds += 4*3600;
break;
case 32:
seconds += (4*3600+30*60);
break;
case 33:
seconds += 5*3600;
break;
case 34:
seconds += (5*3600+30*60);
break;
case 35:
seconds += (5*3600+45*60);
break;
case 36:
seconds += 6*3600;
break;
case 37:
seconds += (6*3600+30*60);
break;
case 38:
seconds += 7*3600;
break;
case 39:
seconds += 8*3600;
break;
case 40:
seconds += 9*3600;
break;
case 41:
seconds += (9*3600+30*60);
break;
case 42:
seconds += 10*3600;
break;
case 43:
seconds += (10*3600+30*60);
break;
case 44:
seconds += 11*3600;
break;
case 45:
seconds += (11*3600+30*60);
break;
case 46:
seconds += 12*3600;
break;
case 47:
seconds += (12*3600+45*60);
break;
case 48:
seconds += 13*3600;
break;
case 49:
seconds += 14*3600;
break;
}
*tstmp = seconds;
//calculation for date
calc_date_time(seconds, datetime);
}
void calc_date_time(TSTAMP seconds, DATETIME *datetime)
{
uint8 yf = 0;
uint32 p_year_total_sec;
uint32 r_year_total_sec;
TSTAMP n=0, d=0, total_d=0, rz=0;
uint16 y=0, r=0, yr=0;
signed long long yd=0;
n = seconds;
total_d = seconds/(SECS_PERDAY);
d=0;
p_year_total_sec=SECS_PERDAY*365;
r_year_total_sec=SECS_PERDAY*366;
while(n>=p_year_total_sec)
{
if((EPOCH+r)%400==0 || ((EPOCH+r)%100!=0 && (EPOCH+r)%4==0))
{
n = n -(r_year_total_sec);
d = d + 366;
}
else
{
n = n - (p_year_total_sec);
d = d + 365;
}
r+=1;
y+=1;
}
y += EPOCH;
datetime->yy = y;
yd=0;
yd = total_d - d;
yf=1;
while(yd>=28)
{
if(yf==1 || yf==3 || yf==5 || yf==7 || yf==8 || yf==10 || yf==12)
{
yd -= 31;
if(yd<0)break;
rz += 31;
}
if (yf==2)
{
if (y%400==0 || (y%100!=0 && y%4==0))
{
yd -= 29;
if(yd<0)break;
rz += 29;
}
else
{
yd -= 28;
if(yd<0)break;
rz += 28;
}
}
if(yf==4 || yf==6 || yf==9 || yf==11 )
{
yd -= 30;
if(yd<0)break;
rz += 30;
}
yf += 1;
}
datetime->mo = yf;
yr = total_d-d-rz;
yr += 1;
datetime->dd = yr;
seconds = seconds%SECS_PERDAY;
datetime->hh = seconds/3600;
datetime->mm = (seconds%3600)/60;
datetime->ss = (seconds%3600)%60;
}
/*
TSTAMP change_datetime_to_seconds(void)
{
TSTAMP seconds=0;
uint32 total_day=0;
uint16 i=0,run_year_cnt=0,l=0;
l = dt_ntp.yy;
for(i=EPOCH;i<l;i++)
{
if((i%400==0) || ((i%100!=0) && (i%4==0)))
{
run_year_cnt += 1;
}
}
total_day=(l-EPOCH-run_year_cnt)*365+run_year_cnt*366;
for(i=1;i<=dt_ntp.mm;i++)
{
if(i==5 || i==7 || i==10 || i==12)
{
total_day += 30;
}
if (i==3)
{
if (l%400==0 && l%100!=0 && l%4==0)
{
total_day += 29;
}
else
{
total_day += 28;
}
}
if(i==2 || i==4 || i==6 || i==8 || i==9 || i==11)
{
total_day += 31;
}
}
seconds = (total_day+dt_ntp.dd-1)*24*3600;
seconds += dt_ntp.ss;//seconds
seconds += dt_ntp.mm*60;//minute
seconds += dt_ntp.hh*3600;//hour
return seconds;
}
*/
uint8 func_pack_ntp_message(uint8 *ntp_server_ip, uint8 *ntp_message)
{
uint8 flag;
NTPFORMAT ntpfmt;
ntpfmt.dstaddr[0] = ntp_server_ip[0];
ntpfmt.dstaddr[1] = ntp_server_ip[1];
ntpfmt.dstaddr[2] = ntp_server_ip[2];
ntpfmt.dstaddr[3] = ntp_server_ip[3];
ntpfmt.leap = 11; /* leap indicator */
ntpfmt.version = 3; /* version number */
ntpfmt.mode = 3; /* mode */
ntpfmt.stratum = 0; /* stratum */
ntpfmt.poll = 0; /* poll interval */
ntpfmt.precision = 0; /* precision */
ntpfmt.rootdelay = 0; /* root delay */
ntpfmt.rootdisp = 0; /* root dispersion */
ntpfmt.refid = 0; /* reference ID */
ntpfmt.reftime = 0; /* reference time */
ntpfmt.org = 0; /* origin timestamp */
ntpfmt.rec = 0; /* receive timestamp */
ntpfmt.xmt = 1; /* transmit timestamp */
flag = (ntpfmt.leap << 6) + (ntpfmt.version <<3 ) + ntpfmt.mode; //one byte Flag
ntp_message[0] = flag;
return 0;
}
uint8 func_get_ntp_time(uint8 sock, TSTAMP *tstamp, DATETIME *datetime, uint16 timeout_ms)
{
uint16 cnt_timeout = 0;
uint8 ntp_message[48] = {0,};
uint8 ntp_back_msg[256] = {0,};
uint8 ntp_s_ip[4] = {0,};
uint16 ntp_s_port, len;
//socket init
if(getSn_SR(sock) == SOCK_CLOSED)
{
socket(sock, Sn_MR_UDP, NTP_PORT, 0);
}
//pack NTP message
func_pack_ntp_message(NTP_SERVER_IP, ntp_message);
//send ntp message
sendto(sock, ntp_message, sizeof(ntp_message), NTP_SERVER_IP, NTP_PORT);
//wait for NTP message back
for(;;)
{
if(getSn_IR(sock) & Sn_IR_RECV)
{
setSn_IR(sock, Sn_IR_RECV);
}
if ((len = getSn_RX_RSR(sock)) > 0)
{
len = recvfrom(sock, ntp_back_msg, sizeof(ntp_back_msg), ntp_s_ip, &ntp_s_port);
if(len >= 48 && ntp_s_port == NTP_PORT)
{
//analysis
func_analysis_ntp_back_msg(ntp_back_msg, 40, tstamp, datetime);
break;
}
}
cnt_timeout ++;
if(cnt_timeout > timeout_ms)
{
close(sock);
return 1;
}
delay_ms(1);
}
//close socket
close(sock);
return 0;
}
ntp.h
#ifndef __NTP_H__
#define __NTP_H__
#include "types.h"
#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif
#define NTP_PORT 123
#define EPOCH 1900 // NTP start year
#define TIMEZONE0 22
#define TIMEZONE8 39
typedef unsigned long long TSTAMP;
typedef signed char s_char;
typedef unsigned int tdist;
typedef struct _ntpformat
{
uint8 dstaddr[4]; /* destination (local) address */
char version; /* version number */
char leap; /* leap indicator */
char mode; /* mode */
char stratum; /* stratum */
char poll; /* poll interval */
s_char precision; /* precision */
tdist rootdelay; /* root delay */
tdist rootdisp; /* root dispersion */
tdist refid; /* reference ID */
TSTAMP reftime; /* reference time */
TSTAMP org; /* origin timestamp */
TSTAMP rec; /* receive timestamp */
TSTAMP xmt; /* transmit timestamp */
} NTPFORMAT;
typedef struct _datetime
{
uint16 yy;
uint8 mo;
uint8 dd;
uint8 hh;
uint8 mm;
uint8 ss;
} DATETIME;
#define SECS_PERDAY 86400UL // seconds in a day = 60*60*24
#define UTC_ADJ_HRS 9 // SEOUL : GMT+9
void calc_date_time(TSTAMP seconds, DATETIME *datetime);
uint8 func_pack_ntp_message(uint8 *ntp_server_ip, uint8 *ntp_message);
void func_analysis_ntp_back_msg(uint8* buf, uint16 idx, TSTAMP *tstmp, DATETIME *datetime);
uint8 func_get_ntp_time(uint8 sock, TSTAMP *tstamp, DATETIME *datetime, uint16 timeout_ms);
#endif
测试的主函数代码:
#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif
#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif
#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif
#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif
#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif
#include "w5500.h"
#include "socket.h"
#include "w5500_conf.h"
#include "dhcp.h"
#include "ntp.h"
int main(void)
{
DHCP_Get dhcp_get;
TSTAMP tstmp;
DATETIME dt;
uint8 buffer[1024];
u8 mac[6]={0, 0, 0, 0, 0, 0};
init_led();
init_hardware_usart2_dma(9600);
init_system_spi();
func_w5500_reset();
getMacByLockCode(mac);
setSHAR(mac);
/*DHCP TEST*/
sysinit(txsize, rxsize);
setRTR(2000);
setRCR(3);
//DHCP
if(func_dhcp_get_ip_sub_gw(0, mac, &dhcp_get, 500) == 0)
{
func_usart2_dma_send_bytes(dhcp_get.lip, 4);
setSUBR(dhcp_get.dns);
setGAR(dhcp_get.gw);
setSIPR(dhcp_get.lip);
}
for(;;)
{
delay_ms(500);
if(func_get_ntp_time(1, &tstmp, &dt, 2000) == 0)
{
int len;
memset(buffer, 0, sizeof(buffer));
len = sprintf((char*)buffer, "%04d-%02d-%02d %02d:%02d:%02d", dt.yy, dt.mo, dt.dd, dt.hh, dt.mm, dt.ss);
func_usart2_dma_send_bytes(buffer, len);
}
func_led1_on();
delay_ms(460);
func_led1_off();
delay_ms(560);
}
}
通过串口将获取的网络时间打印出来,测试的效果:
可以看出测试的时间和windows的时间,相差不大。