通过sntp同步系统时间

通过sntp同步系统时间

小型物联网设备,很少有接口提供给用户进行数据交互,那么我们设备的系统时间只能够通过获取网络时间后,再更新到本地。那么,就少不了使用sntp协议。
ntp协议:NTP(Network Time Protocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC(通用协调时,可以理解为0时区的时间,领先东八区8个小时),再配合各个时区的偏移调整就能实现精准同步对时功能。
sntp协议:简单网络时间协议(Simple Network Time Protocol),由 NTP 改编而来,主要用来同步因特网中的计算机时钟。在 RFC2030 中定义。主要运用于小型的设备,占用内存小。

注意:NTP时间戳从1900年开始记秒数,而UNIX时间戳从1970年开始记秒数

一.常用时间函数介绍

首先介绍time.h中的常用api;

1.time();

函数原型:time_t time(time_t *t)
介绍:C 库函数 time_t time(time_t *seconds) 返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位。如果 seconds 不为空,则返回值也存储在变量 seconds 中。
返回值:1970-01-01 00:00:00 起至今经过的时间,单位秒

2.ctime()

函数原型:char *ctime(const time_t *timer)
介绍:C 库函数 char *ctime(const time_t *timer) 返回一个表示当地时间的字符串,当地时间是基于参数 timer。
返回的字符串格式如下: Www(星期) Mmm (月)dd(日) hh:mm:ss(时分秒) yyyy(年)
返回值:是基于参数 timer计算,1970-01-01 00:00:00 经过timer时间后的时间字符串

3.localtime()

函数原型:struct tm *localtime(const time_t *timer)
介绍:C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。
返回值:填充tm结构体,表示1970-01-01 00:00:00 经过timer时间后的时间结构体
注意:年份输出需要+1900,月份输出需要+1才能得到准确的utc时间
tm结构体:

struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59                */
   int tm_min;         /* 分,范围从 0 到 59                */
   int tm_hour;        /* 小时,范围从 0 到 23                */
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31                    */
   int tm_mon;         /* 月份,范围从 0 到 11                */
   int tm_year;        /* 自 1900 起的年数                */  
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6                */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365                    */
   int tm_isdst;       /* 夏令时                        */    
};

4.mktime()

函数原型:time_t mktime(struct tm * timeptr)
介绍:C库函数time_t mktime(struct tm * timeptr)把timeptr所指向的结构转换为一个依据本地时区的time_t值。
参数:tm结构体,同上。
返回值:将参数timeptr所指的tm结构数据转换成从公元1970年1月1日0时0分0秒算起至今的UTC时间所经过的秒数。

二.功能实现

下面分析某项目将网络时间更新到本地的流程,平台可能存在差异,实现流程大致相同的。
源码分析
创建一个sntp客户端,目的从网络中获取时间,更新。

static void sntp_client(void)
{
    /** Set this to 1 to allow config of SNTP server(s) by DNS name */
#if (!SNTP_SERVER_DNS)
    struct ip4_addr test_addr;
#endif
    hal_rtc_time_t r_time = {6,6,6,1,1,6,0};
    hal_rtc_status_t ret = HAL_RTC_STATUS_OK;

    //Set RTC to a incorrect time.
    
    //设置默认系统时间
    ret = hal_rtc_set_time(&r_time);
    // LOG_I(sntp_client_main, "[%d]cur_time[%d:%d:%d]", ret, r_time.rtc_hour, r_time.rtc_min, r_time.rtc_sec);

	// 设置sntp服务器ip地址或者DNS(目的地址),从什么服务器获取时间
    /** Set this to 1 to allow config of SNTP server(s) by DNS name */
#if SNTP_SERVER_DNS
    sntp_setservername(0, "1.cn.pool.ntp.org");
    sntp_setservername(1, "1.hk.pool.ntp.org");
#else
    IP4_ADDR(&test_addr, 213, 161, 194, 93);
    sntp_setserver(0, (const ip_addr_t *)(&test_addr));
    IP4_ADDR(&test_addr, 129, 6, 15, 29);
    sntp_setserver(1, (const ip_addr_t *)(&test_addr));
#endif

	//初始化设置
    sntp_init();

   // LOG_I(sntp_client_main, "SNTP init done");
}
/**
 * Initialize this module.
 * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
 */
void
sntp_init(void)
{
#ifdef SNTP_SERVER_ADDRESS
#if SNTP_SERVER_DNS
  sntp_setservername(0, SNTP_SERVER_ADDRESS);
#else
#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
#endif
#endif /* SNTP_SERVER_ADDRESS */

  if (sntp_pcb == NULL) {
    SNTP_RESET_RETRY_TIMEOUT();
    
    //创建udp,用于接收udp包,时间数据
    sntp_pcb = udp_new();
    LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
    if (sntp_pcb != NULL) {
    
    //有数据,处理接收数据,同步到本地,由sntp_recv处理。
      udp_recv(sntp_pcb, sntp_recv, NULL);
#if SNTP_STARTUP_DELAY
      sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
#else
      sntp_request(NULL);
#endif
    }
  }
}

上述程序执行成功,再次获取本地时间,本地时间就会被更新。

uint32_t get_rtc_stamp_time(char * cur_time)
{
	hal_rtc_time_t r_time;
    hal_rtc_status_t ret = HAL_RTC_STATUS_OK;
	
	time_t timep = 0;    
	struct tm p;
	struct tm *tp;
	
	//获取本地时间,是utc时间
	ret = hal_rtc_get_time(&r_time);
	if (ret == HAL_RTC_STATUS_OK)
	{
		// printf("cur_time[%d,%d,%d,%d]\r\n", r_time.rtc_year, r_time.rtc_mon, r_time.rtc_day, r_time.rtc_week);
		// printf("[%d]cur_time[%d:%d:%d]\r\n", ret, r_time.rtc_hour, r_time.rtc_min, r_time.rtc_sec);
	#if 1
		p.tm_year = r_time.rtc_year + 2000 - 1900;//DATA%100 + 2000 -1900;
		p.tm_mon = r_time.rtc_mon-1;//(DATA/100) %100 -1;
		p.tm_mday = r_time.rtc_day;//DATA/10000;
		
		p.tm_hour= r_time.rtc_hour;//(UTCTime / 10000);
		p.tm_min= r_time.rtc_min;//(UTCTime / 100)%100;
		p.tm_sec= r_time.rtc_sec;//UTCTime % 100;
		
		//从utc时间转换成东八区时间
		timep=mktime(&p);
		timep = timep+28800;
		tp = localtime(&timep);  //0时区与东八区相差8个小时
		//printf("%d %d %d %d %d %d\n",(tp->tm_year+1900), (tp->tm_mon+1), tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
		sprintf(cur_time,"%02d-%02d-%02d %02d:%02d:%02d",(tp->tm_year+1900), (tp->tm_mon+1), tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
		
		//下图log
		printf("%02d-%02d-%02d %02d:%02d:%02d",(tp->tm_year+1900), (tp->tm_mon+1), tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
	#endif
		//sprintf(cur_time,"%d-%02d-%02d %d %02d:%02d:%02d",r_time.rtc_year, r_time.rtc_mon, r_time.rtc_day, r_time.rtc_week,r_time.rtc_hour, r_time.rtc_min, r_time.rtc_sec);

		
	}else{
		printf("get_rtc_stamp_time fail\r\n");
	}
	return timep;
}

测试log如下图所示
通过sntp同步系统时间

上一篇:STM32_2


下一篇:STM32 F7 RTC调试记录