NTP是IOT MCU中常用的功能, RVB2601也提供了NTP的package供使用。由于SDK的完善性还存在一定问题,7.4.3的NTP和系统RTC模块之间的衔接会导致卡死之类的现象。
其实,由于2601开发板的RTC并没有自带电源,因此断电后, RTC无法保存时间。所以,这个RTC在使用中存在一定限制。 同时SDK的一些缺陷也给NTP和GETTIME类的API带来了麻烦。 因NTP的实际精度本来就受网络条件的影响,精度范围在几十ms到500ms之间(参见NTP精度的介绍),故此,对于日常应用(例如电子钟等)而言,一个秒级的时钟已经足够使用了。以下介绍一个秒级NTP的快速实现代码,供坛友参考。
int mbre_simple_ntp_proc(char *server, int32_t *totalSeconds)
{
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);
//MBRE初始化返回值,错误情况下totaoSeconds值为0
if(!totalSeconds) return -1;
*totalSeconds = 0;
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);
}
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;
}
nbytes = BUFSIZE;
if (get_ntp_packet(buf, &nbytes) != 0) {
LOGE(TAG, "construct ntp request errorr");
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");
close(sockfd);
return -1;
}
if(nbytes < 43) return -1;
//MBRE简单获取中国(8时区)的NTP秒数(自1900开始)
{
unsigned long secsSince1900, u1, u2;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)buf[40] << 24;
secsSince1900 |= (unsigned long)buf[41] << 16;
secsSince1900 |= (unsigned long)buf[42] << 8;
secsSince1900 |= (unsigned long)buf[43];
//减去1900秒数起始;同时加上了8时区的秒数偏移
*totalSeconds = secsSince1900 - 2208988800UL + 8 * 3600UL;
}
} else {
LOGE(TAG, "select timeout");
return -1;
}
} else {
LOGE(TAG, "FD_ISSET");
close(sockfd);
return -1;
}
close(sockfd);
return 0;
}
//C文件中的引用处extern说明
extern int mbre_simple_ntp_proc(char *server, int32_t *totalSeconds);
...
int32_t totalSeconds = 0;
int32_t startMicroSecs = 0;
int rtn;
//调用函数,返回成功后,totalSeconds存放的是自1900年起的秒数值
rtn = mbre_simple_ntp_proc("ntp1.aliyun.com", &totalSeconds);
//记下获取ntp的毫秒时间
startMicroSecs = csi_tick_get_ms();
if(0 == rtn) printf("从1900年起算的秒数 = %d\r\n", totalSeconds);
else printf("NTP失败,错误码 %d\r\n", rtn);
...
//在任何需要获取当前时间的地方, 只需要根据当前系统ms数,计算出与获取ntp的偏移即可得到有效秒数
int32_t currentMicroSecs = csi_tick_get_ms();
int32_t currentSecsSince1900 = totalSeconds + (currentMicroSecs - startMicroSecs)/1000;
以下给出arduino使用的time库里头的相关处理代码,很容易计算得到年,月,日,小时,分钟等数据
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
} tmElements_t, TimeElements, *tmElementsPtr_t;
/* 从系统时间转换为年、月、日、时、分、秒的实际代码 */
// 润年润月计算
#define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) )
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
void breakTime(time_t timeInput, tmElements_t &tm){
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
unsigned long days;
time = (uint32_t)timeInput;
tm.Second = time % 60;
time /= 60; // now it is minutes
tm.Minute = time % 60;
time /= 60; // now it is hours
tm.Hour = time % 24;
time /= 24; // now it is days
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm.Year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days=0;
month=0;
monthLength=0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
} else {
break;
}
}
tm.Month = month + 1; // jan is month 1
tm.Day = time + 1; // day of month
}
在arduino的相关库中,提供了很多对时间操作,以及其他功能性开发的方便的现成库,如果铁头哥系列能够像国内IOT MCU其他家(如乐x)一样,提供arduino的适配库,那对于IOT APP开发将是一个非常重要的助力,毕竟如果只是简单连接传感器的话,很多芯片就足够了。 国内IOT MCU的明显优势就是更强的FLASH和RAM,在IOT这个应用为王的领域,有相对成熟扩展支撑,想必会对整个阿里MCU生态起到积极的推进作用。
确实,IOT,现在百花齐放,最近发现他家好多都是开源的
玄铁开源E906开源信息:
https://github.com/T-head-Semi/opene906?spm=a2cl5.25269445.0.0.5da5180fmPCxYb
YoC开源:
https://github.com/T-head-Semi/open-yoc?spm=a2cl5.25269445.0.0.5da5180fmPCxYb
玄铁工具链开源:
https://github.com/T-head-Semi/xuantie-gnu-toolchain?spm=a2cl5.25269445.0.0.5da5180fmPCxYb