欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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);
		
	}
}

通过串口将获取的网络时间打印出来,测试的效果:

STM32F103RC W5500 NTP获取网络时间实现

可以看出测试的时间和windows的时间,相差不大。