实验任务
本实验为RT-Thread Nano 入门线上培训作业题目6:
实现RTC驱动,将NTP服务器中获取的网络时间更新至RTC。
实验目的
熟悉如何获取NTP服务器上的数据。
实验环境
1、硬件环境:野火STM32霸道开发板
2、软件环境:RT-Thread Nano 3.1.3,MDK 5.25
实验步骤
1、编写ESP8266驱动和RTC驱动,这部分的工作,开发板的固件库例程和扩展模块例程已经有了,因此直接拿来用就行。
2、编写获取NTP服务器时间的应用程序。
#include "config.h"
#include "ntp_example.h"
#define NTP_TIMESTAMP_DELTA 2208988800ull /* number of seconds between 1900 and 1970,1900-1970的时间差 */
#define SEC_TIME_ZONE + (8*60*60) /* Beijing,GMT+8, 时区差 */
#define TIME_SERVERIP "cn.ntp.org.cn" /* NTP服务器 */
#define TIME_PORTNUM "123" /* NTP服务器端口号,固定为123 */
int GetNtp(void)
{
uint8_t PosInNtpPacket;
uint8_t buf[48]; /* 存储NTP服务器返回的数据 */
uint32_t local_timestamp;
ntp_packet packet ;
struct tm * Net_time;
uint8_t NTP_Data[48]; /* 48字节的报文 */
u8 count = 4;
u8 id;
NTP_Data[0]=0xa3; /* NTP查询报文赋初值 */
for(PosInNtpPacket=1;PosInNtpPacket<48;PosInNtpPacket++)
NTP_Data[PosInNtpPacket]=0; /* 剩余的47字节为0 */
rt_kprintf ( "\r\nConfig ESP8266 ......\r\n" );
ESP8266_Link_Server ( enumUDP, TIME_SERVERIP, TIME_PORTNUM, Single_ID_0 ); /* 连接NTP服务器 */
ESP8266_UnvarnishSend (); /* 开启透传 */
rt_kprintf ( "\r\nConfig ESP8266 Over\r\n" );
strEsp8266_Fram_Record .InfBit .FramLength = 0;
rt_memset(strEsp8266_Fram_Record.Data_RX_BUF,0,RX_BUF_MAX_LEN); /* 清除接收缓存 */
for(PosInNtpPacket=0;PosInNtpPacket<48;PosInNtpPacket++) /* 发送NTP查询包 */
{
USART_SendData(USART3, (uint8_t)NTP_Data[PosInNtpPacket]);
while(USART_GetFlagStatus(USART3, USART_FLAG_TC)== RESET);
}
rt_thread_mdelay(1000);
rt_memcpy(buf,strEsp8266_Fram_Record.Data_RX_BUF,48); /* 拷贝数据包 */
printf("/*******************************/\r\n"); /* 接收包原始数据打印 */
printf("Receive NTP Packet (Hex):");
for(PosInNtpPacket=0;PosInNtpPacket<48;PosInNtpPacket++)
{
if(PosInNtpPacket%16==0)
{
printf("\r\n");
printf("%02X ",buf[PosInNtpPacket]);
}
else
printf("%02X ",buf[PosInNtpPacket]);
}
printf("\r\n/*******************************/\r\n");
ESP8266_ExitUnvarnishSend ( ); /* 退出透传 */
ESP8266_Close_Link ( ); /* 关闭TCP或UDP连接 */
if( packet.txTm_s == 0 )
return 0;
/* 由于本演示时间精度要求不高,故直接用服务器返回的时间作为对时的时间,并未用公式:时间差offset=((T2-T1)+(T3-T4))/2。而是用T3作为对时基准时间。*/
id = 40;
packet.txTm_s = buf[id+0]<<24 | buf[id+1]<<16 | buf[id+2]<<8 | buf[id+3];
local_timestamp = packet.txTm_s - NTP_TIMESTAMP_DELTA; /* 减去1970和1900的差值 */
local_timestamp +=SEC_TIME_ZONE; /* 加上北京的时间差,GMT+8 */
Net_time = localtime(&local_timestamp); /* 秒数转换位标准时间 */
printf("NTP Time:%04d-%02d-%02d %02d:%02d:%02d\r\n", /* 打印出时间 */
(Net_time->tm_year)+1900, (Net_time->tm_mon)+1,
Net_time->tm_mday, Net_time->tm_hour,
Net_time->tm_min, Net_time->tm_sec);
Set_Time(local_timestamp);
while(count--)
{
local_timestamp = Get_Time();
Net_time = localtime(&local_timestamp);
printf("NTP Time:%04d-%02d-%02d %02d:%02d:%02d\r\n", /* 打印出时间 */
(Net_time->tm_year)+1900, (Net_time->tm_mon)+1,
Net_time->tm_mday, Net_time->tm_hour,
Net_time->tm_min, Net_time->tm_sec);
rt_thread_mdelay(1000);
}
return 0;
}
MSH_CMD_EXPORT(GetNtp, Get BeiJing Time.);
3、编写设置RTC时间戳的函数和获取RTC时间戳的函数,这其实是bsp_rtc.c的内容。
void Set_Time(u32 timestamp)
{
/* 等待确保上一次操作完成 */
RTC_WaitForLastTask();
/* 将时间戳并写入到RTC计数寄存器 */
RTC_SetCounter(timestamp);
/* 等待确保上一次操作完成 */
RTC_WaitForLastTask();
}
u32 Get_Time(void)
{
return RTC_GetCounter();
}
4、编写通过msh获取RTC时间的应用程序。
int GetRTCTime(void)
{
uint32_t local_timestamp;
struct tm * Net_time; /* 48字节的报文 */
u8 count = 4;
while(count--)
{
local_timestamp = Get_Time();
Net_time = localtime(&local_timestamp);
printf("NTP Time:%04d-%02d-%02d %02d:%02d:%02d\r\n", /* 打印出时间 */
(Net_time->tm_year)+1900, (Net_time->tm_mon)+1,
Net_time->tm_mday, Net_time->tm_hour,
Net_time->tm_min, Net_time->tm_sec);
rt_thread_mdelay(1000);
}
return 0;
}
MSH_CMD_EXPORT(GetRTCTime, Get RTC Time.);