STM32F103RC W5500 NTP获取网络时间实现

本文介绍了如何使用STM32微控制器通过W5500以太网芯片从NTP服务器获取时间,并将时间同步到DS1302实时时钟芯片。STM32通过UDP协议发送NTP请求,解析返回的NTP报文,调整时区后存储到DS1302中,最终通过串口输出时间信息。代码示例展示了STM32驱动W5500获取网络时间的实现细节。
摘要由CSDN通过智能技术生成

NTP 是网络时间协议,将获取到的网络时间同步到本地,是本地时间与网络同步。

一般来说,STM32通过W5500从NTP服务器获取到之后,会存同步到DS1302时钟芯片中,再读取DS1302时间在应用中使用。

DS1302的时间设置和读取,可以参考 《STM32F10x读取DS1302的时间,通过USART显示在串口调试助手上》

NTP协议是基于UDP基础上封装的协议,NTP报文格式 可以参考 《NTP报文格式》

STM32驱动W5500,使用UDP来实现获取NTP网络时间的代码实现:NTP服务器IP选取 202.112.10.60,端口固定为123.

ntp.c


   
   
  1. #include "types.h"
  2. #include "ntp.h"
  3. #include "socket.h"
  4. #include "w5500.h"
  5. #include <string.h>
  6. #include <stdio.h>
  7. uint8 NTP_SERVER_IP[ 4] = { 202, 112, 10, 60};
  8. void func_analysis_ntp_back_msg(uint8* buf, uint16 idx, TSTAMP *tstmp, DATETIME *datetime)
  9. {
  10. TSTAMP seconds = 0;
  11. uint8 i = 0 ,zone = TIMEZONE8;
  12. for (i = 0; i < 4; i++)
  13. {
  14. seconds = (seconds << 8) | buf[idx + i];
  15. }
  16. switch (zone)
  17. {
  18. case 0:
  19. seconds -= 12* 3600;
  20. break;
  21. case 1:
  22. seconds -= 11* 3600;
  23. break;
  24. case 2:
  25. seconds -= 10* 3600;
  26. break;
  27. case 3:
  28. seconds -= ( 9* 3600+ 30* 60);
  29. break;
  30. case 4:
  31. seconds -= 9* 3600;
  32. break;
  33. case 5:
  34. case 6:
  35. seconds -= 8* 3600;
  36. break;
  37. case 7:
  38. case 8:
  39. seconds -= 7* 3600;
  40. break;
  41. case 9:
  42. case 10:
  43. seconds -= 6* 3600;
  44. break;
  45. case 11:
  46. case 12:
  47. case 13:
  48. seconds -= 5* 3600;
  49. break;
  50. case 14:
  51. seconds -= ( 4* 3600+ 30* 60);
  52. break;
  53. case 15:
  54. case 16:
  55. seconds -= 4* 3600;
  56. break;
  57. case 17:
  58. seconds -= ( 3* 3600+ 30* 60);
  59. break;
  60. case 18:
  61. seconds -= 3* 3600;
  62. break;
  63. case 19:
  64. seconds -= 2* 3600;
  65. break;
  66. case 20:
  67. seconds -= 1* 3600;
  68. break;
  69. case 21: //£¿
  70. case 22:
  71. break;
  72. case 23:
  73. case 24:
  74. case 25:
  75. seconds += 1* 3600;
  76. break;
  77. case 26:
  78. case 27:
  79. seconds += 2* 3600;
  80. break;
  81. case 28:
  82. case 29:
  83. seconds += 3* 3600;
  84. break;
  85. case 30:
  86. seconds += ( 3* 3600+ 30* 60);
  87. break;
  88. case 31:
  89. seconds += 4* 3600;
  90. break;
  91. case 32:
  92. seconds += ( 4* 3600+ 30* 60);
  93. break;
  94. case 33:
  95. seconds += 5* 3600;
  96. break;
  97. case 34:
  98. seconds += ( 5* 3600+ 30* 60);
  99. break;
  100. case 35:
  101. seconds += ( 5* 3600+ 45* 60);
  102. break;
  103. case 36:
  104. seconds += 6* 3600;
  105. break;
  106. case 37:
  107. seconds += ( 6* 3600+ 30* 60);
  108. break;
  109. case 38:
  110. seconds += 7* 3600;
  111. break;
  112. case 39:
  113. seconds += 8* 3600;
  114. break;
  115. case 40:
  116. seconds += 9* 3600;
  117. break;
  118. case 41:
  119. seconds += ( 9* 3600+ 30* 60);
  120. break;
  121. case 42:
  122. seconds += 10* 3600;
  123. break;
  124. case 43:
  125. seconds += ( 10* 3600+ 30* 60);
  126. break;
  127. case 44:
  128. seconds += 11* 3600;
  129. break;
  130. case 45:
  131. seconds += ( 11* 3600+ 30* 60);
  132. break;
  133. case 46:
  134. seconds += 12* 3600;
  135. break;
  136. case 47:
  137. seconds += ( 12* 3600+ 45* 60);
  138. break;
  139. case 48:
  140. seconds += 13* 3600;
  141. break;
  142. case 49:
  143. seconds += 14* 3600;
  144. break;
  145. }
  146. *tstmp = seconds;
  147. //calculation for date
  148. calc_date_time(seconds, datetime);
  149. }
  150. void calc_date_time(TSTAMP seconds, DATETIME *datetime)
  151. {
  152. uint8 yf = 0;
  153. uint32 p_year_total_sec;
  154. uint32 r_year_total_sec;
  155. TSTAMP n= 0, d= 0, total_d= 0, rz= 0;
  156. uint16 y= 0, r= 0, yr= 0;
  157. signed long long yd= 0;
  158. n = seconds;
  159. total_d = seconds/(SECS_PERDAY);
  160. d= 0;
  161. p_year_total_sec=SECS_PERDAY* 365;
  162. r_year_total_sec=SECS_PERDAY* 366;
  163. while(n>=p_year_total_sec)
  164. {
  165. if((EPOCH+r)% 400== 0 || ((EPOCH+r)% 100!= 0 && (EPOCH+r)% 4== 0))
  166. {
  167. n = n -(r_year_total_sec);
  168. d = d + 366;
  169. }
  170. else
  171. {
  172. n = n - (p_year_total_sec);
  173. d = d + 365;
  174. }
  175. r+= 1;
  176. y+= 1;
  177. }
  178. y += EPOCH;
  179. datetime->yy = y;
  180. yd= 0;
  181. yd = total_d - d;
  182. yf= 1;
  183. while(yd>= 28)
  184. {
  185. if(yf== 1 || yf== 3 || yf== 5 || yf== 7 || yf== 8 || yf== 10 || yf== 12)
  186. {
  187. yd -= 31;
  188. if(yd< 0) break;
  189. rz += 31;
  190. }
  191. if (yf== 2)
  192. {
  193. if (y% 400== 0 || (y% 100!= 0 && y% 4== 0))
  194. {
  195. yd -= 29;
  196. if(yd< 0) break;
  197. rz += 29;
  198. }
  199. else
  200. {
  201. yd -= 28;
  202. if(yd< 0) break;
  203. rz += 28;
  204. }
  205. }
  206. if(yf== 4 || yf== 6 || yf== 9 || yf== 11 )
  207. {
  208. yd -= 30;
  209. if(yd< 0) break;
  210. rz += 30;
  211. }
  212. yf += 1;
  213. }
  214. datetime->mo = yf;
  215. yr = total_d-d-rz;
  216. yr += 1;
  217. datetime->dd = yr;
  218. seconds = seconds%SECS_PERDAY;
  219. datetime->hh = seconds/ 3600;
  220. datetime->mm = (seconds% 3600)/ 60;
  221. datetime->ss = (seconds% 3600)% 60;
  222. }
  223. /*
  224. TSTAMP change_datetime_to_seconds(void)
  225. {
  226. TSTAMP seconds=0;
  227. uint32 total_day=0;
  228. uint16 i=0,run_year_cnt=0,l=0;
  229. l = dt_ntp.yy;
  230. for(i=EPOCH;i<l;i++)
  231. {
  232. if((i%400==0) || ((i%100!=0) && (i%4==0)))
  233. {
  234. run_year_cnt += 1;
  235. }
  236. }
  237. total_day=(l-EPOCH-run_year_cnt)*365+run_year_cnt*366;
  238. for(i=1;i<=dt_ntp.mm;i++)
  239. {
  240. if(i==5 || i==7 || i==10 || i==12)
  241. {
  242. total_day += 30;
  243. }
  244. if (i==3)
  245. {
  246. if (l%400==0 && l%100!=0 && l%4==0)
  247. {
  248. total_day += 29;
  249. }
  250. else
  251. {
  252. total_day += 28;
  253. }
  254. }
  255. if(i==2 || i==4 || i==6 || i==8 || i==9 || i==11)
  256. {
  257. total_day += 31;
  258. }
  259. }
  260. seconds = (total_day+dt_ntp.dd-1)*24*3600;
  261. seconds += dt_ntp.ss;//seconds
  262. seconds += dt_ntp.mm*60;//minute
  263. seconds += dt_ntp.hh*3600;//hour
  264. return seconds;
  265. }
  266. */
  267. uint8 func_pack_ntp_message(uint8 *ntp_server_ip, uint8 *ntp_message)
  268. {
  269. uint8 flag;
  270. NTPFORMAT ntpfmt;
  271. ntpfmt.dstaddr[ 0] = ntp_server_ip[ 0];
  272. ntpfmt.dstaddr[ 1] = ntp_server_ip[ 1];
  273. ntpfmt.dstaddr[ 2] = ntp_server_ip[ 2];
  274. ntpfmt.dstaddr[ 3] = ntp_server_ip[ 3];
  275. ntpfmt.leap = 11; /* leap indicator */
  276. ntpfmt.version = 3; /* version number */
  277. ntpfmt.mode = 3; /* mode */
  278. ntpfmt.stratum = 0; /* stratum */
  279. ntpfmt.poll = 0; /* poll interval */
  280. ntpfmt.precision = 0; /* precision */
  281. ntpfmt.rootdelay = 0; /* root delay */
  282. ntpfmt.rootdisp = 0; /* root dispersion */
  283. ntpfmt.refid = 0; /* reference ID */
  284. ntpfmt.reftime = 0; /* reference time */
  285. ntpfmt.org = 0; /* origin timestamp */
  286. ntpfmt.rec = 0; /* receive timestamp */
  287. ntpfmt.xmt = 1; /* transmit timestamp */
  288. flag = (ntpfmt.leap << 6) + (ntpfmt.version << 3 ) + ntpfmt.mode; //one byte Flag
  289. ntp_message[ 0] = flag;
  290. return 0;
  291. }
  292. uint8 func_get_ntp_time(uint8 sock, TSTAMP *tstamp, DATETIME *datetime, uint16 timeout_ms)
  293. {
  294. uint16 cnt_timeout = 0;
  295. uint8 ntp_message[ 48] = { 0,};
  296. uint8 ntp_back_msg[ 256] = { 0,};
  297. uint8 ntp_s_ip[ 4] = { 0,};
  298. uint16 ntp_s_port, len;
  299. //socket init
  300. if(getSn_SR(sock) == SOCK_CLOSED)
  301. {
  302. socket(sock, Sn_MR_UDP, NTP_PORT, 0);
  303. }
  304. //pack NTP message
  305. func_pack_ntp_message(NTP_SERVER_IP, ntp_message);
  306. //send ntp message
  307. sendto(sock, ntp_message, sizeof(ntp_message), NTP_SERVER_IP, NTP_PORT);
  308. //wait for NTP message back
  309. for(;;)
  310. {
  311. if(getSn_IR(sock) & Sn_IR_RECV)
  312. {
  313. setSn_IR(sock, Sn_IR_RECV);
  314. }
  315. if ((len = getSn_RX_RSR(sock)) > 0)
  316. {
  317. len = recvfrom(sock, ntp_back_msg, sizeof(ntp_back_msg), ntp_s_ip, &ntp_s_port);
  318. if(len >= 48 && ntp_s_port == NTP_PORT)
  319. {
  320. //analysis
  321. func_analysis_ntp_back_msg(ntp_back_msg, 40, tstamp, datetime);
  322. break;
  323. }
  324. }
  325. cnt_timeout ++;
  326. if(cnt_timeout > timeout_ms)
  327. {
  328. close(sock);
  329. return 1;
  330. }
  331. delay_ms( 1);
  332. }
  333. //close socket
  334. close(sock);
  335. return 0;
  336. }

ntp.h


   
   
  1. #ifndef __NTP_H__
  2. #define __NTP_H__
  3. #include "types.h"
  4. #ifndef __Z_UTIL_TIME_H
  5. #define __Z_UTIL_TIME_H
  6. #include "z_util_time.h"
  7. #endif
  8. #define NTP_PORT 123
  9. #define EPOCH 1900 // NTP start year
  10. #define TIMEZONE0 22
  11. #define TIMEZONE8 39
  12. typedef unsigned long long TSTAMP;
  13. typedef signed char s_char;
  14. typedef unsigned int tdist;
  15. typedef struct _ntpformat
  16. {
  17. uint8 dstaddr[ 4]; /* destination (local) address */
  18. char version; /* version number */
  19. char leap; /* leap indicator */
  20. char mode; /* mode */
  21. char stratum; /* stratum */
  22. char poll; /* poll interval */
  23. s_char precision; /* precision */
  24. tdist rootdelay; /* root delay */
  25. tdist rootdisp; /* root dispersion */
  26. tdist refid; /* reference ID */
  27. TSTAMP reftime; /* reference time */
  28. TSTAMP org; /* origin timestamp */
  29. TSTAMP rec; /* receive timestamp */
  30. TSTAMP xmt; /* transmit timestamp */
  31. } NTPFORMAT;
  32. typedef struct _datetime
  33. {
  34. uint16 yy;
  35. uint8 mo;
  36. uint8 dd;
  37. uint8 hh;
  38. uint8 mm;
  39. uint8 ss;
  40. } DATETIME;
  41. #define SECS_PERDAY 86400UL // seconds in a day = 60*60*24
  42. #define UTC_ADJ_HRS 9 // SEOUL : GMT+9
  43. void calc_date_time(TSTAMP seconds, DATETIME *datetime);
  44. uint8 func_pack_ntp_message(uint8 *ntp_server_ip, uint8 *ntp_message);
  45. void func_analysis_ntp_back_msg(uint8* buf, uint16 idx, TSTAMP *tstmp, DATETIME *datetime);
  46. uint8 func_get_ntp_time(uint8 sock, TSTAMP *tstamp, DATETIME *datetime, uint16 timeout_ms);
  47. #endif

测试的主函数代码:


   
   
  1. #ifndef __STM32F10X_H
  2. #define __STM32F10X_H
  3. #include "stm32f10x.h"
  4. #endif
  5. #ifndef __Z_UTIL_TIME_H
  6. #define __Z_UTIL_TIME_H
  7. #include "z_util_time.h"
  8. #endif
  9. #ifndef __Z_HARDWARE_LED_H
  10. #define __Z_HARDWARE_LED_H
  11. #include "z_hardware_led.h"
  12. #endif
  13. #ifndef __Z_HARDWARE_SPI_H
  14. #define __Z_HARDWARE_SPI_H
  15. #include "z_hardware_spi.h"
  16. #endif
  17. #ifndef __Z_HARDWARE_USART2_H
  18. #define __Z_HARDWARE_USART2_H
  19. #include "z_hardware_usart2.h"
  20. #endif
  21. #include "w5500.h"
  22. #include "socket.h"
  23. #include "w5500_conf.h"
  24. #include "dhcp.h"
  25. #include "ntp.h"
  26. int main(void)
  27. {
  28. DHCP_Get dhcp_get;
  29. TSTAMP tstmp;
  30. DATETIME dt;
  31. uint8 buffer[ 1024];
  32. u8 mac[ 6]={ 0, 0, 0, 0, 0, 0};
  33. init_led();
  34. init_hardware_usart2_dma( 9600);
  35. init_system_spi();
  36. func_w5500_reset();
  37. getMacByLockCode(mac);
  38. setSHAR(mac);
  39. /*DHCP TEST*/
  40. sysinit(txsize, rxsize);
  41. setRTR( 2000);
  42. setRCR( 3);
  43. //DHCP
  44. if(func_dhcp_get_ip_sub_gw( 0, mac, &dhcp_get, 500) == 0)
  45. {
  46. func_usart2_dma_send_bytes(dhcp_get.lip, 4);
  47. setSUBR(dhcp_get.dns);
  48. setGAR(dhcp_get.gw);
  49. setSIPR(dhcp_get.lip);
  50. }
  51. for(;;)
  52. {
  53. delay_ms( 500);
  54. if(func_get_ntp_time( 1, &tstmp, &dt, 2000) == 0)
  55. {
  56. int len;
  57. memset(buffer, 0, sizeof(buffer));
  58. len = sprintf(( char*)buffer, "%04d-%02d-%02d %02d:%02d:%02d", dt.yy, dt.mo, dt.dd, dt.hh, dt.mm, dt.ss);
  59. func_usart2_dma_send_bytes(buffer, len);
  60. }
  61. func_led1_on();
  62. delay_ms( 460);
  63. func_led1_off();
  64. delay_ms( 560);
  65. }
  66. }

通过串口将获取的网络时间打印出来,测试的效果:

可以看出测试的时间和windows的时间,相差不大。

### 回答1: STM32F103是一款具有ARM Cortex-M3内核的单片机,而W5500是一款基于SPI接口的以太网控制芯片。要实现STM32F103接收MQTT QoS1消息,需要进行以下步骤: 1. 硬件连接:将W5500通过SPI接口连接到STM32F103的相应引脚,并给W5500提供适当的电源和时钟。 2. 初始化W5500:在STM32F103上初始化W5500控制器,设置其工作模式和网络连接参数,包括IP地址、端口号、MAC地址等。 3. 建立MQTT连接:使用W5500进行网络连接,建立到MQTT服务器的连接。发送MQTT连接请求,包括客户端ID、用户名、密码等信息。等待MQTT服务器的回应,确保连接建立成功。 4. 订阅主题:发送MQTT订阅主题请求,将感兴趣的主题名称发送给服务器。等待服务器的确认,表明订阅成功。 5. 接收消息:在循环中使用W5500接收MQTT消息,通过订阅主题和正确配置接收回调函数,确保只接收到QoS1消息。 6. 消息确认:收到消息后,发送MQTT PUBACK消息确认收到。这将使服务器知道消息成功到达设备,并且可以继续发送更多的消息。 以上就是实现STM32F103接收MQTT QoS1消息的一般步骤。具体实现时,需要根据具体的硬件和软件平台进行相应的编程和配置。 ### 回答2: STM32F103W5500是很常见的嵌入式系统中使用的芯片,用于实现网络通信功能。MQTT是一种轻量级的消息传输协议,常用于物联网设备之间的通信。QoS1(Quality of Service 1)是MQTT中的一种服务质量等级,表示消息至少被传送一次。 要实现STM32F103通过W5500接收MQTT QoS1消息,需要以下步骤: 1. 初始化W5500芯片,包括设置网络参数、初始化SPI通信等。确保STM32F103能够正常与W5500进行通信。 2. 连接到MQTT服务器。使用W5500建立到MQTT服务器的TCP连接,并发送MQTT CONNECT报文进行认证和连接。 3. 订阅消息。发送MQTT SUBSCRIBE报文来订阅QoS1消息。 4. 接收消息。使用W5500来接收服务器传来的MQTT PUBLISH报文,并解析消息内容。 5. 确认接收。对接收到的消息进行确认,发送MQTT PUBACK报文,表示成功接收到消息。 6. 处理下一条消息。继续接收和处理后续的QoS1消息,直至所有消息处理完成。 需要注意的是,为了能够正确接收MQTT QoS1消息,需要实现消息持久化、重发机制等。当接收到消息后,需要将其存储在合适的地方,以防止消息丢失。同时,需要定时发送MQTT PUBACK报文来确认接收,以便服务器知道该消息已被成功接收。 以上是基本的实现流程,具体细节可能因使用的MQTT库和具体业务需求而有所区别。具体开发时,可以根据实际情况对这些步骤进行适当调整和优化。 ### 回答3: 要实现STM32F103W5500接收MQTT QoS 1消息,首先需要确保已经配置好STM32F103的硬件和W5500网络连接,并安装好相应的驱动库。 以下是大致的步骤: 1. 初始化W5500模块,包括设置IP地址、端口号、连接模式等参数。 2. 创建一个MQTT客户端,设置相关的MQTT连接参数,包括服务器地址、端口号、客户端ID等。 3. 连接到MQTT服务器。 4. 订阅MQTT主题,以便接收相应的QoS 1消息。可以使用MQTT的订阅回调函数,在接收到消息时触发相应的操作。 5. 循环接收消息,通过W5500模块接收MQTT服务器发送的QoS 1消息。 6. 在接收到消息时,可以根据需要进行相应的处理,例如解析消息内容、进行业务逻辑处理等。 需要注意的是,STM32F103的能力有限,如果要处理大量的QoS 1消息,可能需要进行一些优化措施,例如使用DMA进行数据传输,或者调整MQTT消息的传输速率等。 同时,还需要关注STM32F103W5500的资源占用情况,确保系统正常运行。在实际开发中,可以根据具体需求进行相应的调试和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值