【嵌入式实战】STM32+Lwip 实现 SNTP 网络授时(超详细)

本文详细介绍STM32结合LWIP使用SNTP协议获取网络时间的方法,包括配置步骤、测试验证及时间戳处理,确保嵌入式设备时间精确同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


原创声明

本文为 HinGwenWoong 原创,如果这篇文章对您有帮助,欢迎转载,转载请阅读文末的【授权须知】,感谢您对 HinGwenWoong 文章的认可!


前言

物联网的设备,离不开时间戳的使用,下面介绍 STM32 + LWIP 如何 使用 SNTP 获取网络时间

我是 HinGwenWoong ,一个有着清晰目标不停奋斗的程序猿,热爱技术,喜欢分享,码字不易,如果帮到您,请帮我在屏幕下方点赞 👍 ,您的点赞可以让技术传播得更远更广,谢谢!


一、SNTP 是什么?

SNTP协议采用客户端/服务器的工作方式,可以采用单播(点对点)或者广播(一点对多点)模式操作。SNTP服务器通过接收GPS信号或自带的原子钟作为系统的时间基准。单播模式下,SNTP客户端能够通过定期访问SNTP服务器获得准确的时间信息,用于调整客户端自身所在系统的时间,达到同步时间的目的。广播模式下,SNTP服务器周期性地发送消息给指定的IP广播地址或者IP多播地址。SNTP客户端通过监听这些地址来获得时间信息。

网络中一般存在很多台SNTP服务器,客户端会通过一定的算法选择最好的几台服务器使用。如果一台SNTP服务器在工作过程中失去了外部时间源,此时SNTP服务器会告诉SNTP客户端“我失去了外部时间”。当SNTP客户端收到这个信息时,就会丢弃发生故障的SNTP服务器发给它的时间信息,然后重新选择其他的SNTP服务器。


二、Cube 快速配置

2.1 STM32 ETH 设置

在这里插入图片描述

2.2 修改 PHY 地址

本项目使用的是 LAN8720 芯片,需要修改 PHY Address0
在这里插入图片描述

2.3 LWIP SNTP 配置

SNTP设置

2.4 开启 STM32 RTC

开启RTC


三、生成工程的简单测试

3.1 手动修改 MAC 地址

Cube 生成的 MAC 地址是固定的,防止和测试环境中的其他设备相撞,需要打开文件 ethernetif.c 手动修改 MAC 地址,我这里提取了 芯片ID作为MAC地址的最后几位,这里是 STM32F767 的芯片ID的地址 0x1FF0F420
在这里插入图片描述

uint32_t sn0 = *(uint32_t *)(0x1FF0F420);//STM32 cpu id
MACAddr[3] = (sn0 >> 16) & 0xFF;
MACAddr[4] = (sn0 >> 8) & 0xFFF;
MACAddr[5] = sn0 & 0xFF;

3.2 Ping 测试

编译 -> 烧录 到单片机里面,拿一条和 PC 在同一局域网内的网线,根据 MX_LWIP_Init()函数下面设置的 IP 测试 ping 功能,下面是成功的结果图:
在这里插入图片描述


四、使用步骤

4.1 开启 Lwip Debug

打开 lwipopts.hLwip 开启 debug,并重写 打印函数,并打开 SNTPDebug 模式开启DEBUG
代码:

//开启 LWIP DEBUG 
#define LWIP_DEBUG 
  #include "bsp_printlog.h" 
  #define LWIP_PLATFORM_DIAG(x) do {print_log x;} while(0)

4.2 初始化 SNTP


/*!
* @brief Lwip 的 SNTP 初始化封装接口
*        执行条件:无
*
* @retval: 无
*/
void bsp_sntp_init(void)
{
	//设置 SNTP 的获取方式 -> 使用向服务器获取方式
	sntp_setoperatingmode(SNTP_OPMODE_POLL);
	
	//SNTP 初始化
	sntp_init();
	
	//加入授时中心的IP信息
	set_sntp_server_list();
}

4.3 加入服务器 IP列表

/*!
* @brief 设置 SNTP 的服务器地址,
* 		 加入多个 IP 以免某个 IP 获取不了时间
*        执行条件:无
*
* @retval: 无
*/
void set_sntp_server_list(void)
{
	uint32_t server_list[SNTP_MAX_SERVERS] =	{  
													0x279148D2,  //国家授时中心
													0x42041876,
													0x5F066CCA,
													0x0B6C1978,
													0x0B0C5CB6,
													0x58066BCB,
													0x14731978,
													0xC51F70CA,
													0x521D70CA,
													0x820176CA,
													0x510176CA,
												};
	ip_addr_t sntp_server;
												
	for(int i = 0; i < SNTP_MAX_SERVERS; i++)
	{
		sntp_server.addr = server_list[i];
		sntp_setserver(i, &sntp_server);  // 国家授时中心
	}
}

4.4 编写处理函数

打开 lwipopts.h文件,定义 SNTP 的处理函数接口

//定义 Lwip SNTP 的 处理函数
#include "bsp_sntp.h"
#define SNTP_SET_SYSTEM_TIME		sntp_set_time

在应用代码中实现 sntp_set_time 函数接口,下面的例子是将获取到的时间戳经过转化成 年月日时分秒 设置到 RTC 中

/*!
* @brief SNTP 获取时间戳的处理函数
*        执行条件:无
*
* @param [in] : sntp 获取的时间戳
*
* @retval: 无
*/
void sntp_set_time(uint32_t sntp_time)
{
	if(sntp_time == 0)
	{
		print_log("sntp_set_time: wrong!@@\n");
		return;
	}
	
	print_log("sntp_set_time: c00, enter!\n");
	print_log("sntp_set_time: c01, get time = %u\n", sntp_time);

	struct tm *time;
	RTC_TimeTypeDef sTime = {0};
	RTC_DateTypeDef sDate = {0};

	sntp_time += (8 * 60 * 60); ///北京时间是东8区需偏移8小时

	time = localtime(&sntp_time);

	/*
	 * 设置 RTC 的 时间
	 */
	sTime.Hours = time->tm_hour;
	sTime.Minutes = time->tm_min;
	sTime.Seconds = time->tm_sec;
	sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
	sTime.StoreOperation = RTC_STOREOPERATION_RESET;
	if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
	{
		Error_Handler();
	}
	
	/*
	 * 设置 RTC 的 日期
	 */
	sDate.WeekDay = time->tm_wday;
	sDate.Month = (time->tm_mon) + 1;
	sDate.Date = time->tm_mday;
	sDate.Year = (time->tm_year) + 1900 - 2000;
	if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
	{
		Error_Handler();
	}

	print_log("sntp_set_time: c02, decode time: 20%d-%02d-%02d %d:%d:%d\n", \
				sDate.Year, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds);
	
	print_log("sntp_set_time: c03, test get = %u\n", get_timestamp());
	print_log("sntp_set_time: c04, set rtc time done\n");
}

4.5 从RTC中获取时间戳

/*!
 * @brief 获取当前时间戳
 *
 * @param [in] none
 *
 * @retval 当前时间戳
 */
uint32_t get_timestamp(void)
{
    struct tm stm;
    static RTC_DateTypeDef g_Date = {0};
    static RTC_TimeTypeDef g_Time = {0};

    ///获取时间必须在获取日期前
    HAL_RTC_GetTime(&hrtc, &g_Time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &g_Date, RTC_FORMAT_BIN);

    stm.tm_year = g_Date.Year + 100;    //RTC_Year rang 0-99,but tm_year since 1900

    stm.tm_mon = g_Date.Month - 1;      //RTC_Month rang 1-12,but tm_mon rang 0-11

    stm.tm_mday = g_Date.Date;          //RTC_Date rang 1-31 and tm_mday rang 1-31

    stm.tm_hour = g_Time.Hours;         //RTC_Hours rang 0-23 and tm_hour rang 0-23

    stm.tm_min = g_Time.Minutes;        //RTC_Minutes rang 0-59 and tm_min rang 0-59

    stm.tm_sec = g_Time.Seconds;

	return (mktime(&stm) - (8 * 60 * 60));///配置时由于东八区增加8小时,现为时间戳,需减去
}

5.6 最终效果

使用 RTT 连接 STM32 (详见我的教程【嵌入式小技巧】stm32 实现 Segger RTT 打印(超详细)
编译、烧录到单片机,可以看到获取时间戳成功,获取的时间经过自己的函数,设置到RTC中,获取的时候也获取相同
最终效果图


总结

以上是 STM32 + LWIP 如何 使用 SNTP 获取网络时间 的全部内容,本文介绍了获取网络时间的一个方法,让嵌入式物联网设备能够时钟和网络同步。


更多阅读推荐

我是 HinGwenWoong ,一个有着清晰目标不停奋斗的程序猿,热爱技术,喜欢分享,码字不易,如果帮到您,请帮我在屏幕下方点赞 👍 ,您的点赞可以让技术传播得更远更广,谢谢!


授权须知

  1. 原创文章在推送两天后才可进行转载
  2. 转载文章,禁止声明原创
  3. 不允许直接二次转载,转载请根据原文链接联系作者
  4. 若无需改版,在文首清楚标注作者及来源/原文链接,并删除【原创声明】,即可直接转载。
    但对于未注明转载来源/原文链接的文章,我将保留追述的权利。

作者:HinGwenWoong
一个有着清晰目标不停奋斗的程序猿,热爱技术,喜欢分享,共同进步!
CSDN: HinGwenWoong
原文链接:【嵌入式实战】STM32+Lwip 实现 SNTP 网络授时(超详细)

  1. 若需要修改文章的排版,请根据原文链接联系作者
  2. 再次感谢您的认可,转载请遵守如上转载须知!
### 关于面包板电源模块 MB102 的 USB 供电规格及兼容性 #### 1. **MB102 基本功能** 面包板电源模块 MB102 是一种常见的实验工具,主要用于为基于面包板的小型电子项目提供稳定的电压输出。它通常具有两路独立的稳压输出:一路为 5V 和另一路可调电压(一般范围为 3V 至 12V)。这种设计使得它可以满足多种芯片和传感器的不同工作电压需求。 #### 2. **USB 供电方式** MB102 支持通过 USB 接口供电,输入电压通常是标准的 5V DC[^1]。由于其内部集成了 LM7805 稳压器以及可调节电位器控制的直流-直流变换电路,因此即使输入来自电脑或其他低功率 USB 设备,也能稳定地向负载供应电力。不过需要注意的是,如果项目的功耗较高,则可能超出某些 USB 端口的最大电流能力(一般是 500mA),从而引起不稳定现象或者保护机制启动断开连接的情况发生。 #### 3. **兼容性分析** 该型号广泛适用于各种微控制器单元 (MCU),特别是那些像 Wemos D1 R32 这样可以通过杜邦线轻松接入并共享相同逻辑级别的系统[^2]。另外,在提到 Arduino Uno 板时也表明了良好的互操作性,因为两者均采用相似的标准接口定义与电气特性参数设置[^4]: - 对于需要 3.3V 工作环境下的组件来说,只需调整好对应跳线帽位置即可实现精准匹配; - 当涉及到更多外围扩展应用场合下,例如带有多重模拟信号采集任务的情形里,利用 MB102 提供干净无干扰的基础能源供给就显得尤为重要了[^3]。 综上所述,对于打算构建以单片机为核心的原型验证平台而言,选用具备良好声誉记录且易于获取配件支持服务链路上下游资源丰富的品牌产品——如这里讨论过的这款特定类型的配电装置不失为明智之举之一。 ```python # 示例 Python 代码展示如何检测硬件状态 import machine pin = machine.Pin(2, machine.Pin.IN) if pin.value() == 1: print("Power supply is stable.") else: print("Check your connections and power source.") ```
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值