【平头哥RVB2601创意应用开发】五。使用NTP授时RTC时钟
本帖最后由 anni_zzg 于 2022-5-25 11:42 编辑<p>搞定RTC时钟后,开始想通过串口设计时间,后来知道可以使用NTP授时,心中一亮,那就开干吧。</p>
<p> <span style="font-size:20px;"><strong>一。 先下载安装NTP组件。</strong></span></p>
<p> 前面的测试都是在ch2601_helloworld工程中进行的,所以NTP功能也在这个工程中操作。</p>
<p>首先在打开工程的任意一个组件上右键点击,选View Details Web... 进入组件资源界面</p>
<p> </p>
<p>输入你要查看的组件NTP,然后搜索,安装</p>
<p><strong><span style="font-size:20px;"><span style="font-family:宋体;">二。代码实现。</span></span></strong></p>
<p> <strong>(1)网络设置</strong>,可以参考ch2601_webplayer_demo这个工程,最起码它的网络功能问题不大(UDP有点问题)</p>
<p> 在init.c文件中添加如下代码</p>
<pre>
<code class="language-cpp">netmgr_hdl_t app_netmgr_hdl;
void w800_in(int linkid, void* data, size_t len, char remote_ip, 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(); // 命令行初始化并注册默认的命令
}</code></pre>
<p>main.c 添加如下代码: </p>
<pre>
<code class="language-cpp">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;
}</code></pre>
<p><strong>(2) RTC时钟的添加</strong></p>
<pre>
<code class="language-cpp">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");
}</code></pre>
<p><strong>(3) NTP功能添加</strong></p>
<pre>
<code class="language-cpp">/* 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, hostent->h_length);
saddr = *((unsigned long*)hostent->h_addr_list);
}
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;
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;
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;
// 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;
}
</code></pre>
<p>(<strong>4)修改UDP连接功能</strong>,UDP的通信不但需要远程IP,远程端口,还需要本地的端口,本地端口自己制定一个大于1024的端口值。</p>
<pre>
<code class="language-cpp">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;
}</code></pre>
<p><strong>(5)显示部分参考我的经验分享三和四。</strong></p>
<p><span style="font-size:20px;"><span style="font-family:宋体;"><strong>三。 总结。</strong></span></span></p>
<p>NTP的实现走过了不少的坑,当初网络通了,可是NTP一直不通,不知道怎么下手了。还好RVB2601活动群分享了网友sonicfirr(OCC ID:firr)的RTC应用示例 <a href="https://bbs.eeworld.com.cn/thread-1199207-1-1.html">【平头哥RVB2601创意应用开发】使用体验06 -- NTP授时 - 平头哥RISC-V RVB2601活动专区 - 电子工程世界-论坛 (eeworld.com.cn)</a>,本程序中去掉了sonicfirr定义的aita_rtc_t和aita_tm_u两个结构体。经验证csi_rtc_time_t和时间函数 tm是可以转换使用的。</p>
<p><strong><span style="font-size:20px;"><span style="font-family:宋体;">四。实验结果</span></span></strong></p>
<p> (1)通过设置 netmgr_config_wifi(app_netmgr_hdl, <span style="color:#27ae60;">"CMCC-GEFm", 9, "24r37ph4", 8</span>); //配置成自己的WIFI,如下,说明得到自己的IP地址,WIFI网络连接成功。</p>
<p>(2)授时,并读取RTC</p>
<p> </p>
<p>(3)OLED结果展示</p>
<p> </p>
确认可以了吗?是不是平头哥修正了?我还一直没有弄通列! lugl4313820 发表于 2022-5-25 21:57
确认可以了吗?是不是平头哥修正了?我还一直没有弄通列!
<p>可以了老弟,正常使用没问题</p>
<p> </p>
anni_zzg 发表于 2022-6-2 14:34
可以了老弟,正常使用没问题
<p>明天发你的工程我学习一下。</p>
页:
[1]