【STM32训练—WiFi模块】第一篇、STM32驱动ESP8266WiFi模块获取网络时间

【开源项目自学笔记】STM32与ESP8266通讯 这个项目学习了这几块内容:stm32的串口中断,定时器中断,时间片轮询机制,esp8266,DTH11温湿度传感器的使用。对串口通信收发数据的处理。设计知识点:串口的格式化打印。DWT内核精确延时。sysTick延时。一、DTH11介绍DTH11是串行接口,单线双向。一次一次完整的数据传输为40bit,高位先出。数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 +8bit校验。 阅读详情
AI摘要

本文介绍如何使用STM32和ESP8266模块获取网络时间,并通过串口显示动态时间。文中详细讲解了利用ESP8266模块连接WiFi获取时间数据包的过程,并通过STM32的定时器实现时间的动态更新。

目录

第一部分、写在前面

1、硬件准备

2、相关的AT指令

3、参考博客 

第二部分、电脑串口助手调试ESP8266模块获取网络时间

1、ESP8266获取时间的流程

2、具体实现步骤

第三部分、STM32驱动ESP8266模块获取网络时间

1、实现原理

2、相关代码

 第四部分、总结

1、效果展示

2、完整工程代码

3、写在后面


第一部分、写在前面

        利用STM32获取网络时间,然后再通过LCD将时间显示出来,这是之前很久做的工程,这段时间刚好比较悠闲,然后之前也做过一些笔记,现在刚好将其完善一下。我感觉这篇博客主要讲实现的方法,注意:代码不是重点,重点是我是如何一步一步实现的。而且后续的博客还会讲到如何利用同样的方式去获取天气,去连接阿里云等。

        所以,学会一种方法很重要。

1、硬件准备

        STM32选用核心板F103C8T6,然后再加一个ESP8266 WiFi模块(任何型号应该都可以),最后需要一个USB-TTL模块用来打印串口数据。群号:1020775171。

2、相关的AT指令

AT                           //查询模块是否正常工作           
AT+RST                       //模块复位
AT+RESTORE                   //恢复出厂设置
AT+CWMODE=1                  //设置WiFi模块的模式
AT+CIPMUX=0                  //设置模块为单路连接模式
AT+CWJAP="WIFI名称","密码"    //连接网络
AT+CIPSTART="TCP","192.168.666",80//连接TCP服务器,192.168.0.102是服务器IP,8080是服务器端口。
AT+CIPSTART="TCP","quan.suning.com",80  //或者 AT+CIPSTART="TCP","175.6.49.231",80
AT+CIPMODE=1	             //开启透传模式
AT+CIPSEND		             //开始发送数据
+++                          //退出透传模式

3、参考博客 

        首先是这一篇,这篇文章作者是通过Arduino IDE开发板实现的,但是给我提供了灵感,文章地址:esp8266获取网络时间_tongyue的博客-优快云博客_esp8266获取网络时间

        其次是关于苏宁后台时间数据的解析方式,这里我参考的是这一篇,文章解释的非常详细,而且我时间的解析代码也是来源于这篇文章,推荐大家去看一下。利用苏宁API接口获取北京时间授时_xgy516的博客-优快云博客_获取北京时间api

第二部分、电脑串口助手调试ESP8266模块获取网络时间

1、ESP8266获取时间的流程

        下图就是获取时间的流程图,这里的时间其实是通过访问苏宁网页的后台得到的时间数据,例如你可以直接在浏览器中输入“quan.suning.com/getSysTime.do”,你就会看到返回的时间数据格式是怎样的。另外,后面也会解释每个步骤的原理。

2、具体实现步骤

        上面介绍了获取时间的流程图,下面就依据这流程图的步骤,一步一步来实现。

第一步、让ESP8266连接上USB-TTL模块,然后连接电脑,接着打开电脑上的串口助手软件,最后点击打开串口。注意:默认串口的波特率为115200

第二步、在多条发送那一栏提前建立指令集,如下图所示

0:AT
1:AT+RST
2:AT+CWMODE=1
3:AT+CIPMUX=0
4:AT+CWJAP="你的WiFi名称","你的WiFi密码"
5:AT+CIPMODE=1
6:AT+CIPSTART="TCP","quan.suning.com",80
7:AT+CIPSEND
8:GET http://quan.suning.com/getSysTime.do
9:+++

第三步、开始发送指令,之间点击按钮0就可以了,注意:勾选发送新行

如果返回OK,表明连接成功。如果没有反应,那就要注意:看一下ESP8266的RXD和TXD与USB-TTL的RXD与TXD是不是交叉相接?

第三步、对ESP8266进行复位操作

第四步、设置ESP8266为STA(Station)模式,这个模式类似于无线终端,本身并不接受无线的接入,它可以连接到AP,一般无线网卡即工作在该模式。

第五步、设置ESP8266为单路连接模式,因为后面我要将模块设置为透传模式,而透传模式只能在单连接模式下进行

第六步、连接WiFi,这里我推荐通过手机开热点的方式建立一个WiFi,因为这样连接成功后,手机上也会有显示。

第七步、设置为透传模式,透传模式是指:与传输网络的介质、调制解调方式、传输方式、传输协议无关的一种数据传送方式,这就好比收快递,邮件中间有可能通过自行车、汽车、火车、飞机的多种组合运输方式到达你的手上,但你不用关心它们中间经历了哪些。

第八步、连接目标的服务器,TCP是传输协议,quan.suning.com是服务器的IP地址,80是服务器端口。

第九步、开始发送数据,只出现一个 >

第十步、发送获取数据的请求,得到时间数据

第十一步、未退出透传模式,发送指令返回乱码数据

退出透传模式,发送指令+++,取消发送新行,返回+++,表明退出成功

验证透传模式是都退出成功,勾选发送新行发送AT,若返回OK,证明退出透传模式成功,若返回乱码数据据,多发送几次+++(取消发送新行)。

第三部分、STM32驱动ESP8266模块获取网络时间

1、实现原理

        熟悉了上面串口助手获取时间的步骤,那么下面就可以利用STM32来按照上述的步骤获取时间,如下图。

        接着我们就要考虑一个问题,上面通过ESP8266获取的时间是静态的,因为你不可能一直让ESP8266连续去获取网络时间,这样我们单片机就干不了别的事情了,那么怎么获取动态时间呢?解决办法:获取一次时间之后,就通过STM32自带的定时器,利用定时器让时间动起来。

        其次就是动态时间的表现方式:方式一、可以通过串口实时打印动态时间数据。方式二、借助显示屏将时间显示出来。我这里就利用方式一实现,其它花里胡哨的交给你自己。

2、相关代码

(1)main.c文件代码

#include "delay.h"
#include "usart.h"
#include "usart2.h"
#include "esp8266.h"
#include "timer.h"

//时间数据
extern int DAYS,MOONS,YEARS,TIMES;
/*注意;这时间与准确时间大约存在6秒内的误差,因为STM32做计算肯定也是要时间的。   */
/*其次:关于时间显示在其他器件上,例如:LCD,OLED等,就需要你自己动手移植显示   */
/*		屏的驱动了。*/

extern int hour_return;//小时
extern int min_return; //分钟
extern int sec_return; //秒数


int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断控制器分组设置
	Usart1_Init(115200);
	Usart2_Init(115200);
	LED_Init();	
	delay_init();//初始化很重要//用不了的函数一般都是没有初始化	
	TIM3_Int_Init(10000,7199);         //设置延时1s
	Get_current_time();     //获取时间
	cJSON_Time_Parse();

	while(1)
	{
		LED0 = 0;
		delay_ms(500);
		LED0 = 1;
		delay_ms(500);
		/*将动态的时间通过串口打印出来*/
		printf("\r\n时间数据: \r\n");
		printf("%d年%d月%d日   ",YEARS,MOONS,DAYS);	
		printf("%02d:%02d:%02d \r\n",hour_return,min_return,sec_return);	

	}
}

(2)esp8266.c文件代码,主要是负责获取时间数据包,处理时间数据包。

#include "stm32f10x.h"
#include "sys.h"
#include "string.h"
#include "stdlib.h"
#include "esp8266.h"

#include "usart.h"
#include "usart2.h"

#include "delay.h"
#include "led.h"

/*用于保存小时,分钟,秒数的变量*/
int hour_return;//小时
int min_return; //分钟
int sec_return; //秒数

//WIFI和密码·
#define ESP8266_WIFI_INFO		"AT+CWJAP=\"iPhone111\",\"123456789\"\r\n"

//苏宁后台获取时间的API
#define Time_TCP		"AT+CIPSTART=\"TCP\",\"quan.suning.com\",80\r\n"
//苏宁后台获取时间GET报文
#define Time_GET		"GET http://quan.suning.com/getSysTime.do\r\n"


//ESP8266数据存放
unsigned char esp8266_buf[300] = {0};
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
//存放时间数据
unsigned char Time_buff[100];   //位数是随机确定的



/**************************************************************************/
//函数作用:ESP8266_Init初始化函数
//函数名称:ESP8266_Init(void);
//内部参数:
//修改日期:2022年4月18日  下午16:18
/**************************************************************************/
void ESP8266_Init(void)
{
    ESP8266_Clear();//清除缓冲

    /*让WIFI退出透传模式 要发两次*/
    while(ESP8266_SendCmd("+++", ""));//这是一个死循环,目的结束透传模式

    /*让WIFI清除Flah*/
    printf("0.恢复出厂设置成功\r\n");

    while(ESP8266_SendCmd("AT+RESTORE\r\n", "OK"));//恢复出厂设置

    //初始AT应答,看wifi接线是否成功
    printf("1.AT应答成功\r\n");

    while(ESP8266_SendCmd("AT\r\n", "OK"));

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //加一步ESP8266复位操作
    printf("2.RST复位成功\r\n");
    ESP8266_SendCmd("AT+RST\r\n", "");
    delay_ms(500);
    ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");//断开与服务器的连接
    delay_ms(500);
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    printf("3.CWMODE设置工作模式,保存到Flash\r\n");

    while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"));//没有CUR就是保存到Flash,AT+CWMODE_CUR设置模块工作模式为station,不保存到Flash

    printf("4.AT+CIPMUX单连接模式设置成功\r\n");

    while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"));//AT+CIPMUX=0 设置为单连接模式

    printf("5.寻找对应的WIFI名称和密码\r\n");

    while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"));

    printf("6.ESP8266_Init连接WIFI成功\r\n");
}



/**************************************************************************/
//函数作用:获取苏宁后台时间
//函数名称:Get_current_time();
//内部参数:
//修改日期:2022年4月18日  晚上20:32
//作者:    大屁桃
/**************************************************************************/
void Get_current_time(void)
{
    ESP8266_Init();	     //连接Wifi的ESP8266初始化
    ESP8266_Clear();

    while(ESP8266_SendCmd(Time_TCP, "CONNECT"));

    printf("6.访问苏宁服务器成功 OK\r\n");

    while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));//开启透传模式

    printf("7.开启透传模式成功 OK\r\n");

    /*sizeof(Time_GET),必须用sizeof函数,用strlen没有用*/
    ESP8266_SendData((u8 *)Time_GET, sizeof(Time_GET)); //发送AT+CIPSEND  以及 Time_GET

    ESP8266_GetIPD_GET(200, Time_buff); //将串口数据取出来
    ESP8266_Clear();//清除缓存数据

    while(ESP8266_SendCmd("+++", ""));      /*退出透传模式,发送两次*/

    printf("9.退出透传模式成功 OK\r\n");

}


/*******************************************解析时间*************************************/
//代码来源于优快云博客地址:https://blog.youkuaiyun.com/xgy516/article/details/119968124
/****************************************************************************************
年的首地址移动11位;
月份首地址移动15位;
日期首地址移动17位;
小时首地址移动19位;
分钟首地址移动21位;
秒钟首地址移动23位;
*/
#define  YEAR_ADD_DRES 11
#define  MOON_ADD_DRES 15
#define  DAYS_ADD_DRES 17

#define  HOURS_ADD_DRES 19
#define  MINUTES_ADD_DRES 21
#define  SECONDS_ADD_DRES 23

int DAYS, MOONS, YEARS, TIMES;
///**************************************************************************/
////函数作用:解析苏宁时间函数
////函数名称:cJSON_Time_Parse();
////内部参数:
////修改日期:2022年4月18日  下午22:11
///**************************************************************************/
void cJSON_Time_Parse(void)
{
    char *data_pt;
    char *day_string;
    char *moon_string;
    char *year_string;
    char *hour_string;
    char *minute_string;
    char *second_string;


    data_pt = strstr((const char *)Time_buff, (const char *)"sysTime1");  //寻找到时间结果的地址

//    printf("%s\r\n",Time_buff);
    if(data_pt != NULL)
    {
        day_string = data_pt + DAYS_ADD_DRES;          //日期地址
        moon_string = data_pt + MOON_ADD_DRES;         //月份地址
        year_string = data_pt + YEAR_ADD_DRES;        //年份地址
        hour_string = data_pt + HOURS_ADD_DRES;       //小时地址
        minute_string = data_pt + MINUTES_ADD_DRES;   //分钟地址
        second_string = data_pt + SECONDS_ADD_DRES;   //秒中地址

        //将时间信息传递给全局变量
        DAYS = Get_Day(day_string);
        MOONS = Get_Moonth(moon_string);
        YEARS = Get_Year(year_string);
        TIMES = Get_Times(hour_string, minute_string, second_string);
		
		hour_return = TIMES/3600;//小时
		min_return = (TIMES%3600)/60; //分钟
        sec_return = (TIMES%3600)%60; //秒数
		printf("时间获取并处理成功\r\n");

    }
    else
    {
        printf("时间获取失败\r\n");
    }
}

//得到年函数(以年开始的字符串长度过长,因此使用不一样的方法)
//输入值是年位置的地址
//返回值是 整型的10进制四位数
int Get_Year(char *y)
{

    int year_return;
    char *year_temp;
    char year[5] = {0};
    char i;
//年的获取须要提取一次字符串,不然没法读取
    year_temp = y;

    for(i = 0; i < 4; i++)
    {
        year[i] = *year_temp;
        year_temp ++;
    }

    year_return =  atoi(&year[0]);
    return year_return;
}

//得到月份函数
//输入值是月份位置的地址
//返回值是 整型的10进制两位数
int Get_Moonth(char *m)
{
    int moonth_return;
    moonth_return = atoi(m) / 100000000; //取月份
    return moonth_return;
}

//得到日期函数
//输入值是日期位置的地址
//返回值是 整型的10进制两位数
int Get_Day(char *d)
{


    int day_return;
    day_return = atoi(d) / 1000000; //取日期

    return day_return;
}

//得到时间
//输入值是时间的位置的地址
//返回值是 整型的10进制的时间总秒数
int Get_Times(char *h, char *m, char *s)
{
    int time_return;
    int hour_return;
    int min_return;
    int sec_return;

    hour_return = atoi(h) / 10000; //取小时
    min_return = atoi(m) / 100; //取分钟
    sec_return = atoi(s);   //取秒数

    time_return = hour_return * 3600 + min_return * 60 + sec_return; //转换成总秒数

    return time_return;
}

/*****************************************************************解析苏宁时间END********************************************************************************/
/*************************************************************************************************************************************************/

/**************************************************************************/
//函数作用:串口二中断函数
//函数名称:USART2_IRQHandler();
//内部参数:
//修改日期:2022年4月18日  下午4:18
/**************************************************************************/
void USART2_IRQHandler(void)
{
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
    {

        if(esp8266_cnt >= sizeof(esp8266_buf))	esp8266_cnt = 0; //防止串口被刷爆

        esp8266_buf[esp8266_cnt++] = USART2->DR;

//		USART_SendData(USART1,USART2->DR);      //让接收到的数据打印在串口一上

        USART_ClearFlag(USART2, USART_FLAG_RXNE);
    }
}
/**下面的代码来源于:
	************************************************************
	************************************************************
	************************************************************
	*	文件名: 	esp8266.c
	*
	*	作者: 		张继瑞
	*
	*	日期: 		2017-05-08
	*
	*	版本: 		V1.0
	*
	*	说明: 		ESP8266的简单驱动
	*
	*	修改记录:
	************************************************************
	************************************************************
	************************************************************
**/
//==========================================================
//	函数名称:	ESP8266_Clear
//
//	函数功能:	清空缓存
//
//	入口参数:	无
//
//	返回参数:	无
//
//	说明:
//==========================================================
void ESP8266_Clear(void)
{

    memset(esp8266_buf, 0, sizeof(esp8266_buf));
    esp8266_cnt = 0;

}
//==========================================================
//	函数名称:	ESP8266_SendData
//
//	函数功能:	发送数据
//
//	入口参数:	data:数据
//				len:长度
//
//	返回参数:	无
//
//	说明:
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{
    char cmdBuf[32];

    ESP8266_Clear();								//清空接收缓存
    sprintf(cmdBuf, "AT+CIPSEND\r\n");		//发送命令

    if(!ESP8266_SendCmd(cmdBuf, ">"))				//收到‘>’时可以发送数据
    {
        printf("8.开始处于透传发送状态!\r\n");
        /*发送请求数据*/
        Usart_SendString(USART2, data, len);		//发送设备连接请求数据
    }
}

//==========================================================
//	函数名称:	ESP8266_GetIPD
//
//	函数功能:	copy天气数据到buff数组里面
//
//	返回参数:	平台返回的原始数据
//
//	说明:		copy天气数据到buff
//==========================================================

unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut, u8 *buff) //这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
{
    do
    {
        delay_ms(5);
    }
    while(timeOut--);

    strcpy((char*)buff, (char*)esp8266_buf);
    return buff;
}



//==========================================================
//	函数名称:	ESP8266_WaitRecive
//
//	函数功能:	等待接收完成
//
//	入口参数:	无
//
//	返回参数:	REV_OK-接收完成		REV_WAIT-接收超时未完成
//
//	说明:		循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{

    if(esp8266_cnt == 0) 							//如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
        return REV_WAIT;

    if(esp8266_cnt == esp8266_cntPre)				//如果上一次的值和这次相同,则说明接收完毕
    {
        esp8266_cnt = 0;							//清0接收计数

        return REV_OK;								//返回接收完成标志
    }

    esp8266_cntPre = esp8266_cnt;					//置为相同

    return REV_WAIT;								//返回接收未完成标志

}


//==========================================================
//	函数名称:	ESP8266_SendCmd
//
//	函数功能:	发送命令
//
//	入口参数:	cmd:命令
//				res:需要检查的返回指令
//
//	返回参数:	0-成功	1-失败
//
//	说明:
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{

    unsigned char timeOut = 250;

    Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));

    while(timeOut--)
    {
        if(ESP8266_WaitRecive() == REV_OK)							//如果收到数据
        {
            if(strstr((const char *)esp8266_buf, res) != NULL)		//如果检索到关键词
            {
                ESP8266_Clear();									//清空缓存

                return 0;
            }
        }

        delay_ms(10);
    }
    return 1;
}

(3)esp8266.h文件代码

#ifndef __ESP8266_H
#define __ESP8266_H
#include "sys.h"
#include "stdio.h"	

#define REV_OK		0	//接收完成标志
#define REV_WAIT	1	//接收未完成标志

//函数声明
unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut,u8 *buff);

void ESP8266_Clear(void);
_Bool ESP8266_WaitRecive(void);

_Bool ESP8266_SendCmd(char *cmd, char *res);
void ESP8266_SendData(unsigned char *data, unsigned short len);

void ESP8266_Init(void);
void Get_current_time(void);

//解析苏宁返回数据
void cJSON_Time_Parse(void);
int Get_Year(char *y);
int Get_Moonth(char *m);
int Get_Day(char *d);
int Get_Times(char *h, char *m, char *s);
		
#endif

(4)usart2.c文件代码

#include "sys.h"
#include "usart.h"
#include "usart2.h"

#include "stdarg.h"	 	 
#include "stdio.h"	 	 
#include "string.h"	 


/**********************************************************串口二***************************************************************/
void Usart2_Init(unsigned int baud)
{

	GPIO_InitTypeDef gpioInitStruct;
	USART_InitTypeDef usartInitStruct;
	NVIC_InitTypeDef nvicInitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	//PA2	TXD
	gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
	gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpioInitStruct);
	
	//PA3	RXD
	gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
	gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpioInitStruct);
	
	usartInitStruct.USART_BaudRate = baud;
	usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//无硬件流控
	usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;						//接收和发送
	usartInitStruct.USART_Parity = USART_Parity_No;									//无校验
	usartInitStruct.USART_StopBits = USART_StopBits_1;								//1位停止位
	usartInitStruct.USART_WordLength = USART_WordLength_8b;							//8位数据位
	USART_Init(USART2, &usartInitStruct);
	
	USART_Cmd(USART2, ENABLE);														//使能串口
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);									//使能接收中断
	
	nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
	nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
	nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&nvicInitStruct);

}


/********************************************************  发送一个16位数 ************************************************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}
/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{

	unsigned short count = 0;
	
	for(; count < len; count++)
	{
		USART_SendData(USARTx, *str++);									//发送数据
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);		//等待发送完成
	}

}
/*****************  发送字符串 **********************/
void Usart_SendString2( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
{

	unsigned char UsartPrintfBuf[296];
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);							//格式化
	va_end(ap);
	
	while(*pStr != 0)
	{
		USART_SendData(USARTx, *pStr++);
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
	}

}

(5)timer.c文件代码

#include "timer.h"
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u32 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
 
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(  //使能或者失能指定的TIM中断
		TIM3, //TIM2
		TIM_IT_Update ,
		ENABLE  //使能
		);
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
							 
}
 
extern int hour_return;
extern int min_return;
extern int sec_return;
 
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
			sec_return++;
							                                   
			if(sec_return == 60) 
			{
				sec_return = 0;
				min_return++;
				if(min_return == 60)
				{
					min_return = 0;
					hour_return ++;
					if(hour_return == 24) hour_return = 0;
				}
			}
		}
}

 第四部分、总结

1、效果展示

        下图是整个代码跑起来的功能视频。(视频不能居中)

2、完整工程代码

         这是这篇博客的完整的代码,工程源码。

3、写在后面

        最终得到的时间和网络时间大约相差5秒,这是因为STM32是串行执行的,因此得到数据肯定是有延迟的噻,要想很准确手动加上几秒就可以了🐶🐶🐶。

        写博客累,这么认真写博客就更累了,这篇博客花费时间最久。所以希望喜欢的小伙伴点个赞收藏一下,给我继续写下去的动力😛。

        最后,有问题小伙伴欢迎进群,我所有博客中涉及到的参考资料和工程都放在群文件里,可直接下载。

STM32-连接wifi模块(esp8266) 乐鑫智能互联平台——ESP8266 拥有高性能无线 SOC,是一个完整且自成体系的 WiFi 网络解决方案,能够独立运行,也可以作为 slave 搭载于其他 Host 运行。 阅读详情

相关推荐

【训练营】基于安信可WiFi模块的物联网时钟项目实战(立创EDA)

本文提供了一份基于安信可WiFi模块(如ESP8266/ESP-12F)与STM32主控,从零开始构建物联网时钟的完整实战指南。内容涵盖硬件连接、软件流程、核心代码实现(包括WiFi连接、NTP时间获取、OLED显示)及常见问题调试,手把手帮助嵌入式与物联网初学者完成一个看得见摸得着的练手项目。

weixin_42527665的博客 234

STM32---WIFI模块ESP8266

模块连接时基于串口方式连接,其模块与电脑(服务器)之间数据的发送与接收也是基于串口。1、对串口的配置。串口初始化、串口中断接收数据,串口发送函数封装(模块发送数据)2、AT指令的发送,并发送AT指令时发送相关的数据:熟练运用spprintf()函数。

m0_52864526的博客 1万+

STM32多模通信物联网系统设计:Lora、WiFi与GPS集成实践

物联网系统通过集成多种通信技术实现远程数据采集与传输,其核心在于根据应用场景选择合适的通信模块并设计高效的数据流。Lora技术以其远距离、低功耗特性,适用于无网络覆盖的野外环境;WiFi则提供高速稳定的互联网接入,便于数据上报至云平台;GPS模块为系统提供精准定位,增强户外应用的实用性。这些技术的融合在环境监测、资产追踪等场景中具有重要价值。本文以STM32单片机为主控,结合Lora模块、WiFi模块和GPS模块,构建了一个露营环境检测系统,详细阐述了多模通信的硬件集成、软件调度与低功耗设计策略,为嵌入式开

weixin_33806300的博客 304

STM32第十六课:WiFi模块的配置及应用

1.先看原理图,配串口,引脚和中断。2.根据发送逻辑进行中断函数的编写。3.在主函数中调用并按照需求进行实现。

小白橘颂的博客 1万+

STM32开发:实现WIFI模块控制

在现代工业中,无线网络技术的应用越来越广泛。STM32作为一款常用的单片机,其集成了丰富的外设和高性能的处理能力,为实现WIFI模块控制提供了良好的硬件和软件基础。本文将介绍如何使用STM32控制WIFI模块,并提供相应的源代码。将STM32开发板通过USB线连接至电脑,打开串口调试助手,将波特率设置为115200,可以看到WIFI模块已经成功连接至指定的SSID,并获得了IP地址。将串口模块通过USB线连接至电脑,打开串口调试助手,将波特率设置为115200。STM32开发:实现WIFI模块控制。

qq_37934722的博客 3712

STM32与ESP8266 WiFi模块的网络时间同步

STM32微控制器与ESP8266 WiFi模块之间的硬件连接主要依赖于STM32提供的串行通信接口(如USART),以及ESP8266的通用输入输出引脚(GPIO)。ESP8266模块普遍使用UART接口进行数据通信。因此,我们需要将STM32的UART引脚与ESP8266模块的TX和RX引脚相连接,以便进行数据的发送和接收。

weixin_42298254的博客 857

【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266WiFi模块获取天气

本文主要是STM32驱动ESP8266wifi模块获取天气。

大屁桃的博客 8878

stm32+esp8266模块获取网络时间

整个系统需要使用两次串口通信,第一次是PC与开发板通信(RXD-PA9,TXD-PA10),作用是当开发板与wifi模块开始配置是,逐步向PC打印出配置信息,并且将最终解析出的网络时间数据打印在串口助手上。第二次是开发板与wifi模块通信(PA2-TX,PA3-RX),作用是开发板发送AT指令,检查返回信息,并将获取的网络时间信息存储在数组中。开发板与wifi模块需要连线,3.3v-3v,GND-G,PA2-TX,PA3-RX。wifi模块无法识别AT指令,需要刷AT固件。STM32选用核心板。

weixin_44583300的博客 1491

【亲测免费】 STM32驱动ESP8266WiFi模块获取网络时间

STM32驱动ESP8266WiFi模块获取网络时间 【下载地址】STM32驱动ESP8266WiFi模块获取网络时间 本仓库包含了我博客文章《【STM32训练—ESP8266WiFi模块】第一篇、STM32驱动ESP8266WiFi模块获取网络时间》对应的工程文件。通过这个工程,你可以学习如何使用STM32微控制器驱动...

gitblog_09814的博客 587

STM32+ESP8266+DHT11物联网温湿度上报项目全解析

在嵌入式物联网开发中,传感器数据采集与无线传输是两大核心环节。STM32作为主流MCU,负责逻辑控制与数据解析;ESP8266凭借低成本和WiFi能力,成为通信模组的理想选择;而DHT11以其单总线协议和免校准特性,常被用于温湿度感知场景。这三者组合构成了一套经典的入门级物联网终端架构,其背后涵盖GPIO时序模拟、串口AT指令调度、TCP/IP数据上报、断线重连等关键技术点。理解DHT11单总线时序、ESP8266的AT指令交互流程,以及STM32的串口状态机设计,能够帮助开发者快速搭建从硬件采集到云端传输

weixin_28285943的博客 290

RcisTarget实战:从差异基因到转录因子调控网络的完整分析流程(附代码)

本文详细介绍了RcisTarget工具在转录因子调控网络分析中的完整流程,从差异基因到motif富集分析,再到结果可视化与生物学解读。通过实战代码演示,帮助研究者掌握这一强大的R包应用,揭示基因表达背后的调控机制,特别适用于基因组学和生物信息学研究。

weixin_29214199的博客 228

探索智能物联网:STM32驱动ESP8266 WiFi模块获取天气

探索智能物联网:STM32驱动ESP8266 WiFi模块获取天气 【下载地址】STM32驱动ESP8266WiFi模块获取天气 本仓库包含了【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266 WiFi模块获取天气的完整设计工程。该工程详细展示了如何使用STM32微控制器驱动ESP8266 WiFi模块...

gitblog_09812的博客 499

FPGA新手必看:XC7Z020最小系统搭建全攻略(附原理图解析)

本文为FPGA新手提供了XC7Z020最小系统搭建的完整实战指南。文章详细解析了电源架构、时钟复位、配置调试等关键硬件电路设计,并重点剖析了外围电路与端口引脚的连接要点及常见陷阱,帮助读者从零开始构建稳定可靠的开发平台。

weixin_29189377的博客 359

STM32驱动ESP8266 WiFi模块获取天气

STM32驱动ESP8266 WiFi模块获取天气 【下载地址】STM32驱动ESP8266WiFi模块获取天气 本仓库包含了【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266 WiFi模块获取天气的完整设计工程。该工程详细展示了如何使用STM32微控制器驱动ESP8266 WiFi模块,并通过网络获取...

gitblog_09781的博客 446

Python爬取腾讯视频m3u8加密流实战(附完整代码)

本文提供了一份详细的Python实战教程,指导如何通过抓包分析、模拟请求头与载荷,破解腾讯视频的m3u8加密流防盗链机制,并附上完整代码实现视频片段的下载与合并。教程涵盖从侦察到代码实现的完整流程,并包含参数动态化、多线程下载等高级技巧与避坑指南,旨在帮助开发者掌握流媒体下载的核心技术。

weixin_29090089的博客 1593

产品失效你必须要知道的概念---MTBF、MTTF和FIT

如果我们想知道该电路在10年后仍能正常工作的几率,按照前面例子的相同计算步骤,结果将是90.3%。例如,假设一个TDK电容器的MTBF为6918年(16.5失效时间间隔,FIT),这并不意味着该电容器能持续这么久(显然,没有人能够对部件进行这么长时间的测试)。综上,MTBF和MTTF是用来描述系统或设备级别的故障间隔时间,而FIT是用来描述某单一器件的失效率的单位。MTBF和MTTF使用相同的计算方法,但MTBF用于可维修的产品,而MTTF用于不可维修或维修成本过高的产品。

weixin_49761275的博客 3833

STM32F103RCT6单片机通过ESP8266抓取网络上时间显示到OLED和物联网dht11温湿度传感器信号采集显示到串口和OLED

STM32F103RCT6单片机通过ESP8266网络抓取时间显示到OLED和物联网dht11温湿度传感器信号采集显示到串口和OLED

m0_56455840的博客 1632

声发射传感器的工作原理是什么?

介绍声发射传感器的工作原理。

qq_39948394的博客 9246

毕设 stm32与深度学习口罩佩戴检测系统(源码+硬件+论文)

本文介绍了一个基于STM32与深度学习的口罩佩戴检测系统毕业设计项目。系统采用STM32作为下位机主控,通过WiFi模块接收上位机传输的检测结果,实现OLED显示和蜂鸣器报警功能。深度学习部分使用YOLOv5模型,在8535张标注数据集上进行训练,准确率达到97%。项目创新性地结合嵌入式硬件与AI技术,构建了一套完整的口罩检测门禁系统,具有较高的实用价值和毕业设计参考意义。系统硬件包括STM32开发板、WiFi模块、显示屏等,软件设计涵盖TCP通信、数据处理及报警功能。该项目综合评分为:难度3分、工作量3分

m0_984093的博客 688

095、PCIE物理层测试模式:从信号眼图到误码率实战

本文介绍了PCIe物理层测试模式的调试方法和实战经验。重点解析了环回测试、合规性测试和静默模式的应用场景及配置要点,分享了信号眼图分析、误码率测试和接收端验证的实用技巧。作者结合自身调试经历,强调物理层测试对排查信号完整性问题的重要性,建议在量产前进行基础测试,并提醒注意芯片勘误和测试模式切换等易错点。通过合理使用测试模式,可显著提高PCIe链路调试效率,确保物理层稳定可靠。

z534437748的博客 60

Win11 1 月更新黑屏循环故障技术分析与解决方案

2026年1月30日Windows 11更新引发黑屏循环故障,主要影响2018年前出厂、采用MBR分区及特定BIOS固件的设备,尤其对使用经典版Outlook(POP协议)的用户影响显著。故障表现为系统无限重启黑屏,导致数据访问中断。分析表明,问题源于硬件兼容性、分区格式与系统更新的交互冲突。解决方案包括进入安全模式回滚更新、暂停自动更新、升级驱动/BIOS,以及等待微软官方补丁。建议用户更新前评估硬件兼容性并备份数据,企业环境应建立分阶段更新测试机制。该事件为系统更新风险管理提供了重要参考。

博客 1441

毕设项目 stm32与深度学习口罩佩戴检测系统(源码+硬件+论文)

基于STM32与深度学习的口罩佩戴检测系统,通过PC端摄像头实时检测,利用WiFi模块将结果传输至STM32处理。系统包含主控、通信、显示、报警模块,检测到未戴口罩时触发蜂鸣器报警。硬件采用STM32F103RCT6开发板、WiFi模块、显示屏等组件。软件设计包括上位机TCP通信和下位机初始化程序。深度学习模型使用8535张标注图片训练,准确率达97%。项目创新性地结合嵌入式与AI技术,为疫情防控提供智能化解决方案。

2201_75853290的博客 624

HTML5+CSS3+JS小实例:焦点图波浪切换动画特效

实例:焦点图波浪切换动画特效。字体图标库:Font Awesome。技术栈:HTML+CSS+JS。

艾恩小灰灰的博客 907

基于STM32单片机智能厨房安全检查 WIFI 火焰 声光报警

该系统基于STM32单片机,集成WIFI通信、火焰检测和声光报警功能,旨在提升厨房安全防护能力。通过实时监测环境参数,及时发现火灾隐患并触发报警机制,同时支持远程通知用户。

mind526的博客 617

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

单片机毕设 stm32与深度学习口罩佩戴检测系统(源码+硬件+论文)

本文介绍了一个基于STM32与深度学习的口罩佩戴检测系统,该系统通过上位机摄像头实时检测人脸口罩佩戴情况,并将结果通过WiFi传输至STM32下位机进行显示和报警。系统采用YOLOv3目标检测算法,在8535张数据集的训练下达到97%的准确率。硬件部分包含STM32主控、蜂鸣器、WiFi模块和液晶显示屏,软件实现TCP通信、状态显示和报警功能。项目创新性地结合嵌入式系统与深度学习,提供了一套完整的口罩检测解决方案,适用于毕业设计等应用场景,具有较高的实用价值和技术创新性。

DD学长的博客 983

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

上一篇: 【Android Studio学习】第二篇、APP实现画简易的波形图
下一篇: 【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266WiFi模块获取天气
大屁桃
博客等级 码龄8年 2万+粉丝 54原创
评论 30
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大屁桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值