1617|3

9

帖子

0

TA的资源

一粒金砂(中级)

【平头哥RVB2601创意应用开发】五。使用NTP授时RTC时钟 [复制链接]

本帖最后由 anni_zzg 于 2022-5-25 11:42 编辑

搞定RTC时钟后,开始想通过串口设计时间,后来知道可以使用NTP授时,心中一亮,那就开干吧。

     一。 先下载安装NTP组件。

 前面的测试都是在ch2601_helloworld工程中进行的,所以NTP功能也在这个工程中操作。

首先在打开工程的任意一个组件上右键点击,选View Details Web... 进入组件资源界面

                50.png

输入你要查看的组件NTP,然后搜索,安装

51.png 二。代码实现。

    (1)网络设置,可以参考ch2601_webplayer_demo这个工程,最起码它的网络功能问题不大(UDP有点问题)

     在init.c文件中添加如下代码

netmgr_hdl_t app_netmgr_hdl;

void w800_in(int linkid, void* data, size_t len, char remote_ip[16], uint16_t remote_ports)
{
    uint8_t* d;
    d = (uint8_t*)data;
    printf("recv data:%s", d);
}

static void network_init()
{
    w800_wifi_param_t w800_param;
    /* init wifi driver and network */
    w800_param.reset_pin = PA21;
    w800_param.baud = 1 * 1000000;
    w800_param.cs_pin = PA15;
    w800_param.wakeup_pin = PA25;
    w800_param.int_pin = PA22;
    w800_param.channel_id = 0;
    w800_param.buffer_size = 4 * 1024;

    wifi_w800_register(NULL, &w800_param);
    app_netmgr_hdl = netmgr_dev_wifi_init(); //无线设备初始化

    if(app_netmgr_hdl) {
	utask_t* task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
	netmgr_service_init(task);                                         //服务初始化
	netmgr_config_wifi(app_netmgr_hdl, "CMCC-GEFm", 9, "24r37ph4", 8); //无线配置
	// w800_packet_input_cb_register(&w800_in);//初始化输入回调函数
	netmgr_start(app_netmgr_hdl); //使能网络设备
    }
}

void board_yoc_init()
{
    board_init();                                // 板级初始化
    event_service_init(NULL);                    // 发布订阅服务初始化
    console_init(CONSOLE_UART_IDX, 115200, 512); // 串口初始化
    ulog_init();                                 // 日志初始化
    aos_set_log_level(AOS_LL_DEBUG);             // 配置默认日志打印级别

    int ret = partition_init(); // 分区初始化
    if(ret <= 0) {
	LOGE(TAG, "partition init failed");
    } else {
	LOGI(TAG, "find %d partitions", ret);
    }

    aos_kv_init("kv"); // kv文件系统初始化,可用于保存网络ssid&psk

    network_init(); // 网络初始化

    board_cli_init(); // 命令行初始化并注册默认的命令
}

main.c 添加如下代码: 

static void network_event(uint32_t event_id, const void* param, void* context)
{
    switch(event_id) {
    case EVENT_NETMGR_GOT_IP:
	// LOGD(TAG, "net got ip");
	ntp_sync_time(NULL);
	break;
    case EVENT_NETMGR_NET_DISCON:
	LOGD(TAG, "net disconnect");
	if((int)param == NET_DISCON_REASON_DHCP_ERROR) {
	    netmgr_reset(netmgr_get_handle("wifi"), 30); // 本次网络连接失败,30s后自动重连
	}

	break;
    }

    app_exception_event(event_id);
}

int main(void)
{
    board_yoc_init();
    LOGD(TAG, "%s\n", aos_get_app_version());
    // uart0_init();
    oled_init();
    led_init();
    key_init();
    timer_init();
    aita_InitTimer();
    rtc_init();
    IIC_Init();
    SGP30_Init();
    LED_RED_OFF;
    LED_GREEN_OFF;
    LED_BLUE_OFF;
    aos_timer_start(&polling_timer);

    event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL); //订阅服务
    event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
    return 0;
}

(2) RTC时钟的添加

aos_timer_t polling_timer; // timer for polling

void pollingTimerCb(void* arg1, void* arg2)
{
    csi_rtc_time_t rtc;

    csi_rtc_get_time(&aita_rtc, &rtc);

    strftime(oled_buf, sizeof(oled_buf), "%Y-%m-%d", &rtc);
    show_string(32, 0, oled_buf);

    strftime(oled_buf, sizeof(oled_buf), "%T", &rtc);
    show_string(40, 16, oled_buf);

    SHT30_read_result();
    SGP30_read_result();

    strftime(oled_buf, sizeof(oled_buf), "%Y-%m-%d %T", &rtc);

    LOGD(TAG, "%s\n", oled_buf);
}
// Init and startup periodic timer for polling job
void aita_InitTimer(void)
{
    int ret = -1;
    LOGI(TAG, "initialize timer for polling job!\n");

    ret = aos_timer_new_ext(&polling_timer, pollingTimerCb, "args", 1000, 1, 0);
    if(ret != 0) {
	LOGE(TAG, "polling_timer create failed\n");
    }
}
void rtc_init(void)
{
    csi_error_t ret;
    ret = csi_rtc_init(&aita_rtc, 0);
    if(ret == CSI_OK)
	printf("csi_rtc OK\n");
    else
	printf("csi_rtc wrong\n");
}

(3) NTP功能添加

/* ntpclient.c */
#include <aos/aos.h>
#include <sys/select.h>
#include <lwip/netdb.h>
#include <arpa/inet.h>
#include <drv/rtc.h>

static const char* TAG = "NTP";

#ifndef CONFIG_NTP_CTS_ZONE
#define CTS_ZONE 8
#else
#define CTS_ZONE CONFIG_NTP_CTS_ZONE
#endif

#define VERSION_3 3
#define VERSION_4 4

#define MODE_CLIENT 3
#define MODE_SERVER 4

// NTP protocol Content
#define NTP_LI 0
#define NTP_VN VERSION_3
#define NTP_MODE MODE_CLIENT
#define NTP_STRATUM 0
#define NTP_POLL 4
#define NTP_PRECISION -6

#define NTP_HLEN 48

#define NTP_PORT 123

#define TIMEOUT 3

#define BUFSIZE 128

#define JAN_1970 0x83aa7e80

#define NTP_CONV_FRAC32(x) (uint64_t)((x) * ((uint64_t)1 << 32))
#define NTP_REVE_FRAC32(x) ((double)((double)(x) / ((uint64_t)1 << 32)))

#define NTP_CONV_FRAC16(x) (uint32_t)((x) * ((uint32_t)1 << 16))
#define NTP_REVE_FRAC16(x) ((double)((double)(x) / ((uint32_t)1 << 16)))

#define USEC2FRAC(x) ((uint32_t)NTP_CONV_FRAC32((x) / 1000000.0))
#define FRAC2USEC(x) ((uint32_t)NTP_REVE_FRAC32((x)*1000000.0))

#define NTP_LFIXED2DOUBLE(x)                                        \
    ((double)(ntohl(((struct l_fixedpt*)(x))->intpart) - JAN_1970 + \
              FRAC2USEC(ntohl(((struct l_fixedpt*)(x))->fracpart)) / 1000000.0))

struct s_fixedpt {
    uint16_t intpart;
    uint16_t fracpart;
};

struct l_fixedpt {
    uint32_t intpart;
    uint32_t fracpart;
};

struct ntphdr {
    unsigned int ntp_mode : 3;
    unsigned int ntp_vn : 3;
    unsigned int ntp_li : 2;

    uint8_t ntp_stratum;
    uint8_t ntp_poll;
    int8_t ntp_precision;
    struct s_fixedpt ntp_rtdelay;
    struct s_fixedpt ntp_rtdispersion;
    uint32_t ntp_refid;
    struct l_fixedpt ntp_refts;
    struct l_fixedpt ntp_orits;
    struct l_fixedpt ntp_recvts;
    struct l_fixedpt ntp_transts;
};

in_addr_t inet_host(const char* host)
{
    in_addr_t saddr;
    struct hostent* hostent;

    if((saddr = inet_addr(host)) == INADDR_NONE) {
	if((hostent = gethostbyname(host)) == NULL) {
	    return INADDR_NONE;
	}

	// memcpy(&saddr, hostent->h_addr_list[0], hostent->h_length);

	saddr = *((unsigned long*)hostent->h_addr_list[0]);
    }

    return saddr;
}

int get_ntp_packet(void* buf, size_t* size)
{
    struct ntphdr* ntp;
    struct timeval tv;

    if(!size || *size < NTP_HLEN) {
	return -1;
    }

    memset(buf, 0, *size);

    ntp = (struct ntphdr*)buf;
    ntp->ntp_li = NTP_LI;
    ntp->ntp_vn = NTP_VN;
    ntp->ntp_mode = NTP_MODE;
    ntp->ntp_stratum = NTP_STRATUM;
    ntp->ntp_poll = NTP_POLL;
    ntp->ntp_precision = NTP_PRECISION;

    gettimeofday(&tv, NULL);
    ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
    ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));

    *size = NTP_HLEN;

    return 0;
}

double get_offset(const struct ntphdr* ntp, const struct timeval* recvtv)
{
    double t1, t2, t3, t4;

    t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
    t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
    t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
    t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;

    return ((t2 - t1) + (t3 - t4)) / 2;
}

struct tm tm_val;
void aita_print_ntp(struct ntphdr* ntp)
{
    time_t time;
    // by author. get ntp time, set into rtc then show datetime once
    time = ntohl(ntp->ntp_recvts.intpart) - JAN_1970 + CTS_ZONE * 3600;
    ; // with timezone offset
    char tbuf[64];
    tm_val = *(localtime(&time));
    extern csi_rtc_t aita_rtc; // rtc device descriptor
    csi_rtc_set_time(&aita_rtc, &tm_val);
    strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", &tm_val);
    printf("aita datetime: %s\n", tbuf);
}

static int _ntp_sync_time(char* server)
{
    char buf[BUFSIZE];
    size_t nbytes;
    int sockfd, maxfd1;
    struct sockaddr_in servaddr = {
	0,
    };
    fd_set readfds;
    struct timeval timeout, recvtv, tv, rcvtimeout = { 3, 0 };
    double offset;

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(NTP_PORT);

    if(server == NULL) {
	// 1.cn.pool.ntp.org is more reliable
	servaddr.sin_addr.s_addr = inet_host("ntp1.aliyun.com");
	LOGD(TAG, "ntp1.aliyun.com");
    } else {
	servaddr.sin_addr.s_addr = inet_host(server);
	LOGD(TAG, "%s", server);
    }
    printf("--------------------->check server done! _ntp_sync_time()\n");

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	// LOGE(TAG, "socket error");
	return -1;
    }

    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeout, sizeof(struct timeval));

    if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)) != 0) {
	// LOGE(TAG, "connect error");
	close(sockfd);
	return -errno;
    }
    printf("--------------------->connect server done! _ntp_sync_time()\n");

    nbytes = BUFSIZE;

    if(get_ntp_packet(buf, &nbytes) != 0) {
	// LOGE(TAG, "construct ntp request errorr");
	close(sockfd);
	return -1;
    }

    send(sockfd, buf, nbytes, 0); //发送一个UDP数据包
    printf("--------------------->send request pack done! _ntp_sync_time()\n");

    FD_ZERO(&readfds);        //将集合readfds清零
    FD_SET(sockfd, &readfds); //将sockfd加入集合
    maxfd1 = sockfd + 1;

    timeout.tv_sec = TIMEOUT;
    timeout.tv_usec = 0;

    if(select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) {
	if(FD_ISSET(sockfd, &readfds)) {
	    if((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0) {
		// LOGE(TAG, "recv error");
		close(sockfd);
		return -1;
	    }

	    // printf("nbytes = %d\n", nbytes);
	    // print_ntp((struct ntphdr *) buf);
	    aita_print_ntp((struct ntphdr*)buf);
	    gettimeofday(&recvtv, NULL);
	    offset = get_offset((struct ntphdr*)buf, &recvtv);
	    gettimeofday(&tv, NULL);
// TODO: ctime has some problem

// char tbuf[64];
// strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", localtime(&tv.tv_sec));
// memset(&tv, 0, sizeof(tv));
// tbuf = ctime((time_t *)&tv.tv_sec);
// LOGD(TAG, "system time1:%s", tbuf);
#if 1

	    tv.tv_sec += (int)offset;
	    tv.tv_usec += offset - (int)offset;

	    if(settimeofday(&tv, NULL) != 0) {
		LOGE(TAG, "set time error");
		close(sockfd);
		return -1;
	    }
	    LOGD(TAG, "--------------------->settimeofday done! _ntp_sync_time()\n");

// TODO: ctime has some problem
// LOGD(TAG, "ntp time:\t%s", ctime((time_t *) &tv.tv_sec));
#endif
	}
    } else {
	close(sockfd);
	LOGD(TAG, "--------------------->select body error! _ntp_sync_time()\n");
	return -1;
    }

    close(sockfd);
    LOGD(TAG, "--------------------->whole done! _ntp_sync_time()\n");
    return 0;
}
int ntp_sync_time(char* server)
{
    int ret = -1;
    for(int i = 0; i < 2; i++) {
	ret = _ntp_sync_time(server);
	if(ret == 0) {
	    LOGD(TAG, "sync success");
	    break;
	}
    }

    if(ret < 0) {
	LOGE(TAG, "sync error");
    }

    return ret;
}

4)修改UDP连接功能,UDP的通信不但需要远程IP,远程端口,还需要本地的端口,本地端口自己制定一个大于1024的端口值。

int32_t udp_local_port = 1338;

int w800_connect_remote(int id, net_conn_e type, char *srvname, uint16_t port)
{
    int ret = -1;
    int ret_id;

    if (g_net_status < NET_STATUS_GOTIP) {
        LOGE(TAG, "net status error\n");
        return -1;
    }

    aos_mutex_lock(&g_cmd_mutex, AOS_WAIT_FOREVER);

    atparser_clr_buf(g_atparser_uservice_t);

    switch (type) {
        case NET_TYPE_TCP_SERVER:
            /* TCP Server can NOT ignore lport */
            break;

        case NET_TYPE_UDP_UNICAST:
			printf("UDP_UNICAST mode\n");
			ret = atparser_send(g_atparser_uservice_t, "AT+CIPSTART=%d,%s,%s,%d,%d", id, "udp_unicast", srvname, port, udp_local_port);
            break; 
        case NET_TYPE_TCP_CLIENT:
            ret = atparser_send(g_atparser_uservice_t, "AT+CIPSTART=%d,%s,%s,%d", id, "tcp_client", srvname, port);
            break;

        default:
            LOGE(TAG, "type=%d err!", type);
            return -1;

    }

    if (ret == 0) {
        ret = -1;

        if ((atparser_recv(g_atparser_uservice_t, "OK\n") == 0) \
            && (atparser_recv(g_atparser_uservice_t, "+EVENT=CONNECT,%d\n", &ret_id) == 0)) {
            if (ret_id == id) {
                ret = 0;
            }
        }
    }

    atparser_cmd_exit(g_atparser_uservice_t);

    aos_mutex_unlock(&g_cmd_mutex);

    return ret;
}

(5)显示部分参考我的经验分享三和四。

三。 总结。

NTP的实现走过了不少的坑,当初网络通了,可是NTP一直不通,不知道怎么下手了。还好RVB2601活动群分享了网友sonicfirr(OCC ID:firr)的RTC应用示例 【平头哥RVB2601创意应用开发】使用体验06 -- NTP授时 - 平头哥RISC-V RVB2601活动专区 - 电子工程世界-论坛 (eeworld.com.cn),本程序中去掉了sonicfirr定义的aita_rtc_t和aita_tm_u两个结构体。经验证csi_rtc_time_t和时间函数 tm是可以转换使用的。

四。实验结果

    (1)通过设置 netmgr_config_wifi(app_netmgr_hdl, "CMCC-GEFm", 9, "24r37ph4", 8); //配置成自己的WIFI,如下,说明得到自己的IP地址,WIFI网络连接成功。 52.png

(2)授时,并读取RTC

    53.png

(3)OLED结果展示

  55.png

最新回复

明天发你的工程我学习一下。   详情 回复 发表于 2022-6-2 16:10

回复
举报

6506

帖子

9

TA的资源

版主

确认可以了吗?是不是平头哥修正了?我还一直没有弄通列!

点评

可以了老弟,正常使用没问题    详情 回复 发表于 2022-6-2 14:34

回复

9

帖子

0

TA的资源

一粒金砂(中级)

lugl4313820 发表于 2022-5-25 21:57 确认可以了吗?是不是平头哥修正了?我还一直没有弄通列!

可以了老弟,正常使用没问题

 

点评

明天发你的工程我学习一下。  详情 回复 发表于 2022-6-2 16:10

回复

6506

帖子

9

TA的资源

版主

anni_zzg 发表于 2022-6-2 14:34 可以了老弟,正常使用没问题  

明天发你的工程我学习一下。


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表