stm32 同步NTP服务器的时间

https://blog.csdn.net/haidscs/article/details/102733130

首先找一个可用的ntp服务器,这里以阿里的ntp服务器为例:ntp1.aliyun.com。

把域名的ip解析出来:

因为ntp服务器是udp协议,ip:120.25.115.20 端口号:123,格式是接收48个字节,第一个字节以0xa3(版本4) 、0x1b, (版本3)、0x13(版本2) 、0x0b(版本1),返回的数据中带有时间。

ntp协议的报文格式:

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。
认证符(可选项) 

转化举例:

把十六进制数转换十进制数,再减去1900-1970的时间差(2208988800秒)。0xE15C2D5B->3780914523 -2208988800=1571925723 时间转化工具出来 。补充:有些服务器需要加上北京时间差(东八区的时区 【8*60*60】)

以上是在Windows上验证。

下面以stm32获取ntp服务器的时间

#define NTP_TIMESTAMP_DELTA 2208988800ull

#define DEV_LAN_NTP_UDP_REV_BUFF_MAX      200
u8      rbuf[DEV_LAN_UDP_REV_BUFF_MAX+1]={0};
u16     rlen=0;
static struct udp_pcb            *lwip_ntp_udp_pcb=0;
static unsigned char            *lwip_ntp_udp_rev_buff=0;
static unsigned short            lwip_ntp_udp_rev_buff_max=0;
static unsigned short            *lwip_ntp_udp_rev_len=0; 

//发送

err_t lwip_ntp_udp_ip_send(unsigned char *data, unsigned long len, unsigned char *send_ip, unsigned short send_port)

{
    struct pbuf *send_pbuf;
    unsigned long i=0;
    struct ip_addr sip;
    err_t  err=0;
    
    IP4_ADDR(&sip, send_ip[0], send_ip[1], send_ip[2], send_ip[3]);
    if(lwip_ntp_udp_pcb == NULL)
      return -1;
    for (i=0; i<len; i+=200)
    {
        send_pbuf = pbuf_alloc(PBUF_RAW,200, PBUF_REF);
        if (send_pbuf !=NULL)
        {
            send_pbuf->payload = (void*)&data[i];
            if (i+200<=len) send_pbuf->tot_len = 200;
            else send_pbuf->tot_len=len%200;
            err=udp_sendto(lwip_ntp_udp_pcb, send_pbuf, &sip, send_port);
            pbuf_free(send_pbuf); 
            if (err!=0) break;                    
        }
    }
    return err;
}

//接收

void lwip_ntp_udp_rev(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
{
    struct pbuf *q=0;
    
    if (p !=NULL)
    {
         lwip_ntp_udp_rev_addr = *addr;
         lwip_ntp_udp_rev_port = port; 
         
         for (q=p; q!=NULL; q=q->next)
         {
             if ((q->tot_len+*lwip_ntp_udp_rev_len)<=lwip_ntp_udp_rev_buff_max)
             {
                 if (q->tot_len>=1472) 
                 {
                     memcpy(&lwip_ntp_udp_rev_buff[*lwip_ntp_udp_rev_len],q->payload,1472);
                     *lwip_ntp_udp_rev_len+=1472;
                 }
                 else 
                 {
                     memcpy(&lwip_ntp_udp_rev_buff[*lwip_ntp_udp_rev_len],q->payload,q->tot_len);
                     *lwip_ntp_udp_rev_len+=q->tot_len;                    
                 }
             }
             else break;
         }     
         pbuf_free(p);
    }
    else
    {
        udp_remove(upcb);
    }
}

//创立udp连接

err_t lwip_ntp_udp_connect_init(unsigned short src_port)
{
     err_t err=0;
     struct ip_addr rmtipaddr;
     lwip_ntp_udp_close();
     
     lwip_ntp_udp_pcb = udp_new();
     if (lwip_ntp_udp_pcb)
     {
        IP4_ADDR(&rmtipaddr,120,25,115,20);//将点分10进制IP转为4字节变量,因为IP控制块里面通过32位存储IP地址。
        err = udp_bind(lwip_ntp_udp_pcb, IP_ADDR_ANY, src_port);//绑定本地的ip和端口
        //err=udp_connect(lwip_ntp_udp_pcb,&rmtipaddr,123);   // udp连接,NTP使用的是UDP和123端口          
        udp_recv(lwip_ntp_udp_pcb, lwip_ntp_udp_rev, NULL);
     }
     return err;
}
void lwip_ntp_udp_rev_init(unsigned char *rev_buff, unsigned short rev_buff_max, unsigned short *rev_len)
{
    *rev_len=0;
    lwip_ntp_udp_rev_buff=rev_buff;
    lwip_ntp_udp_rev_buff_max=rev_buff_max;
    lwip_ntp_udp_rev_len=rev_len;
}
void lwip_ntp_udp_close(void)
{
    udp_remove(lwip_ntp_udp_pcb);
}

void main()
{
    u8 buuf[48] = {0x1b};
    u8 ip[4] ={120,25,115,20};   
    u32 NtpTime = 0;

    lwip_ntp_udp_connect_init(666);//创建连接
    lwip_ntp_udp_rev_init(rbuf,DEV_LAN_UDP_REV_BUFF_MAX,&rlen);//分配内存
    lwip_ntp_udp_ip_send(buuf,48,ip,123);//向addr的123端口发送报文,NTP使用的是UDP和123端口

    if(rlen>0)
    {
        NtpTime = rbuf[0]<<24 | rbuf[1]<<16 | rbuf[2]<<8 | rbuf[3];
        NtpTime -= NTP_TIMESTAMP_DELTA;
    }
    while(1);
}

 

posted on 2020-09-28 13:42  覃隆强  阅读(2299)  评论(0编辑  收藏  举报