W5100S-EVB-PICO通过SNTP获取网络时间(十一)

前言

        上一章我们用开发板进行ping测试,本章我们用它通过SNTP获取网络时间并在串口显示。

什么是SNTP? 能用来做什么?

        SNTP(Simple Network Time Protocal简单网络时间协议),用于跨广域网或局域网同步时间的协议,具有较高的精确度(几十毫秒),SNTP是NTP协议的简化版;我们可用来给本地设备进行校正时间。

SNTP报文

W5100S-EVB-PICO通过SNTP获取网络时间(十一)_第1张图片

NTP报文格式如上图所示,它的字段含义参考如下:

  • LI 闰秒标识器,占用2个bit
  • VN 版本号,占用3个bits,表示NTP的版本号,现在为3
  • Mode 模式,占用3个bits,表示模式
  • stratum(层),占用8个bits
  • Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
  • Precision 精度,占用8个bits,,表示本地时钟精度
  • Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
  • Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
  • Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源   
  • 参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
  • 原始时间戳,客户端发送的时间,64bits。
  • 接受时间戳,服务端接受到的时间,64bits。
  • 传送时间戳,服务端送出应答的时间,64bits。
  • 认证符(可选项)

连接方式

        连接可上网的路由器LAN口

获取网络时间测试

1.相关代码

        我们打开库文件找到SNTP文件夹了,打开sntp.c文件,本章我们直接调用的是这几个函数:SNTP_init()、SNTP_run(),一个是初始化,一个是运行;其中初始化函数我们依次传入socket端口号、NTP服务器IP地址、时区(直接在sntp.c文件里可知中国对应为39)、数据收发缓存buf;运行函数我们传入对应的时间结构体即可,如下所示:

void SNTP_init(uint8_t s, uint8_t *ntp_server, uint8_t tz, uint8_t *buf)
{
	NTP_SOCKET = s;

	NTPformat.dstaddr[0] = ntp_server[0];
	NTPformat.dstaddr[1] = ntp_server[1];
	NTPformat.dstaddr[2] = ntp_server[2];
	NTPformat.dstaddr[3] = ntp_server[3];

	time_zone = tz;

	data_buf = buf;

	uint8_t Flag;
	NTPformat.leap = 0;           /* leap indicator */
	NTPformat.version = 4;        /* version number */
	NTPformat.mode = 3;           /* mode */
	NTPformat.stratum = 0;        /* stratum */
	NTPformat.poll = 0;           /* poll interval */
	NTPformat.precision = 0;      /* precision */
	NTPformat.rootdelay = 0;      /* root delay */
	NTPformat.rootdisp = 0;       /* root dispersion */
	NTPformat.refid = 0;          /* reference ID */
	NTPformat.reftime = 0;        /* reference time */
	NTPformat.org = 0;            /* origin timestamp */
	NTPformat.rec = 0;            /* receive timestamp */
	NTPformat.xmt = 1;            /* transmit timestamp */

	Flag = (NTPformat.leap<<6)+(NTPformat.version<<3)+NTPformat.mode; //one byte Flag
	memcpy(ntpmessage,(void const*)(&Flag),1);
}

int8_t SNTP_run(datetime *time)
{
	uint16_t RSR_len;
	uint32_t destip = 0;
	uint16_t destport;
	uint16_t startindex = 40; //last 8-byte of data_buf[size is 48 byte] is xmt, so the startindex should be 40

	switch(getSn_SR(NTP_SOCKET))
	{
	case SOCK_UDP:
		if ((RSR_len = getSn_RX_RSR(NTP_SOCKET)) > 0)
		{
			if (RSR_len > MAX_SNTP_BUF_SIZE) RSR_len = MAX_SNTP_BUF_SIZE;	// if Rx data size is lager than TX_RX_MAX_BUF_SIZE
			recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport);
			
			get_seconds_from_ntp_server(data_buf,startindex);
			time->yy = Nowdatetime.yy;
			time->mo = Nowdatetime.mo;
			time->dd = Nowdatetime.dd;
			time->hh = Nowdatetime.hh;
			time->mm = Nowdatetime.mm;
			time->ss = Nowdatetime.ss;

			ntp_retry_cnt=0;
			//close(NTP_SOCKET);

			//return 1;
		}

		if(ntp_retry_cnt<0xFFFF)
		{
			if(ntp_retry_cnt==0)//first send request, no need to wait
			{
				sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
				ntp_retry_cnt++;
			}
			else // send request again? it should wait for a while
			{
				if((ntp_retry_cnt % 0xFFF) == 0) //wait time
				{
					sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
#ifdef _SNTP_DEBUG_
					printf("ntp retry: %d\r\n", ntp_retry_cnt);
#endif
					ntp_retry_cnt++;
					return 1;
				}
			}
		}
		else //ntp retry fail
		{
			ntp_retry_cnt=0;
#ifdef _SNTP_DEBUG_
			printf("ntp retry failed!\r\n");
#endif
			close(NTP_SOCKET);
		}
		break;
	case SOCK_CLOSED:
		socket(NTP_SOCKET,Sn_MR_UDP,ntp_port,0);
		break;
	}
	// Return value
	// 0 - failed / 1 - success
	return 0;
}

        主函数比较简单,我们直接初始化网络配置信息对应参数,以及NTP服务器IP地址;然后初始化sntp后在循环里调用即可,如下所示:

#define SOCKET_ID 0
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
  
void network_init(void);

wiz_NetInfo net_info = {
    .mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},
    .ip = {192, 168, 1, 11},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 1, 1},
    .dns = {8, 8, 8, 8},
    .dhcp = NETINFO_STATIC};
wiz_NetInfo get_info;
static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0,};
static uint8_t sntp_server_ip[4]={202, 112, 10, 60};
static uint16_t timezone = 39;
datetime date;

int main()                                                          
{   
    stdio_init_all();
    sleep_ms(2000);
    network_init();

    SNTP_init(SOCKET_ID, sntp_server_ip, timezone, ethernet_buf);
    
    while(true)
    {
        SNTP_run(&date);
        sleep_ms(1000);
        printf("NOW: %d-%d-%d  %d:%d:%d\r\n",date.yy,date.mo,date.dd,date.hh,date.mm,date.ss);
        
    }
}

void network_init(void)
{
    uint8_t temp;
    wizchip_initialize();
    printf("W5100s udp client example.\r\n");
    sleep_ms(2000);
    wizchip_setnetinfo(&net_info);
    print_network_information(get_info);
    sleep_ms(2000);   
}

2. 测试现象

编译烧录后,打开串行监视器,即可看到在打印的实时时间信息,前两次打印为0是由于socket端口未开启和开启后首次发送请求前这两次状态期间,尚未获得时间数据,因此打印的是初始化赋的0,如下图所示:

W5100S-EVB-PICO通过SNTP获取网络时间(十一)_第2张图片

相关链接

本章相关例程链接icon-default.png?t=N7T8https://gitee.com/wiznet-hk/w5100s-evb-pico-routine.git

你可能感兴趣的:(WIZnet,W5100S-EVB-Pico,C/C++教程,W5100S,以太网芯片,树莓派RP2040,wiznet,嵌入式设备入网方案)