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


    目录

    第一部分、写在前面

    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指令

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

    3、参考博客 

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

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

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

    1、ESP8266获取时间的流程

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

    2、具体实现步骤

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

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

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

    1. 0:AT
    2. 1:AT+RST
    3. 2:AT+CWMODE=1
    4. 3:AT+CIPMUX=0
    5. 4:AT+CWJAP="你的WiFi名称","你的WiFi密码"
    6. 5:AT+CIPMODE=1
    7. 6:AT+CIPSTART="TCP","quan.suning.com",80
    8. 7:AT+CIPSEND
    9. 8:GET http://quan.suning.com/getSysTime.do
    10. 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文件代码

    1. #include "delay.h"
    2. #include "usart.h"
    3. #include "usart2.h"
    4. #include "esp8266.h"
    5. #include "timer.h"
    6. //时间数据
    7. extern int DAYS,MOONS,YEARS,TIMES;
    8. /*注意;这时间与准确时间大约存在6秒内的误差,因为STM32做计算肯定也是要时间的。 */
    9. /*其次:关于时间显示在其他器件上,例如:LCD,OLED等,就需要你自己动手移植显示 */
    10. /* 屏的驱动了。*/
    11. extern int hour_return;//小时
    12. extern int min_return; //分钟
    13. extern int sec_return; //秒数
    14. int main()
    15. {
    16. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断控制器分组设置
    17. Usart1_Init(115200);
    18. Usart2_Init(115200);
    19. LED_Init();
    20. delay_init();//初始化很重要//用不了的函数一般都是没有初始化
    21. TIM3_Int_Init(10000,7199); //设置延时1s
    22. Get_current_time(); //获取时间
    23. cJSON_Time_Parse();
    24. while(1)
    25. {
    26. LED0 = 0;
    27. delay_ms(500);
    28. LED0 = 1;
    29. delay_ms(500);
    30. /*将动态的时间通过串口打印出来*/
    31. printf("\r\n时间数据: \r\n");
    32. printf("%d年%d月%d日 ",YEARS,MOONS,DAYS);
    33. printf("%02d:%02d:%02d \r\n",hour_return,min_return,sec_return);
    34. }
    35. }

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

    1. #include "stm32f10x.h"
    2. #include "sys.h"
    3. #include "string.h"
    4. #include "stdlib.h"
    5. #include "esp8266.h"
    6. #include "usart.h"
    7. #include "usart2.h"
    8. #include "delay.h"
    9. #include "led.h"
    10. /*用于保存小时,分钟,秒数的变量*/
    11. int hour_return;//小时
    12. int min_return; //分钟
    13. int sec_return; //秒数
    14. //WIFI和密码·
    15. #define ESP8266_WIFI_INFO "AT+CWJAP=\"iPhone111\",\"123456789\"\r\n"
    16. //苏宁后台获取时间的API
    17. #define Time_TCP "AT+CIPSTART=\"TCP\",\"quan.suning.com\",80\r\n"
    18. //苏宁后台获取时间GET报文
    19. #define Time_GET "GET http://quan.suning.com/getSysTime.do\r\n"
    20. //ESP8266数据存放
    21. unsigned char esp8266_buf[300] = {0};
    22. unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
    23. //存放时间数据
    24. unsigned char Time_buff[100]; //位数是随机确定的
    25. /**************************************************************************/
    26. //函数作用:ESP8266_Init初始化函数
    27. //函数名称:ESP8266_Init(void);
    28. //内部参数:
    29. //修改日期:2022年4月18日 下午16:18
    30. /**************************************************************************/
    31. void ESP8266_Init(void)
    32. {
    33. ESP8266_Clear();//清除缓冲
    34. /*让WIFI退出透传模式 要发两次*/
    35. while(ESP8266_SendCmd("+++", ""));//这是一个死循环,目的结束透传模式
    36. /*让WIFI清除Flah*/
    37. printf("0.恢复出厂设置成功\r\n");
    38. while(ESP8266_SendCmd("AT+RESTORE\r\n", "OK"));//恢复出厂设置
    39. //初始AT应答,看wifi接线是否成功
    40. printf("1.AT应答成功\r\n");
    41. while(ESP8266_SendCmd("AT\r\n", "OK"));
    42. //
    43. //加一步ESP8266复位操作
    44. printf("2.RST复位成功\r\n");
    45. ESP8266_SendCmd("AT+RST\r\n", "");
    46. delay_ms(500);
    47. ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");//断开与服务器的连接
    48. delay_ms(500);
    49. /
    50. printf("3.CWMODE设置工作模式,保存到Flash\r\n");
    51. while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"));//没有CUR就是保存到Flash,AT+CWMODE_CUR设置模块工作模式为station,不保存到Flash
    52. printf("4.AT+CIPMUX单连接模式设置成功\r\n");
    53. while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"));//AT+CIPMUX=0 设置为单连接模式
    54. printf("5.寻找对应的WIFI名称和密码\r\n");
    55. while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"));
    56. printf("6.ESP8266_Init连接WIFI成功\r\n");
    57. }
    58. /**************************************************************************/
    59. //函数作用:获取苏宁后台时间
    60. //函数名称:Get_current_time();
    61. //内部参数:
    62. //修改日期:2022年4月18日 晚上20:32
    63. //作者: 大屁桃
    64. /**************************************************************************/
    65. void Get_current_time(void)
    66. {
    67. ESP8266_Init(); //连接Wifi的ESP8266初始化
    68. ESP8266_Clear();
    69. while(ESP8266_SendCmd(Time_TCP, "CONNECT"));
    70. printf("6.访问苏宁服务器成功 OK\r\n");
    71. while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));//开启透传模式
    72. printf("7.开启透传模式成功 OK\r\n");
    73. /*sizeof(Time_GET),必须用sizeof函数,用strlen没有用*/
    74. ESP8266_SendData((u8 *)Time_GET, sizeof(Time_GET)); //发送AT+CIPSEND 以及 Time_GET
    75. ESP8266_GetIPD_GET(200, Time_buff); //将串口数据取出来
    76. ESP8266_Clear();//清除缓存数据
    77. while(ESP8266_SendCmd("+++", "")); /*退出透传模式,发送两次*/
    78. printf("9.退出透传模式成功 OK\r\n");
    79. }
    80. /*******************************************解析时间*************************************/
    81. //代码来源于CSDN博客地址:https://blog.csdn.net/xgy516/article/details/119968124
    82. /****************************************************************************************
    83. 年的首地址移动11位;
    84. 月份首地址移动15位;
    85. 日期首地址移动17位;
    86. 小时首地址移动19位;
    87. 分钟首地址移动21位;
    88. 秒钟首地址移动23位;
    89. */
    90. #define YEAR_ADD_DRES 11
    91. #define MOON_ADD_DRES 15
    92. #define DAYS_ADD_DRES 17
    93. #define HOURS_ADD_DRES 19
    94. #define MINUTES_ADD_DRES 21
    95. #define SECONDS_ADD_DRES 23
    96. int DAYS, MOONS, YEARS, TIMES;
    97. ///**************************************************************************/
    98. 函数作用:解析苏宁时间函数
    99. 函数名称:cJSON_Time_Parse();
    100. 内部参数:
    101. 修改日期:2022418日 下午2211
    102. ///**************************************************************************/
    103. void cJSON_Time_Parse(void)
    104. {
    105. char *data_pt;
    106. char *day_string;
    107. char *moon_string;
    108. char *year_string;
    109. char *hour_string;
    110. char *minute_string;
    111. char *second_string;
    112. data_pt = strstr((const char *)Time_buff, (const char *)"sysTime1"); //寻找到时间结果的地址
    113. // printf("%s\r\n",Time_buff);
    114. if(data_pt != NULL)
    115. {
    116. day_string = data_pt + DAYS_ADD_DRES; //日期地址
    117. moon_string = data_pt + MOON_ADD_DRES; //月份地址
    118. year_string = data_pt + YEAR_ADD_DRES; //年份地址
    119. hour_string = data_pt + HOURS_ADD_DRES; //小时地址
    120. minute_string = data_pt + MINUTES_ADD_DRES; //分钟地址
    121. second_string = data_pt + SECONDS_ADD_DRES; //秒中地址
    122. //将时间信息传递给全局变量
    123. DAYS = Get_Day(day_string);
    124. MOONS = Get_Moonth(moon_string);
    125. YEARS = Get_Year(year_string);
    126. TIMES = Get_Times(hour_string, minute_string, second_string);
    127. hour_return = TIMES/3600;//小时
    128. min_return = (TIMES%3600)/60; //分钟
    129. sec_return = (TIMES%3600)%60; //秒数
    130. printf("时间获取并处理成功\r\n");
    131. }
    132. else
    133. {
    134. printf("时间获取失败\r\n");
    135. }
    136. }
    137. //得到年函数(以年开始的字符串长度过长,因此使用不一样的方法)
    138. //输入值是年位置的地址
    139. //返回值是 整型的10进制四位数
    140. int Get_Year(char *y)
    141. {
    142. int year_return;
    143. char *year_temp;
    144. char year[5] = {0};
    145. char i;
    146. //年的获取须要提取一次字符串,不然没法读取
    147. year_temp = y;
    148. for(i = 0; i < 4; i++)
    149. {
    150. year[i] = *year_temp;
    151. year_temp ++;
    152. }
    153. year_return = atoi(&year[0]);
    154. return year_return;
    155. }
    156. //得到月份函数
    157. //输入值是月份位置的地址
    158. //返回值是 整型的10进制两位数
    159. int Get_Moonth(char *m)
    160. {
    161. int moonth_return;
    162. moonth_return = atoi(m) / 100000000; //取月份
    163. return moonth_return;
    164. }
    165. //得到日期函数
    166. //输入值是日期位置的地址
    167. //返回值是 整型的10进制两位数
    168. int Get_Day(char *d)
    169. {
    170. int day_return;
    171. day_return = atoi(d) / 1000000; //取日期
    172. return day_return;
    173. }
    174. //得到时间
    175. //输入值是时间的位置的地址
    176. //返回值是 整型的10进制的时间总秒数
    177. int Get_Times(char *h, char *m, char *s)
    178. {
    179. int time_return;
    180. int hour_return;
    181. int min_return;
    182. int sec_return;
    183. hour_return = atoi(h) / 10000; //取小时
    184. min_return = atoi(m) / 100; //取分钟
    185. sec_return = atoi(s); //取秒数
    186. time_return = hour_return * 3600 + min_return * 60 + sec_return; //转换成总秒数
    187. return time_return;
    188. }
    189. /*****************************************************************解析苏宁时间END********************************************************************************/
    190. /*************************************************************************************************************************************************/
    191. /**************************************************************************/
    192. //函数作用:串口二中断函数
    193. //函数名称:USART2_IRQHandler();
    194. //内部参数:
    195. //修改日期:2022年4月18日 下午4:18
    196. /**************************************************************************/
    197. void USART2_IRQHandler(void)
    198. {
    199. if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
    200. {
    201. if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
    202. esp8266_buf[esp8266_cnt++] = USART2->DR;
    203. // USART_SendData(USART1,USART2->DR); //让接收到的数据打印在串口一上
    204. USART_ClearFlag(USART2, USART_FLAG_RXNE);
    205. }
    206. }
    207. /**下面的代码来源于:
    208. ************************************************************
    209. ************************************************************
    210. ************************************************************
    211. * 文件名: esp8266.c
    212. *
    213. * 作者: 张继瑞
    214. *
    215. * 日期: 2017-05-08
    216. *
    217. * 版本: V1.0
    218. *
    219. * 说明: ESP8266的简单驱动
    220. *
    221. * 修改记录:
    222. ************************************************************
    223. ************************************************************
    224. ************************************************************
    225. **/
    226. //==========================================================
    227. // 函数名称: ESP8266_Clear
    228. //
    229. // 函数功能: 清空缓存
    230. //
    231. // 入口参数: 无
    232. //
    233. // 返回参数: 无
    234. //
    235. // 说明:
    236. //==========================================================
    237. void ESP8266_Clear(void)
    238. {
    239. memset(esp8266_buf, 0, sizeof(esp8266_buf));
    240. esp8266_cnt = 0;
    241. }
    242. //==========================================================
    243. // 函数名称: ESP8266_SendData
    244. //
    245. // 函数功能: 发送数据
    246. //
    247. // 入口参数: data:数据
    248. // len:长度
    249. //
    250. // 返回参数: 无
    251. //
    252. // 说明:
    253. //==========================================================
    254. void ESP8266_SendData(unsigned char *data, unsigned short len)
    255. {
    256. char cmdBuf[32];
    257. ESP8266_Clear(); //清空接收缓存
    258. sprintf(cmdBuf, "AT+CIPSEND\r\n"); //发送命令
    259. if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
    260. {
    261. printf("8.开始处于透传发送状态!\r\n");
    262. /*发送请求数据*/
    263. Usart_SendString(USART2, data, len); //发送设备连接请求数据
    264. }
    265. }
    266. //==========================================================
    267. // 函数名称: ESP8266_GetIPD
    268. //
    269. // 函数功能: copy天气数据到buff数组里面
    270. //
    271. // 返回参数: 平台返回的原始数据
    272. //
    273. // 说明: copy天气数据到buff
    274. //==========================================================
    275. unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut, u8 *buff) //这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
    276. {
    277. do
    278. {
    279. delay_ms(5);
    280. }
    281. while(timeOut--);
    282. strcpy((char*)buff, (char*)esp8266_buf);
    283. return buff;
    284. }
    285. //==========================================================
    286. // 函数名称: ESP8266_WaitRecive
    287. //
    288. // 函数功能: 等待接收完成
    289. //
    290. // 入口参数: 无
    291. //
    292. // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
    293. //
    294. // 说明: 循环调用检测是否接收完成
    295. //==========================================================
    296. _Bool ESP8266_WaitRecive(void)
    297. {
    298. if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
    299. return REV_WAIT;
    300. if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
    301. {
    302. esp8266_cnt = 0; //清0接收计数
    303. return REV_OK; //返回接收完成标志
    304. }
    305. esp8266_cntPre = esp8266_cnt; //置为相同
    306. return REV_WAIT; //返回接收未完成标志
    307. }
    308. //==========================================================
    309. // 函数名称: ESP8266_SendCmd
    310. //
    311. // 函数功能: 发送命令
    312. //
    313. // 入口参数: cmd:命令
    314. // res:需要检查的返回指令
    315. //
    316. // 返回参数: 0-成功 1-失败
    317. //
    318. // 说明:
    319. //==========================================================
    320. _Bool ESP8266_SendCmd(char *cmd, char *res)
    321. {
    322. unsigned char timeOut = 250;
    323. Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
    324. while(timeOut--)
    325. {
    326. if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
    327. {
    328. if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
    329. {
    330. ESP8266_Clear(); //清空缓存
    331. return 0;
    332. }
    333. }
    334. delay_ms(10);
    335. }
    336. return 1;
    337. }

    (3)esp8266.h文件代码

    1. #ifndef __ESP8266_H
    2. #define __ESP8266_H
    3. #include "sys.h"
    4. #include "stdio.h"
    5. #define REV_OK 0 //接收完成标志
    6. #define REV_WAIT 1 //接收未完成标志
    7. //函数声明
    8. unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut,u8 *buff);
    9. void ESP8266_Clear(void);
    10. _Bool ESP8266_WaitRecive(void);
    11. _Bool ESP8266_SendCmd(char *cmd, char *res);
    12. void ESP8266_SendData(unsigned char *data, unsigned short len);
    13. void ESP8266_Init(void);
    14. void Get_current_time(void);
    15. //解析苏宁返回数据
    16. void cJSON_Time_Parse(void);
    17. int Get_Year(char *y);
    18. int Get_Moonth(char *m);
    19. int Get_Day(char *d);
    20. int Get_Times(char *h, char *m, char *s);
    21. #endif

    (4)usart2.c文件代码

    1. #include "sys.h"
    2. #include "usart.h"
    3. #include "usart2.h"
    4. #include "stdarg.h"
    5. #include "stdio.h"
    6. #include "string.h"
    7. /**********************************************************串口二***************************************************************/
    8. void Usart2_Init(unsigned int baud)
    9. {
    10. GPIO_InitTypeDef gpioInitStruct;
    11. USART_InitTypeDef usartInitStruct;
    12. NVIC_InitTypeDef nvicInitStruct;
    13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    14. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    15. //PA2 TXD
    16. gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    17. gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
    18. gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    19. GPIO_Init(GPIOA, &gpioInitStruct);
    20. //PA3 RXD
    21. gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    22. gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
    23. gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    24. GPIO_Init(GPIOA, &gpioInitStruct);
    25. usartInitStruct.USART_BaudRate = baud;
    26. usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
    27. usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送
    28. usartInitStruct.USART_Parity = USART_Parity_No; //无校验
    29. usartInitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
    30. usartInitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
    31. USART_Init(USART2, &usartInitStruct);
    32. USART_Cmd(USART2, ENABLE); //使能串口
    33. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
    34. nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
    35. nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    36. nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    37. nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
    38. NVIC_Init(&nvicInitStruct);
    39. }
    40. /******************************************************** 发送一个16位数 ************************************************/
    41. void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
    42. {
    43. uint8_t temp_h, temp_l;
    44. /* 取出高八位 */
    45. temp_h = (ch&0XFF00)>>8;
    46. /* 取出低八位 */
    47. temp_l = ch&0XFF;
    48. /* 发送高八位 */
    49. USART_SendData(pUSARTx,temp_h);
    50. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    51. /* 发送低八位 */
    52. USART_SendData(pUSARTx,temp_l);
    53. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    54. }
    55. /***************** 发送一个字符 **********************/
    56. void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
    57. {
    58. /* 发送一个字节数据到USART */
    59. USART_SendData(pUSARTx,ch);
    60. /* 等待发送数据寄存器为空 */
    61. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    62. }
    63. void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
    64. {
    65. unsigned short count = 0;
    66. for(; count < len; count++)
    67. {
    68. USART_SendData(USARTx, *str++); //发送数据
    69. while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
    70. }
    71. }
    72. /***************** 发送字符串 **********************/
    73. void Usart_SendString2( USART_TypeDef * pUSARTx, char *str)
    74. {
    75. unsigned int k=0;
    76. do
    77. {
    78. Usart_SendByte( pUSARTx, *(str + k) );
    79. k++;
    80. } while(*(str + k)!='\0');
    81. /* 等待发送完成 */
    82. while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
    83. {}
    84. }
    85. void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
    86. {
    87. unsigned char UsartPrintfBuf[296];
    88. va_list ap;
    89. unsigned char *pStr = UsartPrintfBuf;
    90. va_start(ap, fmt);
    91. vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
    92. va_end(ap);
    93. while(*pStr != 0)
    94. {
    95. USART_SendData(USARTx, *pStr++);
    96. while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
    97. }
    98. }

    (5)timer.c文件代码

    1. #include "timer.h"
    2. //通用定时器中断初始化
    3. //这里时钟选择为APB1的2倍,而APB1为36M
    4. //arr:自动重装值。
    5. //psc:时钟预分频数
    6. //这里使用的是定时器3!
    7. void TIM3_Int_Init(u32 arr,u16 psc)
    8. {
    9. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    10. NVIC_InitTypeDef NVIC_InitStructure;
    11. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    12. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
    13. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
    14. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    15. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    16. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    17. TIM_ITConfig( //使能或者失能指定的TIM中断
    18. TIM3, //TIM2
    19. TIM_IT_Update ,
    20. ENABLE //使能
    21. );
    22. NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
    23. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
    24. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
    25. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    26. NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    27. TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
    28. }
    29. extern int hour_return;
    30. extern int min_return;
    31. extern int sec_return;
    32. void TIM3_IRQHandler(void) //TIM3中断
    33. {
    34. if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
    35. {
    36. TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
    37. sec_return++;
    38. if(sec_return == 60)
    39. {
    40. sec_return = 0;
    41. min_return++;
    42. if(min_return == 60)
    43. {
    44. min_return = 0;
    45. hour_return ++;
    46. if(hour_return == 24) hour_return = 0;
    47. }
    48. }
    49. }
    50. }

     第四部分、总结

    1、效果展示

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

    2、完整工程代码

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

    3、写在后面

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

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

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

  • 相关阅读:
    echarts漏斗图,形状是通过value来保持金字塔形状的,添加新的字段来用真正的数据
    电脑误删的文件怎么恢复?
    集合框架----源码解读ArrayList篇
    OpenWrt 固件编译教程
    正则表达式运用
    Language Server for Java 1.0 在VS Code上正式发布
    TLS/SSL(一)科普之加密、签名和SSL握手
    【Python爬虫技巧】快速格式化请求头Request Headers
    PTA天梯赛训练 7-8 查验身份证 (15分)
    Linux基础命令行操作
  • 原文地址:https://blog.csdn.net/Learning1232/article/details/127678792