STC8H系列—18.接入Ntp服务同步网络时间教程

一、概述:

   C51内核的单片机接入NTP服务器,通过网络授时时钟,目的是让电子时钟与网络时间同步。在B站看到有同步网络时间的电子钟,便萌发了通过网络授时解决电子时间调节不方便,走时误差大的问题。ESP01s接入网络,使用UPD方式接入阿里云的NTP服务器(起初接入的是北京市中国教育和科研计算机网骨干网,未知原因授时有偶有1分钟的误差,有时无返回值,后来改成阿里云,自然解决),NTP服务器返回Unix时间戳,通过STC8H的解码计算得到日期与时间。过程其中的Unix时间戳的计算花销用时较长,本身算法不难,不好解决的是c51这个8位的单片机内核用来处理32位字长计算确实难为它了,解决的办法把32的数据分组成4个8位的数组,放入至数组中然后运用小学数学老师教的运算方法,计算出最终结果。

二、编程简述:

   1、Esp01s接入ntp

模块的接入参考了https://www.cnblogs.com/Luad/p/10652644.html使用AT指令获取网络时间,其中实现原理大概是使用AT指令运用UDP方式接入ntp服务器,端口是123,然后向服务发送48位hex码,服务器响应返回58位的hex码,文中提及的4位unix戳码为40-43,实际上51-54位。返回的时间戳与标准的时间戳有一定的区别,区别在于起点ntp自1900开始,标准时间戳自1970开始,计算方法将之间的秒差减去,标准时间戳的计算年月日的方法大体上是将年月日分为4年一组进行取模和整除,取模的后第三个年份为闰年,先将整年整年的减去,秒值不足一年了加上年整除值的4倍即得到年,剩下的,取月的方法为一月一月减,减去时按月的天数来,闰年时需要补偿一天时间,天数不足时即为本月,剩下的天数即为日。小时的计算除3600再与24取模,最终结果加上时区8,分钟的计算除60再除模60,秒钟的计算直接模60。设定一个定时器0,每1秒钟刷新一下,将计时显示在0LED上,第4分钟向服务器获取一次时间,用来纠正定时器的计时误差。

三、实验平台搭建:

   1、MCU:STC-打狗棒系列核心实验板 V2.3

   2、实验板平台:德飞莱LY-51s

   3、ESP-01s模块、0.96寸OLED 4脚IIC接口

   4、硬件连接表: 

          ESP-01s接线表

        3V3---------->3.3V

        GND---------->GND

        TX---------->P36

         RX---------->P37

         指示灯接线表

         LED1---------->P01(执行器LED灯模拟)

        LED2---------->P02(通信指示)

         0.96寸4脚OLED接线图硬I2C接线表

         SDL---------->P24

         CLK---------->P25

         VCC---------->+5V

         GND---------->GND

四、测试源代码:

//main.c
#include <STC8H.h>
#include "intrins.h"
#include <stdio.h>
#include "UnixToBeijing.h"
#include "Uart.h"
#include "OLED.h"

sbit Led1=P0^0;
sbit Led2=P0^1;
sbit Led3=P0^2;
sbit Led4=P0^3;
unsigned int count=0;
  unsigned char xdata a[4]={0x00,0x00,0x00,0x00};//ntp服务器Unix格式
  unsigned char code b[4]={0x83,0xAA,0x7E,0x80};
  unsigned char xdata c[4]={0x00,0x00,0x00,0x00};//标准Unix格式
  unsigned int xdata d[6]={0x00,0x00,0x00,0x00,0x00,0x00};
	unsigned char i;	
	  unsigned int oled_y=0,oled_mo=0,oled_d=0,oled_h=0,oled_m=0,oled_s=0;
	  unsigned char idata oledBuf[8]="";

/******************AT指令***********************/
u8 code s1[]="AT+CIPCLOSE\r\n";
u8 code s2[]="AT+RST\r\n";
u8 code s3[]="AT+CIFSR\r\n";
u8 code s4[]="AT+CIPMUX=0\r\n";
u8 code s5[]="AT+CIPSTART=\"UDP\",\"ntp2.aliyun.com\",123\r\n";
u8 code s6[]="AT+CIPMODE=1\r\n";
u8 code s7[]="AT+CIPSEND=48\r\n";
u8 code s8[]={0xE3,0x00,0x06,0xEC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,             0x31,0x4E,0x31,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,              0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 
u8 code k1[]="0.0.0.0";
u8 code k2[]="CONNECT";
u8 code k3[]=">";
u8 code k4[]="apitag";
u8 code k5[]="ta\":0,";
u8 code k6[]="ta\":1,";
u8 code k7[]="WIFI GOT IP";

void init_IO();//初始化IO
void DispTime(unsigned int d[]);//显示当前时间
bit Findkey(u8 key[],u8 len);
void GetWifiNtp();
void GetDate();	
void init_timer0();//定时器0的初始化							
void main()
{
		
	  P_SW2 |= 0x80;  //扩展寄存器XFR访问使能	
		init_IO();
		init_Uart1();
		init_Uart2();	
    EA=1;			
	  printf("STC8H UnixTime Test!\n");
	  init_timer0();
	  init_IIC();//初始化硬IIC
	  OLED_Init();//初始化OLED
	  // sprintf((char *)oledBuf ,"Length:mm");//格式化输出
	  OLED_ShowString(0,0," MG LeiYang!");
	 // OLED_ShowString(0,4,oledBuf);
	  OLED_ShowString(0,2,"Get Ntp Time!");
	  //OLED_ShowString(0,6,"   2023/08/09");
		
//	  U32sub(a,b,c);
	  //c[0]=0x64;c[1]=0xe7;c[2]=0x91;c[3]=0xde;
//	  UnixToBeiJinTime(c,d);
//	  DispTime(d);
    ET0=0;
    GetWifiNtp();	
    ET0=1;    
	while(1)
	 {	
 	   if(count>=40000)
		 {
		  count=0;
			ET0=0;
    	GetDate();
      ET0=1;
		 }
	   
	}				

}

void DispTime(unsigned int d[])
{
  	printf("%02d-",d[0]);
	  printf("%02d-",d[1]);
	  printf("%02d  ",d[2]);
	  printf("%02d:",d[3]);
	  printf("%02d:",d[4]);
	  printf("%02d",d[5]);		
		printf("\n");
}


void init_IO()
{
	RSTCFG=0x50;  //开启RST键进入ISP模式
	P0M1 = 0x00;   P0M0 = 0x00;   //设置P0口为准双向口
	P1M1 = 0x00;   P1M0 = 0x00;   //设置P0口为准双向口
	P2M1 = 0x00;   P2M0 = 0x00;   //设置P1口为准双向口
	P3M1 = 0x00;   P3M0 = 0x00;   //设置P3口为准双向口
	P4M1 = 0x00;   P4M0 = 0x00;   //设置P4口为准双向口
	P5M1 = 0x00;   P5M0 = 0x00;   //设置P5口为准双向口
		
}

void init_timer0()//定时器0的初始化10ms
{
	
		AUXR &= 0x7F;		//定时器时钟12T模式
	  TMOD &= 0xF0;		//设置定时器模式
	  TL0 = 0x33;		//设置定时初始值
	  TH0 = 0xE3;		//设置定时初始值
	  TF0 = 0;		//清除TF0标志
	  TR0 = 1;		//定时器0开始计时
	  	
	ET0=1;//Timer0 开中断	

}

void GetWifiNtp() //wifi连接上线
{
	unsigned char i;
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k7,10)==0);
	SendToEspStr(s3);
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k1,6))
	{
	   SendToEspStr(s3);
		 Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
     RX1_Buffer[RX1_Cnt]='\0';     
	}	
  SendToEspStr(s4);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  SendToEspStr(s5);	
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k2,6))
	{
	   SendToEspStr(s5);
		 Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
     RX1_Buffer[RX1_Cnt]='\0';     
	}
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  
 	SendToEspStr(s7);	
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k3,1))
	{
	   SendToEspStr(s7);
		 Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
     RX1_Buffer[RX1_Cnt]='\0';     
	}
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	RX1_Cnt=0x00;
	for(i=0;i<48;i++)
	{
		Uart1Send(s8[i]);
	}
	//SendToEspStr(s8);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
   for(i=78;i<82;i++)
  {
			//printf("0x%hhx ",RX1_Buffer[i]);
		  a[i-78]=RX1_Buffer[i];
	}	
	
	Led2=0;	
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  U32sub(a,b,c); 
	UnixToBeiJinTime(c,d);
	DispTime(d);	
	oled_y=d[0];oled_mo=d[1];oled_d=d[2];
	oled_h=d[3];oled_m=d[4];oled_s=d[5];
	SendToEspStr(s1);	
	Led2=1;
}

void GetDate()
{
	unsigned char i;
 SendToEspStr(s5);	
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k2,6))
	{
	   SendToEspStr(s5);
		 Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
     RX1_Buffer[RX1_Cnt]='\0';     
	}
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  
 	SendToEspStr(s7);	
	RX1_Buffer[RX1_Cnt]='\0';
	while(Findkey(k3,1))
	{
	   SendToEspStr(s7);
		 Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
     RX1_Buffer[RX1_Cnt]='\0';     
	}
	Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	RX1_Cnt=0x00;
	for(i=0;i<48;i++)
	{
		Uart1Send(s8[i]);
	}
	//SendToEspStr(s8);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
   for(i=78;i<82;i++)
  {
			//printf("0x%hhx ",RX1_Buffer[i]);
		  a[i-78]=RX1_Buffer[i];
	}	
	
	Led2=0;	
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
  Delay1ms(200);Delay1ms(200);Delay1ms(200);Delay1ms(200);
	 U32sub(a,b,c); 
	UnixToBeiJinTime(c,d);
	DispTime(d);	
	oled_y=d[0];oled_mo=d[1];oled_d=d[2];
	oled_h=d[3];oled_m=d[4];oled_s=d[5];
  SendToEspStr(s1);
   
	Led2=1;
}

//接收到的字符串,从中查找关键字
bit Findkey(u8 key[],u8 len)
{
   u8 i=0,j=0;
	 bit result=0;	
	for(i=0;i<RX1_Cnt;i++)
	  {
		 if(RX1_Buffer[i]==key[0])
			{
			   for(j=1;j<len;j++)
				  {
					  if(RX1_Buffer[i+j]!=key[j])
							break;
						if(j==len-1) result=1;
					}
			 }				
     				
		}
		return result;
}


void Timer0_isr() interrupt 1//Timer0中断入口
{
		count++;
	
	if(count%50==0)
		Led1=~Led1;
	
	if(count%100==0)
		{
			oled_s++;
		  if(oled_s>=60)
			{
			   oled_s=0;
				 oled_m++;
			}
			if(oled_m>=60)
			{
			   oled_m=0;
				 oled_h++;
			}
			if(oled_h>=24)
			{
			   oled_h=0;
				 oled_d++;
			}
			    //sprintf((char *)oledBuf ,"        ");//格式化输出
			    sprintf((char *)oledBuf ,"  %02d-%02d-%02d",oled_y,oled_mo,oled_d);
			     OLED_ShowString(0,4,oledBuf);
			     sprintf((char *)oledBuf ,"    %02d:%02d:%02d",oled_h,oled_m,oled_s);
			     OLED_ShowString(0,6,oledBuf);
			    //sprintf((char *)oledBuf ,"        ");//格式化输出
			 
		}
		
}
//C51计算unix时间戳
unsigned char U32sub(unsigned char a[],unsigned char b[],unsigned char c[]);//减法a-b>0 返回1,否则返回0,值存c
unsigned char U32add(unsigned char a[],unsigned char b[],unsigned char d[]);//加法a+b无溢出返回1,否则返回0,值存d
void U32Mul(unsigned char a[],unsigned char b,unsigned char c[]);//乘法
void U32Mul1(unsigned char a[],unsigned char b[],unsigned char c[]);//乘法
char U32Cmp(unsigned char a[],unsigned char b[]);//比较大小返回1,否则返回0,值存d
void U32Div(unsigned char a[],unsigned char b[],unsigned char c[]);//除法
void U32Div1(unsigned char a[],unsigned char b,unsigned char c[]);//除法,除数为u8
void U32Mod(unsigned char a[],unsigned char b,unsigned char c[]);//求余数
void U32ModMon(unsigned char a1[],unsigned char c1[]);//求固定1421*24的余数
void UnixToBeiJinTime(unsigned char a1[],unsigned int c1[]);//Unix时间转北京时间


void UnixToBeiJinTime(unsigned char c[],unsigned int c1[])
{
    unsigned char i=0,flag=0;
	   unsigned int time=0,hours_per_year=0,Pass4year=0;
	   char code Days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};		 
     unsigned int month=0;//月
     unsigned int days=0;//日
     unsigned int hour=0;//小时
     unsigned int minute=0;//分钟
     unsigned int second=0;//分钟
		 unsigned int year=0;//年
		 unsigned char idata d[4]={0x01,0xE1,0x84,0x80};
     unsigned char idata e[4]={0x00,0x00,0x00,0x00};
     unsigned char idata f[4]={0x00,0x00,0x00,0x00};		 
   	unsigned char idata a[4]={0x00,0x00,0x00,0x00};
    	
	    
		 
		 d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;	
	  U32Div1(c,60,d);
	  U32Div1(d,60,e);
	   d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;	
	  U32Mod(e,24,d);
	  hour=d[3]+8;
	  hour=hour%24;
	   d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;
	   e[0]=0x00;e[1]=0x00;e[2]=0x00;e[3]=0x00;
	  U32Div1(c,60,e);
	  U32Mod(e,60,d);
	  minute=d[3];
		 d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;
		 U32Mod(c,60,d);
		 second=d[3];
		 
		 U32Div1(c,60,d);
	    U32Div1(d,60,a); // time/3600的值
		 d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x01;
		  U32sub(a,d,f);//time-1
			e[0]=0x00;e[1]=0x00;e[2]=0x00;e[3]=0x00;
	     U32Div1(f,162,e);
			 d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;
	     U32Div1(e,216,d);
			 //顺便计算好年
	     year=d[3];
	     year=year*4;
	    
	 d[0]=0x00;d[1]=0x00;d[2]=0x00;d[3]=0x00;
	 U32ModMon(c,d);	
	 time=d[2]*256+d[3];
	 
	 year=year+1970;
	 for (i=1;i<4;i++)
	{
		//一年的小时数
		hours_per_year = 365 * 24;
		//判断闰年
		if(i==3)
		{
			//是闰年,一年则多24小时,即一天
			hours_per_year += 24;
		}
		flag=i;
		if(time < hours_per_year)
		{			
			break;
		}
    year++;		
		time -= hours_per_year;
	}
	
   time=time/24;//剩下的天数
	  time++;
	//校正闰年的误差,计算月份,日期
	if((year & 3) == 0)
	{
		if(time > 60)
		{
			time--;
		}
		else
		{
			if(time == 60)
			{
				days = 29;
				return ;
			}
		}
	}

	//计算月日
	for(i = 0; Days[i] < time; i++)
	{
		if((i==1)&&(flag==3))
			time-=29;
		else
		  time -= Days[i];
		
	}
   month=i+1;
	days = (int)(time);	
	c1[0]=year;c1[1]=month;c1[2]=days;c1[3]=hour;c1[4]=minute;c1[5]=second;	

}

猜你喜欢

转载自blog.csdn.net/qq_21082985/article/details/132512205
今日推荐