目录
第一部分、写在前面
1、硬件准备
2、相关的AT指令
3、参考博客?
第二部分、电脑串口助手调试ESP8266模块获取网络时间
1、ESP8266获取时间的流程
2、具体实现步骤
第三部分、STM32驱动ESP8266模块获取网络时间
1、实现原理
2、相关代码
?第四部分、总结
1、效果展示
2、完整工程代码
3、写在后面
第一部分、写在前面
? ? ? ? 利用STM32获取网络时间,然后再通过LCD将时间显示出来,这是之前很久做的工程,这段时间刚好比较悠闲,然后之前也做过一些笔记,现在刚好将其完善一下。我感觉这篇博客主要讲实现的方法,注意:代码不是重点,重点是我是如何一步一步实现的。而且后续的博客还会讲到如何利用同样的方式去获取天气,去连接阿里云等。
? ? ? ? 所以,学会一种方法很重要。
1、硬件准备
????????STM32选用核心板F103C8T6,然后再加一个ESP8266 WiFi模块(任何型号应该都可以),最后需要一个USB-TTL模块用来打印串口数据。
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的博客-CSDN博客_esp8266获取网络时间
????????其次是关于苏宁后台时间数据的解析方式,这里我参考的是这一篇,文章解释的非常详细,而且我时间的解析代码也是来源于这篇文章,推荐大家去看一下。利用苏宁API接口获取北京时间授时_xgy516的博客-CSDN博客_获取北京时间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");
}
/*******************************************解析时间*************************************/
//代码来源于CSDN博客地址:https://blog.csdn.net/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是串行执行的,因此得到数据肯定是有延迟的噻,要想很准确手动加上几秒就可以了🐶🐶🐶。
????????写博客累,这么认真写博客就更累了,这篇博客花费时间最久。所以希望喜欢的小伙伴点个赞收藏一下,给我继续写下去的动力😛。
? ? ? ? 最后,有问题小伙伴欢迎进群,我所有博客中涉及到的参考资料和工程都放在群文件里,可直接下载。Q群号:1020775171。
|