NTP授时成功后,创意项目继续启用RTC,利用NTP获取的时间初始化RTC,以便在系统运行期间可以更快速的获得本地时间。
在AOS API(https://yoc.docs.t-head.cn/yocbook/Chapter3-AliOS/)中包含两套设备驱动接口CSI2和CSI1,通过对RVB2601的SDK查看,发现板子实现的是CSI2接口。
图8-1 文档中的CSI2接口和SDK中的RTC实现
依据文档说明,本项目使用了RTC的三个功能:初始化csi_rtc_init(),设置时间csi_rtc_set_time()和获取时间csi_rtc_get_time()。
图8-2 文档中的csi_rtc_init()描述
图8-3 文档中的csi_rtc_set_time()描述
图8-4 文档中的csi_rtc_get_time()描述
2、newlib包中的time库
RVB2601的SDK中,newlib包提供了C标准库。说来惭愧,本人搞了多年嵌入式,C语言是主要生产工具,但是C标准库却是比较陌生,尤其是time库,因此这里也走了一点弯路(因为不熟悉,也搞不清楚是不是bug,所以不敢称为“踩坑”)。
之前修正的NTP功能,有获取网络时间并调用settimeofday()的语句,通过网上检索了解到这是设置系统时间戳的函数。还有gettimeofday()则是获取系统时间,于是想在ntp_sync_time()调用后,通过gettimeofday()即获得本地时间。但是,这样操作得到的时间是错误的,于是又联想到向平头哥反馈NTP bug时,技术小哥给了一个官方修正NTP bug的操作文档,其中包含有对time库的修改。所以,按照文档修改了一下SDK,结果依然不成功。
所以,最后放弃了使用gettimeofday()的思路,当然time库中还是用到了几个API,可以更方便的进行时间格式转换。API具体有:类型重定义time_t、结构体struct tm、本地时间函数localtime()和时间格式化函数strftime()。
数据结构类型定义和函数声明都位于“../newlib/v7.4.3/include/time.h”头文件中,具体如下。
//by author. omit other codes
typedef int32_t time_t; /* Holds time in seconds */
struct tm
{
int tm_sec; /* Seconds (0-61, allows for leap seconds) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Years since 1900 */
int tm_wday; /* Day of the week (0-6) */
int tm_yday; /* Day of the year (0-365) */
int tm_isdst; /* Non-0 if daylight savings time is in effect */
};
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
inline struct tm *localtime(const time_t *tim_p) {return gmtime(tim_p);}
3、RTC实现与NTP修改
本例的思路是:系统启动进行RTC初始化和网络初始化,在获得IP地址的事件回调中请求NTP授时,并将收到的时间值设置到RTC中,之后激活定时器每个一段时间读取RTC并显示读取值。
注意1、2节中展示的结构体csi_rtc_time_t和struct tm,两者相差一个成员,即struct tm有分量“int tm_isdst”,于是本例采用建立联合体来保存时间数据结构,以同时适配CSI RTC接口和time C库。
另外,本例仿照NTP的原API,编写了可NTP授时同时设置RTC的函数。联合体类型定义和函数声明放在了“../ntp/v7.4.3/include/ntp.h”中,其完整代码如下。
#ifndef __NTP_H__
#define __NTP_H__
#include <drv/rtc.h>
#ifdef __cplusplus
extern "C"
{
#endif
int ntp_sync_time(char *server);
int aita_ntp_sync_time(char *server); //by author. ntp sync & set rtc function
typedef struct {
csi_rtc_time_t rtc;
int isdst;
} aita_rtc_t;
typedef union {
struct tm libc_tm;
aita_rtc_t rtc_tm;
} aita_tm_u;
extern aita_tm_u aita_dt;
#ifdef __cplusplus
}
#endif
#endif
对应的ntp.c,增加的代码如下。
#define AITA_LOCAL 8*3600
aita_tm_u aita_dt;
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 + AITA_LOCAL; //with timezone offset
char tbuf[64];
aita_dt.libc_tm = *(localtime(&time)); //set system datetime variable
extern csi_rtc_t aita_rtc; //rtc device descriptor
csi_rtc_set_time(&aita_rtc, &(aita_dt.rtc_tm.rtc)); //set datetime variable into rtc
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", &(aita_dt.libc_tm));//datetime to string
printf("aita datetime: %s\n", tbuf);
}
int aita_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) {
servaddr.sin_addr.s_addr = inet_host("ntp1.aliyun.com");
LOGD(TAG, "ntp1.aliyun.com\t---------------------> aita_ntp_sync_time()\n");
} else {
servaddr.sin_addr.s_addr = inet_host(server);
LOGD(TAG, "%s\t---------------------> aita_ntp_sync_time()\n", server);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
LOGE(TAG, "socket error\t---------------------> aita_ntp_sync_time()\n");
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\t---------------------> aita_ntp_sync_time()\n");
close(sockfd);
return -errno;
}
nbytes = BUFSIZE;
if (get_ntp_packet(buf, &nbytes) != 0) {
LOGE(TAG, "construct ntp request errorr\t---------------------> aita_ntp_sync_time()\n");
close(sockfd);
return -1;
}
send(sockfd, buf, nbytes, 0);
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
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\t---------------------> aita_ntp_sync_time()\n");
close(sockfd);
return -1;
}
aita_print_ntp((struct ntphdr *) buf);
gettimeofday(&recvtv, NULL);
offset = get_offset((struct ntphdr *)buf, &recvtv);
gettimeofday(&tv, NULL);
tv.tv_sec += (int)offset;
tv.tv_usec += offset - (int)offset;
if (settimeofday(&tv, NULL) != 0) {
LOGE(TAG, "set time error\t---------------------> aita_ntp_sync_time()\n");
close(sockfd);
return -1;
}
}
} else {
close(sockfd);
printf("select body error!\t---------------------> aita_ntp_sync_time()\n");
return -1;
}
close(sockfd);
printf("whole done!\t---------------------> aita_ntp_sync_time()\n");
return 0;
}
案例基于“Hello World Demo”,init.c中定义相关初始化和功能函数,相较前几篇代码变动较多,这里直接给出全部代码。
#include <stdbool.h>
#include <stdlib.h>
#include <aos/aos.h>
#include <aos/cli.h>
#include <aos/kv.h>
#include <yoc/yoc.h>
#include <yoc/partition.h>
#include <yoc/netmgr.h>
#include <yoc/netmgr_service.h>
#include <devices/devicelist.h>
#include <devices/w800.h>
#include <uservice/eventid.h>
#include <ntp.h>
#include "board.h"
#include "app_init.h"
#include "drv/pin.h"
#include "drv/rtc.h"
#include "../w800_api.h"
const char *TAG = "INIT";
#ifndef CONSOLE_UART_IDX
#define CONSOLE_UART_IDX 0
#endif
/* --------------------by AITA Mr.Fei, global variables--------------------- */
void board_yoc_init()
{
board_init();
event_service_init(NULL); //initialize event service for network events
console_init(CONSOLE_UART_IDX, 115200, 128);
ulog_init();
aos_set_log_level(AOS_LL_DEBUG);
LOGI(TAG, "Build:%s,%s\n",__DATE__, __TIME__);
aita_InitRTC();
/* load partition & init kv function */
aita_InitKV();
/* set cycle timer for polling job */
aita_InitTimer();
/* initialize wifi network */
aita_InitNetwork();
board_cli_init();
}
/* --------------------by AITA Mr.Fei, key-value functions------------------ */
//project's global variables
#define ENDIAN_LIMIT 32 //endian counts limit
char GID[9]; //gateway id
char EID[ENDIAN_LIMIT][9];//endians' id
int endian_count = 1; //actually endian counts
int period = 10; //polling period -- minutes
int timeout = 15; //timeout of waiting USR220 resp -- seconds
int repeat = 3; //repeat times when timeout
//Init partition & kv function
int aita_InitKV(void) {
char buf[10] = {0};
int ret = -1;
ret = partition_init();
if (ret > 0) {
LOGI(TAG, "find %d partitions\n", ret);
} else {
LOGE(TAG, "partition init failed\n");
return ret;
}
ret = aos_kv_init("kv");
if(ret == 0) {
LOGE(TAG, "kv init done\n");
} else {
LOGE(TAG, "kv init failed\n");
return -1;
}
ret = aos_kv_getstring("endian_count", &buf, 10);
if(ret > 0) {
endian_count = atoi(buf);
endian_count = (endian_count<ENDIAN_LIMIT) ? endian_count : ENDIAN_LIMIT;
printf("endian_count: %d\n", endian_count);
char eid[5] = "EIDx";
for(int i=0; i<endian_count; i++) {
eid[3] = i+0x30;
aos_kv_getstring(eid, EID[i], 9);
printf("EID No.%d : %s\n", i, EID[i]);
}
} else {
LOGE(TAG, "Endian Count load failed\n");
return ret;
}
ret = aos_kv_getstring("period", &buf, 10);
if(ret > 0) {
period = atoi(buf);
printf("period: %d\n", period);
} else {
LOGE(TAG, "period load failed\n");
return ret;
}
ret = aos_kv_getstring("timeout", &buf, 10);
if(ret > 0) {
timeout = atoi(buf);
printf("timeout: %d\n", timeout);
} else {
LOGE(TAG, "timeout load failed\n");
return ret;
}
ret = aos_kv_getstring("repeat", &buf, 10);
if(ret > 0) {
repeat = atoi(buf);
printf("repeat: %d\n", repeat);
} else {
LOGE(TAG, "repeat load failed\n");
return ret;
}
return 0;
}
/* --------------------by AITA Mr.Fei, timer functions---------------------- */
#define PERIOD_FACTOR 1000
aos_timer_t polling_timer;//timer for polling
//timer callback
void pollingTimerCb(void *arg1, void *arg2) {
aita_ShowRTC();
}
//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", period*PERIOD_FACTOR, 1, 0);
if(ret != 0) {
LOGE(TAG, "polling_timer create failed\n");
}
}
/* --------------------by AITA Mr.Fei, network functions-------------------- */
//newwork macros
#define WIFI_SSID "your ssid"
#define WIFI_PSW "you password"
#define GW_IP "192.168.31.77"
#define GW_PORT 1234
#define ONE_HTTP_IP "183.230.40.33"
#define ONE_HTTP_PORT 80
//netmgr_hdl_t actually is void *
netmgr_hdl_t app_netmgr_hdl; //net manager handler for w800
//network event callback function
static void network_event(uint32_t event_id, const void *param, void *context) {
switch(event_id) {
case EVENT_NETMGR_GOT_IP: {
LOGD(TAG, "EVENT_NETMGR_GOT_IP\n");
printf("start polling_timer. periodic: %d min\n", period);
aita_RequestNTP();
aos_timer_start(&polling_timer);
break;
}
case EVENT_NETMGR_NET_DISCON: {
LOGD(TAG, "EVENT_NETMGR_NET_DISCON\n");
printf("ready to restart wifi connection...\n");
netmgr_reset(app_netmgr_hdl, 30);
break;
}
}
}
//w800 input callback function
void aita_w800in_cb(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports) {
uint8_t* d;
d = (uint8_t *)data;
printf("linkid: %d\n", linkid);
printf("len: %d\n", len);
printf("remote_ip: %s\n", remote_ip);
printf("remote_port: %d\n", remote_ports);
printf("data:%s\n", d);
}
//network init: open w800,
void aita_InitNetwork(void) {
/*
* by AITA Mr.Fei
* w800 interface struct
*/
w800_wifi_param_t w800_param;
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;
/*
* by AITA Mr.Fei
* from w800_devops.c(drv_wifi_at_w800 pack),
* include w800_module_init(but there is no task, so create a new one) & driver_register.
*/
wifi_w800_register(NULL, &w800_param);
/*
* by AITA Mr.Fei
* register uservice for wifi,
* init netmgr_uservice struct then return the pointer
*/
app_netmgr_hdl = netmgr_dev_wifi_init();
if(app_netmgr_hdl) {
//init uservice for wifi
utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
netmgr_service_init(task);
//join AP, WIFI_SSID & WIFI_PSW both are Macro
netmgr_config_wifi(app_netmgr_hdl, WIFI_SSID, 10, WIFI_PSW, 10);
//init w800 AT parser
w800_module_init(task, &w800_param);
//start uservice
netmgr_start(app_netmgr_hdl);
//subscribe events which is managed by netmgr uservice
event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL);
event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
}
}
//tcp client test func
void aita_tcpclienttest(void) {
char buf[] = "hello from RVB2601 by W800\n";
int ret = -1;
ret = w800_connect_remote(0, NET_TYPE_TCP_CLIENT, GW_IP, GW_PORT);
if(ret < 0) return;
int stat;
w800_get_status(&stat); //[ 16.360]<D>w800_api link status 2 已获取到ip
LOGD(TAG, "connect status: %d\n", stat);
w800_send_data(buf, sizeof(buf), 3000);
}
//ntp test func
void aita_RequestNTP(void) {
int ret = -1;
for(int i = 0; i < 2; i++) {
ret = aita_ntp_sync_time("cn.pool.ntp.org");
if (ret == 0) {
LOGD(TAG, "sync success\n");
break;
}
}
if(ret < 0) {
LOGE(TAG, "sync error\n");
}
//register input event callback for w800 module
w800_packet_input_cb_register(&aita_w800in_cb);
}
/* --------------------by AITA Mr.Fei, rtc functions------------------------ */
csi_rtc_t aita_rtc;
void aita_InitRTC(void) {
csi_error_t ret;
ret = csi_rtc_init(&aita_rtc, 0);
if(ret == CSI_OK) printf("initialize RTC done!\n");
else printf("initialize RTC return: %d\n", ret);
}
void aita_ShowRTC(void) {
csi_error_t ret;
char tbuf[64];
ret = csi_rtc_get_time(&aita_rtc, &(aita_dt.rtc_tm.rtc));
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", &(aita_dt.libc_tm));
printf("show aita datetime in RTC: %s\n", tbuf);
}
图8-5 RTC应用效果
本帖最后由 sonicfirr 于 2022-4-16 10:22 编辑
好想借你的NTP用一下,这项目整得没想法了!可否
引用: lugl4313820 发表于 2022-4-16 11:16 好想借你的NTP用一下,这项目整得没想法了!可否
别客气,咱就是为了开源来的
引用: UUC 发表于 2022-4-16 19:13 有点疑问:图8-3 文档中的csi_rtc_set_time()描述 tm_mon 取值范围[0,11],为何不是[1,12] ...
有些RTC寄存器就是这么设计的,具体为什么得问官方了。不过这不影响RTC的使用。
引用: UUC 发表于 2022-4-16 19:13 有点疑问:图8-3 文档中的csi_rtc_set_time()描述 tm_mon 取值范围[0,11],为何不是[1,12] ...
我想这就是一种习惯吧,从0开始编号,C标准库中时间结构体struct tm也是从0编号月份。