CH395通过DNS实现网络时间获取

CH395通过DNS实现网络时间获取

一、结构:

二、实现步骤:

1、软件主要配置参数:

(1)执行初始化CH395,启用DHCP,等待PHY链接

(2)初始化socket0为UDP CLINET模式,本地端口随机或固定均可,目的端口为53用于DNS

(3)DNS组包用于解析域名:ntp.ntsc.ac.cn(国家授时中心服务器)

(4)获取到NTP服务器IP,创建UDPsocket1,设置目的NTP服务器IP,目的端口:123

 

2、步骤:

(1)DHCP和DNS:通过CH395的DHCP和DNS例程解析NTP服务器域名(该过程参考CH395DNS例程步骤即可):    

UINT8 DnsQuery(UINT8 s, UINT8 *name, UINT8 *pSip)
{
    struct dhdr dhp;
    UINT16 len, cnt;
    if (status > 1)
    {
        count++;
        printf("count = %2d\n", (UINT16)count);
        Delay_Ms(2);
        if (count > 2000)
        {
            printf("DNS Fail!!!!\n");
            count = 0;
            status = 0;
        }
    }
    if (status == 1)
    {
        UDPSocketParamInit(s, DNS_SERVER_IP, Socket0SourPort, IPPORT_DOMAIN);
        status = 2;
        printf("status = 2!\n");
    }
    if (status == 2)
    {
        Delay_Ms(200);
        len = MakeDnsQueryMsg(0, (char *)name, dns_buf, MAX_DNS_BUF_SIZE);
        cnt = UDPSendData(s, dns_buf, len);
        if (cnt == 0)
            return (0);
        else
        {
            status = 3;
            printf("status = 3!\n");
        }
    }
    if (status == 4)
    {
        return (parseMSG(&dhp, dns_buf, pSip));
    }
    return 0;
}

 (2)组包发送和解析:

#include "debug.h"
#include "CH395INC.h"
#include "CH395.H"
#include "DNS.H"
#include "SNTP.H"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

uint8_t ntp_len;
uint8_t ntp_buf[128];
int Flag = 0;
static uint32_t sntp_tick = 0;  // 发包计时
#define SNTP_PERIOD_S 64        // 64秒发一次请求

#define NTP_PACKET_LEN          48UL
#define NTP_OFFSET              2208988800UL  // 1900 -> 1970 秒偏移
#pragma pack(1)
typedef struct {
    uint8_t  li_vn_mode;
    uint8_t  stratum;
    uint8_t  poll;
    uint8_t  precision;

    uint32_t root_delay;
    uint32_t root_dispersion;
    uint32_t ref_id;

    uint32_t ref_ts_sec;
    uint32_t ref_ts_frac;

    uint32_t orig_ts_sec;
    uint32_t orig_ts_frac;

    uint32_t recv_ts_sec;
    uint32_t recv_ts_frac;

    uint32_t trans_ts_sec;
    uint32_t trans_ts_frac;
} NTP_Packet;
#pragma pack()

// 时间结构体
typedef struct {
    uint16_t year;
    uint8_t  month;
    uint8_t  day;
    uint8_t  hour;
    uint8_t  min;
    uint8_t  sec;
} _RTC_T;

// 大小端转换 ntohl
uint32_t ntohl_sntp(uint32_t netlong)
{
    return ((netlong & 0xFF000000U) >> 24) |
           ((netlong & 0x00FF0000U) >> 8)  |
           ((netlong & 0x0000FF00U) << 8)  |
           ((netlong & 0x000000FFU) << 24);
}

// 主机Unix秒 → NTP大端时间戳
static uint32_t UnixToNtpBig(uint32_t unix_sec)
{
    uint32_t ntp_sec = unix_sec + NTP_OFFSET;
    return ntohl_sntp(ntp_sec);
}

//读取本地Unix时间
uint32_t GetLocalUnix(void)
{
    // 固定测试时间:2026-06-30 00:00:00 UTC
    return 1782835200UL;
}

// 发送标准合规 SNTP 请求
void SNTP_SendRequest(void)
{
    uint8_t pkt_buf[NTP_PACKET_LEN] = {0};
    uint32_t now_unix = GetLocalUnix();
    uint32_t stamp_val = UnixToNtpBig(now_unix);

    // 0字节:li_vn_mode
    pkt_buf[0] = 0x23;
    // 1 stratum, 2 poll=6, 3 precision=0xFA
    pkt_buf[1] = 0;
    pkt_buf[2] = 6;
    pkt_buf[3] = 0xFA;

    // root_delay / root_dispersion / ref_id 全部0,跳过填充
    // offset 16 ~ 19 orig_ts_sec
    pkt_buf[16] = (stamp_val >> 24) & 0xFF;
    pkt_buf[17] = (stamp_val >> 16) & 0xFF;
    pkt_buf[18] = (stamp_val >> 8)  & 0xFF;
    pkt_buf[19] = stamp_val & 0xFF;
    // orig_ts_frac 20~23 保持0

    // offset 32 ~35 trans_ts_sec
    pkt_buf[32] = (stamp_val >> 24) & 0xFF;
    pkt_buf[33] = (stamp_val >> 16) & 0xFF;
    pkt_buf[34] = (stamp_val >> 8)  & 0xFF;
    pkt_buf[35] = stamp_val & 0xFF;
    // trans_ts_frac 36~39 保持0

    printf("发送SNTP请求包\r\n");
    CH395CMDSendData(1, pkt_buf, NTP_PACKET_LEN);
}

// 解析 NTP 服务器应答 → UNIX 时间戳
// 返回0成功 1包长不足 2模式错误
uint8_t SNTP_GetUnixTime(uint32_t *unix_sec)
{
    
    if(ntp_len < NTP_PACKET_LEN)
        return 1;

    NTP_Packet *pkt = (NTP_Packet *)ntp_buf;
    // 服务器应答 Mode=4
    if ((pkt->li_vn_mode & 0x07) != 4)
        return 2;

    uint32_t ntp_sec = ntohl_sntp(pkt->trans_ts_sec);
    *unix_sec = ntp_sec - NTP_OFFSET;
    return 0;
}

// UNIX 时间戳 → 北京时间(UTC+8)
void UnixToDateTime(uint32_t unix, _RTC_T *dt)
{
    const uint8_t month_table[] = {31,28,31,30,31,30,31,31,30,31,30,31};
    uint32_t seconds, days;
    uint16_t year;
    uint8_t  month, leap = 0;

    unix += 8 * 3600;    // 东八区
    seconds = unix % 86400;
    days    = unix / 86400;

    dt->hour = seconds / 3600;
    dt->min  = (seconds % 3600) / 60;
    dt->sec  = seconds % 60;

    year = 1970;
    while (1)
    {
        leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
        if (days < (365 + leap))
            break;
        days -= (365 + leap);
        year++;
    }
    dt->year = year;

    for (month = 0; month < 12; month++)
    {
        uint8_t mdays = month_table[month];
        if (month == 1 && leap)
            mdays++;
        if (days < mdays)
            break;
        days -= mdays;
    }
    dt->month = month + 1;
    dt->day   = days + 1;
}

// SNTP 主任务(1s调用一次,定时发包+接收解析)
void SNTP_Task(void)
{
    uint32_t unix_time;
    _RTC_T   rtc_time;
    uint8_t  ret;

    // 1. 计时,64秒发送一次NTP请求
    sntp_tick ++;
    if(sntp_tick >= SNTP_PERIOD_S)
    {
        sntp_tick = 0;
        SNTP_SendRequest();
    }

    // 2. 读取CH395接收缓存
    ntp_len = CH395CMDGetRecvLength(1);
    if(ntp_len == 0)
        return; // 无数据直接退出

    CH395CMDGetRecvData(1, ntp_len,ntp_buf);
    printf("收到字节数: %d\r\n", ntp_len);
    printf("第一个字节: 0x%02X\r\n", ntp_buf[0]);

    // 3. 解析应答包
    ret = SNTP_GetUnixTime(&unix_time);
    if (ret == 0)
    {
        UnixToDateTime(unix_time, &rtc_time);
        printf("SNTP 同步成功!\r\n");
        printf("时间: %04u-%02u-%02u %02u:%02u:%02u\r\n",
            rtc_time.year,
            rtc_time.month,
            rtc_time.day,
            rtc_time.hour,
            rtc_time.min,
            rtc_time.sec);
        Flag = 0;
    }
    else
    {
        printf("SNTP FAIL, 错误码: %d\r\n", ret);
        Flag=1;
    }
}

 

 

三、运行结果:

 

int main(void)
{
    UINT8 i;
        UINT8 flag1=0;
    Delay_Init();
    USART_Printf_Init(115200);

    /*
    CH395 supports three interface modes: SPI, serial, and parallel.
    When using different interfaces, refer to the interface configuration instructions in 'CH395DS1.pdf'
    and ensure that the corresponding .c file is included in the compilation.
    Additionally, define CH395_OP_INTERFACE_MODE in 'CH395INC.h' to match the selected interface.
    For example, when using the SPI interface, the TXD pin should be grounded, the SEL pin should be floating or pulled high,
    and the 'CH395SPI_HW.c' file should be included in the compilation.
    Furthermore,CH395_OP_INTERFACE_MODE should be set to CH395_SPI_MODE in 'CH395INC.h'.
    */

#if (CH395_OP_INTERFACE_MODE == CH395_UART_MODE)
    printf("CH395 UART Interface Mode\r\n");
#endif
#if (CH395_OP_INTERFACE_MODE == CH395_SPI_MODE)
    printf("CH395 SPI Interface Mode\r\n");
#endif
#if (CH395_OP_INTERFACE_MODE == CH395_PARA_MODE)
    printf("CH395 Parallel Interface Mode\r\n");
#endif

    CH395_PORT_INIT();
    printf("CH395EVT Test Demo\r\n");
    CH395_GPIO_INIT();
    CH395Reset();
    Delay_Ms(100);
    i = CH395CMDGetVer();
    printf("CH395VER : %2x\r\n", (UINT16)i);

    i = CH395Init(); /* Initialize the CH395 */
    mStopIfError(i);

    while (1)
    {                                              /* Wait for the CH395 Connect Ethernet*/
        if (CH395CMDGetPHYStatus() == PHY_DISCONN) /* Example Query whether the CH395 is connected */
        {
            Delay_Ms(200); /* If no, wait for 200MS and query again */
        }
        else
        {
            printf("CH395 Connect Ethernet\r\n"); /*When the CH395 is connected to the Ethernet, an interruption occurs */
            break;
        }
    }

    i = CH395CMDDHCPEnable(1);
    mStopIfError(i);
    while (1)
    {
        if (Query395Interrupt() == 0)
        {
            CH395GlobalInterrupt();
        }
        i = DnsQuery(0, SNTP_SERVER_IP, ip);
        if (i)
        {      flag1=1;
            printf("Domain name: %s \n", SNTP_SERVER_IP);
                        
            printf("HTTPs_IP = %d.%d.%d.%d\n\n", ip[0], ip[1], ip[2], ip[3]);
                
                    
                    if(flag1 == 1)
                { 
        
                    InitSocketParam();
                    CH395SocketInitOpen();
                    flag1 =2;
                }

                if(flag1 == 2)
                {
                    printf("start\r\n");
                    SNTP_SendRequest();
                  Delay_Ms(2000);
                    Delay_Ms(2000);
                    Delay_Ms(2000);
                    SNTP_Task();
                    flag1=2;
                }
                
            status = 1;
            break;
        }
                
                if(Flag==1)
                {
                    if(flag1 == 2)
                    {
                        printf("start\r\n");
                        SNTP_SendRequest();
                        
                        Delay_Ms(2000);
                        Delay_Ms(2000);
                        Delay_Ms(2000);
                        
                        SNTP_Task();
                        flag1=2;
                        Flag=0;
                    }
                }
        
    }
}

 

image

 

posted on 2026-07-01 08:49  sw2222  阅读(9)  评论(0)    收藏  举报