找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10080|回复: 7
收起左侧

STM32通过GSM模块获取网络时间并设置到单片机内部RTC中实现上电自动校准时间

  [复制链接]
ID:435174 发表于 2019-6-20 23:52 | 显示全部楼层 |阅读模式
        先简单说一下实验目的吧。平时做项目或做一些小作品的时候需要用到时间,时间用的是STM32内部的RTC,在精度要求不是特别高时这样省去接外设时钟模块,省时省力。但我们都知道,RTC在断电后数据是不保存的,也就是说如果没有电源如电池之类一直给后备寄存器供电的话数据是会丢失的,下次开机时时间就会恢复初始化时设置的那个时间,想要时间正确就要重新设置时间,这就很不实用。所以就想通过网络获取时间的方式来自动校正时间。又恰好用到SIM900A这个模块,所以查了下资料,发现已经有前辈做过了。看了https://blog.csdn.net/ludaoyi88/article/details/51757664这位博主的文章,获取时间部分用了这位前辈提供的代码,在他提供的代码上进行测试和改进(直接用不修改的话是不行的。如果大家仔细对比的话会发现其实我改进后的代码跟原版的还是有蛮多小细节不同的),最终得以达到目的,即可以通过服务器获取到网络的时间并自动校正到STM32内部RTC中,这里再次感谢 ludaoyi123这位前辈。大家可以先去看看这位前辈的博客,也就是上面那个链接,获取时间和处理时间数据都是源于他的那篇文章。好了,接下来就说一下怎么获取时间的吧。        此次实验用的单片机是STM32F103C8T6核心板,串口2控制SIM900A模块数据的收发,串口1用于在串口调试助手打印相关信息。下面是我的硬件平台:STM32F103C8T6核心板和SIM900A模块。

硬件平台

硬件平台

获取网络时间的第一种方法是连接到国际授时服务器,IP为:timenistgov,端口为:13,我用的连接方式是TCP连接。当客户端连接到此服务器后,服务器会立刻发送一串格式为“58646 19-06-12 16:05:36 50 0 0 668.3 UTC(NIST) * ”这样的字符串返回给客户端并主动断开连接。这一串字符串中就包含有日期和时间,我们所要做的,就是把相关的日期和时间提取出来转换成数字就好了,这也是最最重要的部分。这里贴出我修改后的代码:`
  1. _nowtime_obj NowTime;        //现在时间日期结构体
  2. ///*******************************************************************************
  3. //* 函数名 : Get_Sever_Time
  4. //* 描述   : 获取Time信息(连接服务器成功情况下)
  5. //* 输入   :
  6. //* 输出   :
  7. //* 返回   :
  8. //* 注意   :服务器返回的数据形式如下:58646 19-06-12 16:05:36 50 0 0 668.3 UTC(NIST) *
  9. //*******************************************************************************/
  10. void Get_Sever_Time(void)
  11. {
  12.     u8 i =0;
  13.     char timestr1[200]={0};
  14.     char *timestr = timestr1;//指向timestr1地址
  15.     printf("\r\n获取时间日期中...\r\n");
  16.     while(1)
  17.     {            
  18.         if(strstr((const char*)USART2_RX_BUF , "-") != NULL || strstr((const char*)USART2_RX_BUF , "5") != NULL )
  19.         {
  20.             timestr = strstr((const char*)USART2_RX_BUF,"5");//58646//USART2_RX_BUF 为接收缓存数组
  21.             break;
  22.         }
  23.          
  24.     }

  25.     printf("\r\n时间数组timestr的数据为");
  26.     printf((char *)timestr);   
  27.     printf("\r\n");
  28.     delay_ms(5);
  29.     //提取UTC世界时间
  30.     for(i = 0 ; i <50 ; i++)
  31.     {
  32.         if(timestr[i] == '-')
  33.         {
  34.             NowTime.year = (timestr[i-2]-'0')*10 + (timestr[i-1]-'0') + 2000;
  35.             NowTime.moon = (timestr[i+1]-'0')*10 + (timestr[i+2]-'0');
  36.             NowTime.day  = (timestr[i+4]-'0')*10 + (timestr[i+5]-'0');
  37.             NowTime.hour = (timestr[i+7]-'0')*10 + (timestr[i+8]-'0');//时差相差8
  38.             if(NowTime.hour >= 16)//北京时间新的一天
  39.             {
  40.                 NowTime.hour = (timestr[i+7]-'0')*10 + (timestr[i+8]-'0') + 8 - 24;
  41.                 NowTime.day = NowTime.day + 1;
  42.                 if(NowTime.hour == 24)
  43.                     NowTime.hour = 0;
  44.             }
  45.             
  46.             else
  47.                 NowTime.hour = (timestr[i+7]-'0')*10 + (timestr[i+8]-'0') + 8 ;//时差相差8
  48.             
  49.             NowTime.minu = (timestr[i+10]-'0')*10 + (timestr[i+11]-'0');
  50.             NowTime.sec  = (timestr[i+13]-'0')*10 + (timestr[i+14]-'0') + 2;//2是返回数据到处理处结果的误差

  51.             sprintf((char*)TimeRTC,"AT+CCLK=\"%d/%02d/%02d,%02d:%02d:%02d+08\"\r\n",NowTime.year,NowTime.moon,NowTime.day,NowTime.hour,NowTime.minu,NowTime.sec);            
  52.             //转换成RTC时间格式           
  53.             break;
  54.         }
  55.     }

  56.    
  57.     printf("\r\n当前时间:%d年%02d月%02d日%02d时%02d分%02d秒\r\n",NowTime.year,NowTime.moon,NowTime.day,NowTime.hour,NowTime.minu,NowTime.sec);
  58.    
  59.     printf("\r\n写入SIM900A设置时间的字符串为:");
  60.     printf((char *)TimeRTC);
  61.     printf("\r\n");
  62.     if(NowTime.year!=0&&NowTime.moon!=0&&NowTime.day!=0&&NowTime.hour!=0&&NowTime.minu!=0)//获取到正确数据
  63.     {
  64.         RTC_Set(NowTime.year,NowTime.moon,NowTime.day,NowTime.hour,NowTime.minu,NowTime.sec);  //设置STM32单片机内部RTC时间
  65.         Set_SIM900A_RTCtime();//给GSM模块设置从网络获取来的时间
  66.     }
  67.     else
  68.         printf("\r\n没能在服务器获取到正确时间,复位重试一下\r\n");
  69.    
  70.     delay_ms(500);
  71.     Get_GSM_RTCtime();//用串口查看一下GSM模块时间正常了没有
  72.     sim900a_send_cmd("AT+CIPSHUT\r\n","SHUT OK",2); //关闭连接
  73.     delay_ms(300);
  74.     AT_DataInit();//清除接收数组
  75.     CLR_Buf2();
  76.     delay_ms(200);

  77. }
复制代码
代码内容很简单,就是连接上服务器后用strstr函数和字符“-”或者“5”比较串口2接收数组中的“58646 19-06-12 16:05:36 50 0 0 668.3 UTC(NIST) * ”这串字符串,如果串口二接收数组中接收到了这一串字符串,那个就将这串字符串保存到数组timestr中并跳出死循环。实验过程中发现最容易出现问题的就是这一块,也就是有时候串口2没能收到完整的字符串导致程序死在死循环里面,应该是串口2中断服务函数没写好。接到完整的字符串后,接下来接着把年月日时分秒提取出来,把字符变成数字并做判断转换成北京时间,当服务器中的小时大于或等于16时当地小时+8-24,且天数+1,否则小时只是单纯地+8,这个8就是时差。数据处理好后就给STM32内部的RTC设置时间和给GSM模块的RTC设置时间,后续如果要重新校正时间的话只需要在SIM900A内部获取RTC的时间就可以了。实验过程中相关信息在串口调试助手显示,结果如下图所示:

测试结果一

测试结果一


一次成功不代表什么,所以又在不同时间段多次测试:

测试结果二

测试结果二

测试三

测试三

测试四

测试四


测试结果表明,实验是成功的,也就是在SIM900A连接到服务器后可以获取到正确时间并校正到RTC中。

但测试的过程中发现上面连接服务器的方法并不是最佳的,原因是连接服务器不是很好用,有时候需要六七秒就能连上,有时候要半分多钟才能连上,感觉不可靠,就像刷脸一样,而且,有时候辛辛苦苦连上了,串口2还不能完完整整接收到那串字符串。。。所以,提供了第二个获取时间的方法,也就是直接从SIM900A内部获取时间日期,下面贴上代码:

  1. _rtctime_obj RTCTime;        //SIM900A时间日期结构体
  2. /*******************************************************************************
  3. * 函数名 : Get_GSM_RTCtime(void)
  4. * 描述   : 获取SIM900A模块中RTC的时间
  5. * 输入   :
  6. * 输出   :
  7. * 返回   :RTCTime的时间结构体--年月日时分秒
  8. * 注意: 如:"19/06/18,13:11:52+08";
  9. *******************************************************************************/
  10. void Get_GSM_RTCtime(void)//直接从GSM模块内部获取时间,初次上电时需要手动复位
  11. {
  12.     u8 i = 0;
  13.     char timestr1[50]={0};
  14.     char *timestr = timestr1;//指向timestr1地址

  15.     AT_DataInit();//清除接收数组
  16.     sim900a_send_cmd("AT+CCLK?\r\n","OK",5);
  17.     delay_ms(1000);
  18.     printf("\r\n获取SIM900A内部时间日期中...\r\n");
  19.     while(1)
  20.     {
  21.    if(strstr((const char*)USART2_RX_BUF , "+CCLK") != NULL )
  22.    {
  23.       timestr = strstr((const char*)USART2_RX_BUF , "+CCLK");
  24.       break;
  25.   }
  26.     }
  27.     delay_ms(500);

  28.     for(i = 0 ; i <50 ; i++)
  29.     {
  30.         if(timestr[i] == '/')
  31.         {
  32.     RTCTime.year = (timestr[i-2]-'0')*10 + (timestr[i-1]-'0') + 2000;
  33.     RTCTime.moon = (timestr[i+1]-'0')*10 + (timestr[i+2]-'0');
  34.     RTCTime.day= (timestr[i+4]-'0')*10 + (timestr[i+5]-'0');
  35.     RTCTime.hour = (timestr[i+7]-'0')*10 + (timestr[i+8]-'0');
  36.     RTCTime.minu = (timestr[i+10]-'0')*10 + (timestr[i+11]-'0');
  37.     RTCTime.sec = (timestr[i+13]-'0')*10 + (timestr[i+14]-'0');
  38.     break;
  39.         }
  40.     }
  41.     printf("\r\nGSM内部时间:%d年%02d月%02d日%02d时%02d分%02d秒\r\n",RTCTime.year,RTCTime.moon,RTCTime.day,RTCTime.hour,RTCTime.minu,RTCTime.sec);
  42.     RTC_Set(RTCTime.year,RTCTime.moon,RTCTime.day,RTCTime.hour,RTCTime.minu,RTCTime.sec);  //设置STM32单片机内部RTC时间
  43.     AT_DataInit();//清除接收数组
  44.     memset(USART2_RX_BUF,0,USART2_MAX_RECV_LEN);
  45. }
复制代码
这代码跟上面的差不多,这里就不重复解释了,也很容易看懂。用法是直接在主函数调用就行了。需要注意的是,板子初次上电时需要手动复位一下单片机,不然串口发送AT+CCLK?命令给SIM900A模块时它不会返回字符串。这里的代码也是上面提到的那位博主提供我再进行小小修改的,我只是代码搬运工。然后发现用AT+CCLK?指令查询时间并没有网上说的那样不能用,反而觉得更好用,个人比较推荐这种方法。其效果图如下所示:

直接获取GSM内部时间.png
下面给出main.c和串口2配置及中断服务函数代码,请各路大神批评指正,提出宝贵意见。

  1. /**
  2.   ******************************************************************************
  3.   * @file    main.c
  4.   * @author  GXNU_LPK
  5.   * @version V1.0
  6.   * @date    2019-06-17
  7.   * @brief   用3.5.0版本库建的工程模板
  8.   ******************************************************************************
  9.   * @attention
  10.   *
  11.   * 实验平台: STM32F103CT6核心板
  12.   * 实验内容:GSM(SIM900A)模块通过服务器实现网络授时
  13.   * 实验作者:广西师范大学电子工程学院2015LPK
  14.   * 备    注:Get_net_time.c这部分的代码来源于https://blog.csdn.net/ludaoyi88/article/details/51757664 ,
  15.   *           根据此ludaoyi123博主提供的思路和代码进行测试和修改而来,经实验测试和改善后目前已初步达到实验目的,但
  16.   *           通过服务器获取时间那种方法 稳定性方面略微存在一些欠缺,请学习者自行改善,仅供学习,不得用于其他用途
  17.   ******************************************************************************
  18.   */
  19.   
  20. #include "stm32f10x.h"
  21. #include "GSM.h"
  22. #include "AT_Cmd.h"
  23. #include "usart2.h"
  24. #include "usart1.h"
  25. #include "delay.h"
  26. #include "string.h"
  27. #include "rtc.h"
  28. #include "Get_net_time.h"
  29. /**
  30.   * @brief  主函数
  31.   * @param  无
  32.   * @retval 无
  33.   */
  34. int main(void)
  35. {
  36.     u8 res;
  37.    
  38.     delay_init();
  39.     Usart2_Init(115200);        //初始化串口2
  40.     delay_ms(3);
  41.     Usart1_Init(115200);        //初始化串口1
  42.    
  43.     if(RTC_Init()==0)
  44.         printf("RTC初始化成功\r\n");
  45.     else
  46.         printf("RTC初始化失败\r\n");
  47.    
  48.     printf("初始化SIM900A中...\r\n");
  49.    
  50.     res=1;
  51.         while(res)
  52.         {
  53.                 res=GSM_Dect();
  54.                 delay_ms(2000);
  55.         }
  56.     res=1;
  57.         while(res)
  58.         {
  59.         res=SIM900A_CONNECT_SERVER_SEND_INFOR((u8*)"time.nist.gov",(u8*)"13");//连接授时服务器(国外)
  60.         }
  61.     Get_Sever_Time();//提取获取到的时间并存入STM32和GSM模块内部RTC中
  62. //    Get_GSM_RTCtime();//直接从GSM模块内部获取时间,初次上电时需要手动复位
  63.    
  64.     printf("\r\n系统初始化完成\r\n");
  65.             while(1)
  66.       {
  67.           display_time();//显示STM32内部RTC的时间
  68.           delay_ms(1000);
  69.       }

  70.    
  71. }

  72. /*********************************************END OF FILE**********************/
复制代码
下面是串口2配置及中断服务函数代码:
  1. #include "usart2.h"
  2. #include "stdio.h"         
  3. #include "string.h"        
  4. #include "stdarg.h"        

  5. //串口接收缓存区         
  6. u8 USART2_RX_BUF[USART2_MAX_RECV_LEN];                                 //接收缓冲,最大USART2_MAX_RECV_LEN个字节.
  7. u8 USART2_TX_BUF[USART2_MAX_SEND_LEN];                           //发送缓冲,最大USART2_MAX_SEND_LEN字节
  8. u16 USART2_RX_STA=0;  

  9. /*
  10.   功能描述: 发送一个字节
  11.   函数参数: byte —— 要发送的字节
  12.   返回说明: 无
  13. */
  14. void UART2_SendByte(unsigned char byte)        
  15. {
  16.         USART_SendData(USART2,byte);//向串口2发送数据
  17.         while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//等待发送结束
  18. }

  19. /*
  20.   功能描述: 串口发送字符串
  21.   函数参数: s —— 指向字符串的指针(字符串以'\0'结尾)
  22.   返回说明: 无
  23.   注:如果在字符串结尾有'\n',则会发送一个回车换行      
  24. */
  25. void UART2_SendStr(char *s)
  26. {
  27.         while( *s != '\0')
  28.         {
  29.                 UART2_SendByte( *s );        
  30.                 s ++;
  31.         }
  32. }

  33. void Usart2_Init(unsigned int bps)
  34. {
  35.         USART_InitTypeDef USART_InitStructure;
  36.         GPIO_InitTypeDef GPIO_InitStructure;
  37.         NVIC_InitTypeDef NVIC_InitStructure;
  38.         
  39.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  40.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  41.         USART_DeInit(USART2);
  42.         
  43.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
  44.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;         //TX  PA2
  45.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
  46.         GPIO_Init(GPIOA,&GPIO_InitStructure );      
  47.         
  48.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  49.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;         //RX  PA3
  50.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
  51.         GPIO_Init(GPIOA,&GPIO_InitStructure );
  52.         
  53.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  54.         
  55. //        NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
  56.     NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
  57.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x01 ;
  58.         NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1 ;
  59.         NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  60.         NVIC_Init(&NVIC_InitStructure);
  61.         
  62.         USART_InitStructure.USART_BaudRate=bps;
  63.         USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
  64.         USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
  65.         USART_InitStructure.USART_Parity=USART_Parity_No;
  66. //        USART_InitStructure.USART_StopBits=USART_StopBits_1;
  67.     USART_InitStructure.USART_StopBits=USART_StopBits_2;
  68.         USART_InitStructure.USART_WordLength=USART_WordLength_8b;
  69.         USART_Init(USART2,&USART_InitStructure );
  70.         
  71. //        USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
  72.     USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
  73.         
  74.         USART_Cmd(USART2,ENABLE);
  75. }

  76. //串口2,printf 函数
  77. //确保一次发送数据不超过USART2_MAX_SEND_LEN字节
  78. void u2_printf(char* fmt,...)  
  79. {  
  80.         u16 i,j;
  81.         va_list ap;
  82.         va_start(ap,fmt);
  83.         vsprintf((char*)USART2_TX_BUF,fmt,ap);
  84.         va_end(ap);
  85.         i=strlen((const char*)USART2_TX_BUF);                //此次发送数据的长度
  86.         for(j=0;j<i;j++)                                                        //循环发送数据
  87.         {
  88.           while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕   
  89.                 USART_SendData(USART2,USART2_TX_BUF[j]);
  90.         }
  91. }



  92. extern void AT_RecvProcess(unsigned char byte);

  93. //void USART2_IRQHandler(void)                        //串口2中断服务程序
  94. //{
  95. //        u8 Res;
  96. //        if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
  97. //        {
  98. //        USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志位
  99. //                Res =USART_ReceiveData(USART2);//(USART2->DR);        //读取接收到的数据
  100. //                AT_RecvProcess(Res);

  101. //        if(USART2_RX_STA < USART2_MAX_RECV_LEN)                //还可以接收数据
  102. //                {                                   
  103. //                        USART2_RX_BUF[USART2_RX_STA]=Res;                //记录接收到的值
  104. //            USART2_RX_STA++;
  105. //                }
  106. //                else
  107. //                {                                
  108. //            USART2_RX_STA=USART2_MAX_RECV_LEN;//强制标记接收完成
  109. //                }
  110. //    }
  111. //}         


  112. void USART2_IRQHandler(void)        
  113. {
  114.          u8 clear=clear;
  115.          USART_ClearFlag(USART2,USART_FLAG_TC);

  116.          if(USART_GetITStatus(USART2,USART_IT_RXNE)!=Bit_RESET)        
  117.            {
  118.            AT_RecvProcess(USART2->DR);
  119.            USART2_RX_BUF[USART2_RX_STA++]=USART2->DR;
  120.            USART_ClearFlag(USART2,USART_FLAG_ORE);//读SR
  121.            }
  122.                  
  123.          else if(USART_GetFlagStatus(USART2,USART_FLAG_IDLE)!=Bit_RESET)
  124.                 {                                       
  125.                 clear=USART2->SR;
  126.                 clear=USART2->DR;                                                
  127.                 USART2_RX_STA=0;
  128.             }                                                
  129. }
复制代码
以上就是SIM900A获取网络时间的两种小方法,不足之处请批评指正,不喜勿喷,谢谢~

没有黑币或黑币不足的的完整工程下载链接:https://download.csdn.net/download/qq_36112455/11247378

有黑币的可以从附件直接下载。
工程资源简介:用STM32F103单片机控制SIM900A模块通过连接国外的授时服务器或者访问SIM900A内部获取网络时间,把获得的时间设置到STM32内部的RTC中,实现单片机上电自动校正时间。时间在串口上显示出来。资源是完整的工程,里面包含了SIM900A的驱动和常用的拨打电话发短信连接到服务器等等功能;另工程里面也有STM32 RTC的驱动,可通过编译等获取电脑上的时间,也可下载下来学习一下。程序测试多次可用,欢迎大家留言评论,一起探讨,共同进步。
版权所有,转载请注明出处,谢谢!代码仅供学习,不得用于其他用途。

GSM通过服务器或内部网络授时.7z

767.08 KB, 下载次数: 76, 下载积分: 黑币 -5

评分

参与人数 1黑币 +6 收起 理由
6789364 + 6 很给力!

查看全部评分

回复

使用道具 举报

ID:355468 发表于 2019-10-27 17:54 | 显示全部楼层
"TCP","time.nist.gov","13"  发完后回来数据是   IIII  这样的,怎么回事?,之前成功过
回复

使用道具 举报

ID:435174 发表于 2019-10-31 21:09 来自手机 | 显示全部楼层
lis。ss 发表于 2019-10-27 17:54
"TCP","time.nist.gov","13"  发完后回来数据是   IIII  这样的,怎么回事?,之前成功过

别用服务器的方式获取了,不稳定不可靠,老是莫名其妙出现奇奇怪怪的问题。用我说的第二种方法直接从SIM卡获取时间吧
回复

使用道具 举报

ID:613800 发表于 2019-12-30 13:34 | 显示全部楼层
获取时间不稳定吗??SIM卡或者时间是怎么做的?
回复

使用道具 举报

ID:435174 发表于 2020-1-3 00:18 | 显示全部楼层
qq1182560902 发表于 2019-12-30 13:34
获取时间不稳定吗??SIM卡或者时间是怎么做的?

从SIM卡获取妥妥的,服务器不稳。 AT指令集你去查一下  AT+CCLK?
回复

使用道具 举报

ID:435174 发表于 2020-1-23 23:30 | 显示全部楼层
lis。ss 发表于 2019-10-27 17:54
"TCP","time.nist.gov","13"  发完后回来数据是   IIII  这样的,怎么回事?,之前成功过

现在回头看了下,你这个问题是串口2中断接收没处理好造成的。如果不想改中断服务函数的话就在void Get_Sever_Time(void)函数下把所有USART2_RX_BUF改成AT_RecvBuffer可以解决此问题,而且再也不会出现上文提到的那些确定,我今晚用SIM800C测过好几遍了,没问题,很好用。你可以试下。
回复

使用道具 举报

ID:435174 发表于 2020-1-23 23:40 | 显示全部楼层
Linux— 发表于 2020-1-3 00:18
从SIM卡获取妥妥的,服务器不稳。 AT指令集你去查一下  AT+CCLK?

现在回头看了下,其实用服务器那种方式还是很稳的,只需要小小改动一下,在void Get_Sever_Time(void)函数下把所有USART2_RX_BUF改成AT_RecvBuffer就好了,克服了上文说的那些缺点,今晚测试过好多次了,没有失败过,每次都成功。而且连接服务器的速度其实是跟信号有关的,之前那个地方信号太弱了,导致连接速度比较慢,在信号好的地方一下子就连上了。还有,AT+CCLK?只是获取模块的内部时间,断电重新上电后还是要从网络获取时间同步进去的,不然也是不准的。此外,获取网络时间和日期也可以用GPRS基站定位,从返回的字符串中把时间数据解析出来就行了。这个方法我也测过了,是能用的,但对信号强度要求更高,不然网络没配置好的话也是定位不到进而获取不了数据的。

评分

参与人数 1黑币 +30 收起 理由
admin + 30 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:435174 发表于 2020-2-5 20:59 | 显示全部楼层
本帖最后由 Linux— 于 2020-2-5 21:41 编辑

各位,我又找到了一种方法,数据手册上提到的。封装成函数就是这样的,亲测可用:
函数如下:
  1. u8 Synchro_NTP_Time(void)//同步时间
  2. {   
  3.     printf("\r\n正在同步网络\r\n");
  4.         if(sim900a_send_cmd("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"\r\n","OK",100))   //设置网络参数
  5.         {
  6.             printf("设置网络参数失败\r\n");
  7.             return 1;
  8.         }

  9.       if(sim900a_send_cmd("AT+SAPBR=3,1,\"APN\",\"UNIWAP\"\r\n","OK",500))  //设置APN
  10.       {
  11.           printf("设置APN失败\r\n");
  12.           return 2;
  13.       }
  14.        if( sim900a_send_cmd("AT+SAPBR=1,1\r\n","OK",200))//激活网络场景
  15.        {
  16.             printf("激活网络场景失败\r\n");
  17. //           sim900a_send_cmd("AT+CGATT=1\r\n","OK",200);
  18.            return 3;
  19.        }

  20.         if(sim900a_send_cmd("AT+SAPBR=2,1\r\n","OK",600))   //获取分配的ip地址
  21.         {
  22.             printf("获取分配的IP地址失败\r\n");
  23.             return 4;
  24.         }

  25.         if(sim900a_send_cmd("AT+CNTP=\"ntp1.aliyun.com\",32\r\n","OK",200)) //设置NTP服务地址和时区(阿里云的NTP)
  26.         {
  27.             printf("设置NTP服务地址和时区失败\r\n");
  28.             return 5;      
  29.         }

  30.         if(sim900a_send_cmd("AT+CNTP\r\n","CNTP: 1",500))  //开启网络同步
  31.         {
  32.             printf("开启网络同步失败\r\n");
  33.             return 6;     
  34.         }  
  35.         printf("\r\n网络同步成功!\r\n");
  36.         Get_GSM_RTCtime();
  37.         
  38.         return 0;
  39. }


复制代码

只要模块注册到了网络,一下子就同步到网络了,GSM模块内部时间也自动对齐网络时间了。模块有信号能注册到网络的话一秒钟就搞定了,还是很快的。调用的时候可以让它循环执行,若是不成功,设置失败次数达到10次就跳出就好了。若是失败的话估计就是在关闭网络场景那一步,其他的没啥问题。下面是我在串口调试助手显示的内容:
  1. AT
  2. OK
  3. AT+CPIN?
  4. READY
  5. GSM模块自检成功
  6. AT+CCLK?
  7. CCLK: "04/01/01,00:00:05+0
  8. 获取SIM900A内部时间日期中...
  9. AT+CCLK?
  10. CCLK?

  11. +CCLK: "04/01/01,00:00:05+0
  12. 模块返回时间数据:CCLK?

  13. +CCLK: "04/01/01,00:00:05+08"

  14. OK

  15. SMS Ready
  16. AT+CCLK?

  17. +CCLK: "04/01/01,00:00:05+08"

  18. OK

  19. 数据转换成功

  20. GSM内部时间:2004年01月01日00时00分05秒
  21. 系统初始化完毕!

  22. 进入同步网络模式

  23. 正在同步网络
  24. AT+SAPBR=3,1,"CONTYPE","GPRS"
  25. OK
  26. AT+SAPBR=3,1,"APN","UNIWAP"
  27. OK
  28. AT+SAPBR=1,1
  29. OK
  30. AT+SAPBR=2,1
  31. OK
  32. AT+CNTP="ntp1.aliyun.com",32
  33. OK
  34. AT+CNTP
  35. CNTP: 1

  36. 网络同步成功!
  37. AT+CCLK?
  38. CCLK: "20/02/05,20
  39. 获取SIM900A内部时间日期中...
  40. AT+CCLK?
  41. CCLK?

  42. +CCLK: "20/02/05,20
  43. 模块返回时间数据:CCLK?

  44. +CCLK: "20/02/05,20:33:05+08"

  45. OK
  46. AT+CCLK?

  47. +CCLK: "20/02/05,20:33:05+08"

  48. OK

  49. 数据转换成功

  50. GSM内部时间:2020年02月05日20时33分05秒
复制代码


可以看到模块刚开机初始化完成时内部时间是2004年01月01日00时00分05秒,同步网络后时间自动更新到当前时间:2020年02月05日20时33分05秒 了。有兴趣的各位不妨试试。相关的截图和NTP同步的手册截图请看博客https://blog.csdn.net/qq_36112455/article/details/92798026。因为这里的我不懂怎么在评论添加图片。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表