STM32F103RC W5500 NTP获取网络时间实现

12 篇文章 7 订阅
1 篇文章 0 订阅

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的时间,相差不大。

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值