本帖最后由 麦田稻草 于 2016-8-11 09:27 编辑
分享一下之前项目用到的同步网络时间的程序给大家,本例需要用到socket,要操作系统的支持,大家可以在 探索者F4 资料盘(A盘)4,程序源码3,扩展例程2,LWIP扩展例程网络实验10 NETCONN_WEBserver实验 的基础下测试。
rtc.h文件增加了如下结构体:
struct rtc_time
{
u16 year;
u8 month;
u8 day;
u8 week;
u8 hour;
u8 min;
u8 sec;
u8 ampm;
};
函数 void RTC_Get_Date(u8 *year,u8 *month,u8 *date,u8 *week);
改为 void RTC_Get_Date(u16 *year,u8 *month,u8 *date,u8 *week);
这些可以看着办,我主要是方便使用。
以下为代码,实现获取sntp服务器的时间,并同步更新本地时间(说明一下,我对时间精度要求不高,所以直接用服务器向客户发时间戳的时间来更新本地时间,经测试,误差小于一秒,这主要看网络状态和服务器的位置,我用的是深圳阿里云,我在广州):
[mw_shl_code=c,true]#include <string.h>
#include <time.h>
#include "lwip/sockets.h"
#include "lwip/inet.h"
#include <netdb.h>
#include "sntp.h"
#include "rtc.h"
#define bzero(a, b) memset(a, 0, b)
#define JAN_1970 2208988676UL+28701 /* 1970 - 1900 in seconds */
#define NTP_SERVER_NAME "120.24.166.46" //深圳阿里云
#define NTP_PORT 123
typedef struct
{
unsigned char LiVnMode;
unsigned char Stratum;
unsigned char Poll;
unsigned char Precision;
long int RootDelay;
long int RootDispersion;
char RefID[4];
long int RefTimeInt;
long int RefTimeFraction;
long int OriTimeInt;
long int OriTimeFraction;
long int RecvTimeInt;
long int RecvTimeFraction;
long int TranTimeInt;
long int TranTimeFraction;
}STNP_Header;
//获取sntp服务器的时间
int GetNTPTime(STNP_Header *H_SNTP)
{
int sockfd=0;
struct sockaddr_in server;
fd_set set;
struct timeval timeout;
bzero((void*)H_SNTP, sizeof(STNP_Header)); //清零
H_SNTP->LiVnMode = 0x1b; //设置NTP报文格式,LI=0:无警告; VN=3:NTP的版本号为3; Mode=3:客户;
//设置要连接的对方的IP地址和端口等属性
bzero(&server,sizeof(server));//初始化结构体
server.sin_family = AF_INET; //设置地址家族
server.sin_port = htons(NTP_PORT); //设置端口
if(inet_aton(NTP_SERVER_NAME,&server.sin_addr) <= 0)//设置地址
printf("inet_pton error
");
sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建一个UDP socket
if(sockfd<0)
{
printf("sockfd error!
");
return -1;
}
//发送数据
if(sendto(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0, (struct sockaddr*)&server, sizeof(server))<0)
{
printf("sendto error!
");
return -1;
}
FD_ZERO(&set); //清除描述词组set的全部位
FD_SET(sockfd, &set);//设置描述词组set中相关fd的位
timeout.tv_sec = 5; //等待超时时间
timeout.tv_usec = 0;
if( select(sockfd+1, &set, NULL, NULL, &timeout) <= 0 )
{
printf("select wait timeout!
");
return -1;
}
if(recv(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0)<0)
{
printf("recv error!
");
return -1;
}
close(sockfd);
return 0;
}
//同步时间
//返回0:成功,-1:失败
int SYNC_Time(void)
{
STNP_Header HeaderSNTP;
time_t t1;
struct timeval tv;
struct rtc_time st;
struct tm *time;
//获取sntp服务器数据
printf("sync time from %s
",NTP_SERVER_NAME);
if(GetNTPTime(&HeaderSNTP)<0)
return -1;
//从1900年1月1号0时0分0秒到服务器向客户发时间戳的时间,单位:秒
t1 = ntohl(HeaderSNTP.TranTimeInt);
printf("t1:%u
",t1);
//减去1900年至1970年的时间
tv.tv_sec=t1-JAN_1970;
printf("UTC:%s
", ctime((const unsigned int *)&tv.tv_sec));
//把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为日历时间
//获得的tm结构体的时间,是已经进行过时区转化为本地时间,年份加上1900,月份加上1
time=localtime((const unsigned int *)&tv.tv_sec);
printf("sntp:%u-%u-%u,%u:%u:%u,星期%d
",(time->tm_year)+1900,(time->tm_mon)+1,time->tm_mday,time->tm_hour,time->tm_min,time->tm_sec,time->tm_wday);
//获取rtc时间
RTC_Get_Time(&st.hour,&st.min,&st.sec,&st.ampm);
RTC_Get_Date(&st.year,&st.month,&st.day,&st.week);
st.year=st.year+2000;
printf("rtc1:%d-%d-%d,%d:%d:%d
",st.year,st.month,st.day,st.hour,st.min,st.sec);
//时间相差超过1分钟就更新时间
if(st.year!=(time->tm_year)+1900 | st.month!=(time->tm_mon)+1 | st.day!=time->tm_mday)
{
RTC_Set_Date((time->tm_year)+1900-2000,(time->tm_mon)+1,time->tm_mday,time->tm_wday);
printf("RTC set date!
");
}
if(st.hour!=time->tm_hour | st.min!=time->tm_min)
{
if(time->tm_hour<12) st.ampm=0;
else st.ampm=1;
RTC_Set_Time(time->tm_hour,time->tm_min,time->tm_sec,st.ampm);
printf("RTC set time!
");
}
RTC_Get_Time(&st.hour,&st.min,&st.sec,&st.ampm);
RTC_Get_Date(&st.year,&st.month,&st.day,&st.week);
st.year=st.year+2000;
printf("rtc2:%d-%d-%d,%d:%d:%d
",st.year,st.month,st.day,st.hour,st.min,st.sec);
return 0;
}
[/mw_shl_code]
断电后上电的时候没接网线,你是怎么能跳到sntp的函数里去的?原子哥的例程main函数里有初始化网卡的函数:
while(lwip_comm_init()) //lwip初始化
{
LCD_ShowString(30,110,200,20,16,"Lwip Init failed!"); //lwip初始化失败
delay_ms(500);
LCD_Fill(30,110,230,130,WHITE);
delay_ms(500);
}
网线没接不可能通过这里,将一直死循环在这里,你是直接用f407跑那个例程吗?
等待时间超时而退出去导致没有执行close(sockfd);,这条语句在我上传的例程里已经修改过来了,贴出来的那些代码是测试的时候的代码。
原子哥的例程里有个问题就是:上电的时候没有接网线,过一段时间再接上去也会一直卡死在初始化网卡那里
while(lwip_comm_init()) //lwip初始化
{
LCD_ShowString(30,110,200,20,16,"Lwip Init failed!"); //lwip初始化失败
delay_ms(500);
LCD_Fill(30,110,230,130,WHITE);
delay_ms(500);
}
原因是u8 lwip_comm_init(void)函数里的以下3句:
if(ETH_Mem_Malloc())return 1; //内存申请失败
if(lwip_comm_mem_malloc())return 1; //内存申请失败
if(LAN8720_Init())return 2; //初始化LAN8720失败
初始化LAN8720失败时没有释放前面2个函数申请的内存导致不断申请内存而内存不足失败。
一周热门 更多>